diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9c725a5d6e7..5d6cd9e3bd1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1865,9 +1865,12 @@ namespace ts { let commaStart = -1; // Meaning the previous token was not a comma while (true) { if (isListElement(kind, /*inErrorRecovery*/ false)) { + const startPos = scanner.getStartPos(); result.push(parseListElement(kind, parseElement)); commaStart = scanner.getTokenPos(); + if (parseOptional(SyntaxKind.CommaToken)) { + // No need to check for a zero length node since we know we parsed a comma continue; } @@ -1888,6 +1891,13 @@ namespace ts { if (considerSemicolonAsDelimiter && token() === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { nextToken(); } + if (startPos === scanner.getStartPos()) { + // What we're parsing isn't actually remotely recognizable as a element and we've consumed no tokens whatsoever + // Consume a token to advance the parser in some way and avoid an infinite loop + // This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions, + // or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied + nextToken(); + } continue; } @@ -2221,7 +2231,6 @@ namespace ts { return finishNode(node); } - const startPos = scanner.getStartPos(); node.decorators = parseDecorators(); node.modifiers = parseModifiers(); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); @@ -2245,14 +2254,6 @@ namespace ts { node.type = parseParameterType(); node.initializer = parseBindingElementInitializer(/*inParameter*/ true); - if (startPos === scanner.getStartPos()) { - // What we're parsing isn't actually remotely recognizable as a parameter and we've consumed no tokens whatsoever - // Consume a token to advance the parser in some way and avoid an infinite loop in `parseDelimitedList` - // This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions, - // or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied - nextToken(); - } - return addJSDocComment(finishNode(node)); } diff --git a/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt b/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt new file mode 100644 index 00000000000..e42d67a62c1 --- /dev/null +++ b/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/example.js(3,20): error TS1003: Identifier expected. + + +==== tests/cases/compiler/example.js (1 errors) ==== + // @ts-check + /** + * @type {function(@foo)} + ~ +!!! error TS1003: Identifier expected. + */ + let x; \ No newline at end of file diff --git a/tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts b/tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts new file mode 100644 index 00000000000..581a485bf0b --- /dev/null +++ b/tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts @@ -0,0 +1,9 @@ +// @filename: example.js +// @checkJs: true +// @allowJs: true +// @noEmit: true +// @ts-check +/** + * @type {function(@foo)} + */ +let x; \ No newline at end of file