Skip to content

Commit 5b77ef8

Browse files
authored
Fix infinite loop in jsdoc parsing (#17420)
* Test case * Move parameter fix to apply to jsdoc (and all lists) * Inline function, generalize comment
1 parent a59db13 commit 5b77ef8

File tree

3 files changed

+30
-9
lines changed

3 files changed

+30
-9
lines changed

src/compiler/parser.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,9 +1865,12 @@ namespace ts {
18651865
let commaStart = -1; // Meaning the previous token was not a comma
18661866
while (true) {
18671867
if (isListElement(kind, /*inErrorRecovery*/ false)) {
1868+
const startPos = scanner.getStartPos();
18681869
result.push(parseListElement(kind, parseElement));
18691870
commaStart = scanner.getTokenPos();
1871+
18701872
if (parseOptional(SyntaxKind.CommaToken)) {
1873+
// No need to check for a zero length node since we know we parsed a comma
18711874
continue;
18721875
}
18731876

@@ -1888,6 +1891,13 @@ namespace ts {
18881891
if (considerSemicolonAsDelimiter && token() === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) {
18891892
nextToken();
18901893
}
1894+
if (startPos === scanner.getStartPos()) {
1895+
// What we're parsing isn't actually remotely recognizable as a element and we've consumed no tokens whatsoever
1896+
// Consume a token to advance the parser in some way and avoid an infinite loop
1897+
// This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions,
1898+
// or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied
1899+
nextToken();
1900+
}
18911901
continue;
18921902
}
18931903

@@ -2221,7 +2231,6 @@ namespace ts {
22212231
return finishNode(node);
22222232
}
22232233

2224-
const startPos = scanner.getStartPos();
22252234
node.decorators = parseDecorators();
22262235
node.modifiers = parseModifiers();
22272236
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
@@ -2245,14 +2254,6 @@ namespace ts {
22452254
node.type = parseParameterType();
22462255
node.initializer = parseBindingElementInitializer(/*inParameter*/ true);
22472256

2248-
if (startPos === scanner.getStartPos()) {
2249-
// What we're parsing isn't actually remotely recognizable as a parameter and we've consumed no tokens whatsoever
2250-
// Consume a token to advance the parser in some way and avoid an infinite loop in `parseDelimitedList`
2251-
// This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions,
2252-
// or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied
2253-
nextToken();
2254-
}
2255-
22562257
return addJSDocComment(finishNode(node));
22572258
}
22582259

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
tests/cases/compiler/example.js(3,20): error TS1003: Identifier expected.
2+
3+
4+
==== tests/cases/compiler/example.js (1 errors) ====
5+
// @ts-check
6+
/**
7+
* @type {function(@foo)}
8+
~
9+
!!! error TS1003: Identifier expected.
10+
*/
11+
let x;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @filename: example.js
2+
// @checkJs: true
3+
// @allowJs: true
4+
// @noEmit: true
5+
// @ts-check
6+
/**
7+
* @type {function(@foo)}
8+
*/
9+
let x;

0 commit comments

Comments
 (0)