From b1e687a149eb962c5bbf3e1c87466cfa7f2f3669 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 25 Jul 2017 17:01:22 -0700 Subject: [PATCH 1/3] Test case --- .../cases/compiler/jsdocParameterParsingInfiniteLoop.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts diff --git a/tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts b/tests/cases/compiler/jsdocParameterParsingInfiniteLoop.ts new file mode 100644 index 0000000000000..581a485bf0bff --- /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 From 5f7a5d102de3765b9fe05b3b8fcb70e64a9e8899 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 25 Jul 2017 17:04:17 -0700 Subject: [PATCH 2/3] Move parameter fix to apply to jsdoc (and all lists) --- src/compiler/parser.ts | 23 +++++++++++-------- ...docParameterParsingInfiniteLoop.errors.txt | 11 +++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9c725a5d6e7d9..4985b01b619f7 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,7 @@ namespace ts { if (considerSemicolonAsDelimiter && token() === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { nextToken(); } + checkZeroLengthNode(startPos); continue; } @@ -1913,6 +1917,16 @@ namespace ts { result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; + + function checkZeroLengthNode(startPos: number) { + 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(); + } + } } function createMissingList(): NodeArray { @@ -2221,7 +2235,6 @@ namespace ts { return finishNode(node); } - const startPos = scanner.getStartPos(); node.decorators = parseDecorators(); node.modifiers = parseModifiers(); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); @@ -2245,14 +2258,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 0000000000000..e42d67a62c10c --- /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 From 92d88efb74b53cdf1872ee67d3b2ccd539bc32b5 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 26 Jul 2017 09:45:24 -0700 Subject: [PATCH 3/3] Inline function, generalize comment --- src/compiler/parser.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4985b01b619f7..5d6cd9e3bd1c6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1891,7 +1891,13 @@ namespace ts { if (considerSemicolonAsDelimiter && token() === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { nextToken(); } - checkZeroLengthNode(startPos); + 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; } @@ -1917,16 +1923,6 @@ namespace ts { result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; - - function checkZeroLengthNode(startPos: number) { - 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(); - } - } } function createMissingList(): NodeArray {