From 5b77ef8b4d42ee7ed3a80ee8326fa9c622c3c5cb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 26 Jul 2017 10:12:59 -0700 Subject: [PATCH] Fix infinite loop in jsdoc parsing (#17420) * Test case * Move parameter fix to apply to jsdoc (and all lists) * Inline function, generalize comment --- src/compiler/parser.ts | 19 ++++++++++--------- ...docParameterParsingInfiniteLoop.errors.txt | 11 +++++++++++ .../jsdocParameterParsingInfiniteLoop.ts | 9 +++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt create mode 100644 tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts 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