@@ -1518,6 +1518,139 @@ namespace ts {
1518
1518
return false ;
1519
1519
}
1520
1520
1521
+ const commonKeywords = [
1522
+ "async" ,
1523
+ "class" ,
1524
+ "const" ,
1525
+ "declare" ,
1526
+ "export" ,
1527
+ "function" ,
1528
+ "interface" ,
1529
+ "let" ,
1530
+ "type" ,
1531
+ "var" ,
1532
+ ] ;
1533
+
1534
+ function parseSemicolonAfter ( expression : Expression | PropertyName ) {
1535
+ // Consume the semicolon if it was explicitly provided.
1536
+ if ( canParseSemicolon ( ) ) {
1537
+ if ( token ( ) === SyntaxKind . SemicolonToken ) {
1538
+ nextToken ( ) ;
1539
+ }
1540
+
1541
+ return ;
1542
+ }
1543
+
1544
+ // Specialized diagnostics for a keyword expression are redundant if the related token is already complaining.
1545
+ const lastError = lastOrUndefined ( parseDiagnostics ) ;
1546
+ if ( lastError && scanner . getTokenPos ( ) < lastError . start + 2 ) {
1547
+ parseErrorAtCurrentToken ( Diagnostics . _0_expected , tokenToString ( SyntaxKind . SemicolonToken ) ) ;
1548
+ return ;
1549
+ }
1550
+
1551
+ // Tagged template literals are sometimes used in places where only simple strings are allowed, e.g.:
1552
+ // module `M1` {
1553
+ // ^^^^^^^^^^^ This block is parsed as a template literal as with module`M1`.
1554
+ if ( isTaggedTemplateExpression ( expression ) ) {
1555
+ parseErrorAt ( skipTrivia ( sourceText , expression . template . pos ) , expression . template . end , Diagnostics . Template_literal_not_allowed_as_a_string_at_this_position ) ;
1556
+ return ;
1557
+ }
1558
+
1559
+ // Otherwise, if this isn't a well-known keyword-like identifier, give the generic fallback message.
1560
+ const expressionText = getExpressionText ( expression ) ;
1561
+ if ( ! expressionText || ! isIdentifierText ( expressionText , languageVersion ) ) {
1562
+ parseErrorAtCurrentToken ( Diagnostics . _0_expected , tokenToString ( SyntaxKind . SemicolonToken ) ) ;
1563
+ return ;
1564
+ }
1565
+
1566
+ const pos = skipTrivia ( sourceText , expression . pos ) ;
1567
+
1568
+ // Some known keywords are likely signs of syntax being used improperly.
1569
+ switch ( expressionText ) {
1570
+ case "const" :
1571
+ case "let" :
1572
+ case "var" :
1573
+ parseErrorAt ( pos , expression . end , Diagnostics . Variable_declaration_not_allowed_at_this_location ) ;
1574
+ return ;
1575
+
1576
+ case "interface" :
1577
+ parseErrorForExpectedName ( scanner . getTokenText ( ) , "{" , Diagnostics . Interface_must_be_given_a_name , Diagnostics . Interface_name_cannot_be_0 ) ;
1578
+ return ;
1579
+
1580
+ case "is" :
1581
+ parseErrorAt ( pos , scanner . getTextPos ( ) , Diagnostics . A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods ) ;
1582
+ return ;
1583
+
1584
+ case "module" :
1585
+ case "namespace" :
1586
+ parseErrorForExpectedName ( scanner . getTokenText ( ) , "{" , Diagnostics . Namespace_must_be_given_a_name , Diagnostics . Namespace_name_cannot_be_0 ) ;
1587
+ return ;
1588
+
1589
+ case "type" :
1590
+ parseErrorForExpectedName ( scanner . getTokenText ( ) , "=" , Diagnostics . Type_alias_must_be_given_a_name , Diagnostics . Type_alias_name_cannot_be_0 ) ;
1591
+ return ;
1592
+ }
1593
+
1594
+ // The user alternately might have misspelled or forgotten to add a space after a common keyword.
1595
+ const suggestion = getSpellingSuggestion ( expressionText , commonKeywords , n => n ) || getSpaceSuggestion ( expressionText ) ;
1596
+ if ( suggestion ) {
1597
+ parseErrorAt ( pos , expression . end , Diagnostics . Unknown_keyword_or_identifier_Did_you_mean_0 , suggestion ) ;
1598
+ }
1599
+
1600
+ // We know this is a slightly more precise case than a missing expected semicolon.
1601
+ parseErrorAt ( pos , expression . end , Diagnostics . Unexpected_keyword_or_identifier ) ;
1602
+ }
1603
+
1604
+ function parseErrorForExpectedName ( name : string , normalToken : string , blankDiagnostic : DiagnosticMessage , nameDiagnostic : DiagnosticMessage ) {
1605
+ if ( name === normalToken ) {
1606
+ parseErrorAtCurrentToken ( blankDiagnostic ) ;
1607
+ }
1608
+ else {
1609
+ parseErrorAtCurrentToken ( nameDiagnostic , name ) ;
1610
+ }
1611
+ }
1612
+
1613
+ function getSpaceSuggestion ( expressionText : string ) {
1614
+ for ( const keyword of commonKeywords ) {
1615
+ if ( expressionText . length > keyword . length + 2 && startsWith ( expressionText , keyword ) ) {
1616
+ return `${ keyword } ${ expressionText . slice ( keyword . length ) } ` ;
1617
+ }
1618
+ }
1619
+
1620
+ return undefined ;
1621
+ }
1622
+
1623
+ function parseSemicolonAfterPropertyName ( name : PropertyName , type : TypeNode | undefined , initializer : Expression | undefined ) {
1624
+ switch ( scanner . getTokenText ( ) ) {
1625
+ case "@" :
1626
+ parseErrorAtCurrentToken ( Diagnostics . Decorators_must_precede_all_other_keywords_for_property_declarations ) ;
1627
+ return ;
1628
+
1629
+ case "(" :
1630
+ parseErrorAtCurrentToken ( Diagnostics . Function_call_not_allowed_at_this_location ) ;
1631
+ nextToken ( ) ;
1632
+ return ;
1633
+ }
1634
+
1635
+ if ( type && ! canParseSemicolon ( ) ) {
1636
+ if ( initializer ) {
1637
+ parseErrorAtCurrentToken ( Diagnostics . _0_expected , tokenToString ( SyntaxKind . SemicolonToken ) ) ;
1638
+ }
1639
+ else {
1640
+ parseErrorAtCurrentToken ( Diagnostics . Missing_before_default_property_value ) ;
1641
+ }
1642
+ return ;
1643
+ }
1644
+
1645
+ return parseSemicolonAfter ( name ) ;
1646
+ }
1647
+
1648
+ function getExpressionText ( expression : Expression | PropertyName ) {
1649
+ return expression && ts . isIdentifier ( expression )
1650
+ ? expression . escapedText . toString ( )
1651
+ : undefined ;
1652
+ }
1653
+
1521
1654
function parseExpectedJSDoc ( kind : JSDocSyntaxKind ) {
1522
1655
if ( token ( ) === kind ) {
1523
1656
nextTokenJSDoc ( ) ;
@@ -5786,7 +5919,7 @@ namespace ts {
5786
5919
identifierCount ++ ;
5787
5920
expression = finishNode ( factory . createIdentifier ( "" ) , getNodePos ( ) ) ;
5788
5921
}
5789
- parseSemicolon ( ) ;
5922
+ parseSemicolonAfter ( expression ) ;
5790
5923
return finishNode ( factory . createThrowStatement ( expression ) , pos ) ;
5791
5924
}
5792
5925
@@ -5847,7 +5980,7 @@ namespace ts {
5847
5980
node = factory . createLabeledStatement ( expression , parseStatement ( ) ) ;
5848
5981
}
5849
5982
else {
5850
- parseSemicolon ( ) ;
5983
+ parseSemicolonAfter ( expression ) ;
5851
5984
node = factory . createExpressionStatement ( expression ) ;
5852
5985
if ( hasParen ) {
5853
5986
// do not parse the same jsdoc twice
@@ -6440,7 +6573,7 @@ namespace ts {
6440
6573
const exclamationToken = ! questionToken && ! scanner . hasPrecedingLineBreak ( ) ? parseOptionalToken ( SyntaxKind . ExclamationToken ) : undefined ;
6441
6574
const type = parseTypeAnnotation ( ) ;
6442
6575
const initializer = doOutsideOfContext ( NodeFlags . YieldContext | NodeFlags . AwaitContext | NodeFlags . DisallowInContext , parseInitializer ) ;
6443
- parseSemicolon ( ) ;
6576
+ parseSemicolonAfterPropertyName ( name , type , initializer ) ;
6444
6577
const node = factory . createPropertyDeclaration ( decorators , modifiers , name , questionToken || exclamationToken , type , initializer ) ;
6445
6578
return withJSDoc ( finishNode ( node , pos ) , hasJSDoc ) ;
6446
6579
}
0 commit comments