@@ -1518,6 +1518,139 @@ namespace ts {
15181518 return false ;
15191519 }
15201520
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+
15211654 function parseExpectedJSDoc ( kind : JSDocSyntaxKind ) {
15221655 if ( token ( ) === kind ) {
15231656 nextTokenJSDoc ( ) ;
@@ -5786,7 +5919,7 @@ namespace ts {
57865919 identifierCount ++ ;
57875920 expression = finishNode ( factory . createIdentifier ( "" ) , getNodePos ( ) ) ;
57885921 }
5789- parseSemicolon ( ) ;
5922+ parseSemicolonAfter ( expression ) ;
57905923 return finishNode ( factory . createThrowStatement ( expression ) , pos ) ;
57915924 }
57925925
@@ -5847,7 +5980,7 @@ namespace ts {
58475980 node = factory . createLabeledStatement ( expression , parseStatement ( ) ) ;
58485981 }
58495982 else {
5850- parseSemicolon ( ) ;
5983+ parseSemicolonAfter ( expression ) ;
58515984 node = factory . createExpressionStatement ( expression ) ;
58525985 if ( hasParen ) {
58535986 // do not parse the same jsdoc twice
@@ -6440,7 +6573,7 @@ namespace ts {
64406573 const exclamationToken = ! questionToken && ! scanner . hasPrecedingLineBreak ( ) ? parseOptionalToken ( SyntaxKind . ExclamationToken ) : undefined ;
64416574 const type = parseTypeAnnotation ( ) ;
64426575 const initializer = doOutsideOfContext ( NodeFlags . YieldContext | NodeFlags . AwaitContext | NodeFlags . DisallowInContext , parseInitializer ) ;
6443- parseSemicolon ( ) ;
6576+ parseSemicolonAfterPropertyName ( name , type , initializer ) ;
64446577 const node = factory . createPropertyDeclaration ( decorators , modifiers , name , questionToken || exclamationToken , type , initializer ) ;
64456578 return withJSDoc ( finishNode ( node , pos ) , hasJSDoc ) ;
64466579 }
0 commit comments