π Search Terms
scanJsDocToken, infinite loop, unstable/ast, scanner, -*/, #63580, #63581
π Version & Regression Information
node_modules/typescript/dist/ast/scanner.js:2147 in 7.0.2:
while (pos < end && isIdentifierPart(char = codePointUnchecked(pos)) || char === CharacterCodes.minus)
&& binds tighter than ||, and char is only reassigned inside the left operand. Once pos === end with the last consumed character a -, the left conjunct short-circuits, char is never refreshed, char === minus stays true forever, and pos increments without bound.
The main fix is the same one line β parenthesise the disjunction:
while (pos < end && (isIdentifierPart(char = codePointUnchecked(pos)) || char === CharacterCodes.minus))
β― Playground Link
Not applicable β reproduces against the typescript/unstable/ast entry point, which the playground does not expose.
π» Code
// npm i typescript@7.0.2
import { createScanner } from "typescript/unstable/ast";
const text = "/** x-yy"; // the terminated form "/** x-*/" hangs too
const scanner = createScanner(99, true);
scanner.setText(text);
// mirrors parseJSDocCommentWorker, which scans [start + 3, length - 5)
// -- so the range ends immediately after the "-"
scanner.scanRange(3, text.length - 5, () => {
while (scanner.scanJsDocToken() !== 1 /* EndOfFileToken */) {}
});
console.log("returned");
π Actual behavior
Never returns; one core spins at 100%. Killed after 12s.
Changing x-yy to xxyy (so the range no longer ends on a hyphen) prints returned immediately, which isolates the trailing - as the trigger.
π Expected behavior
Returns, as it does on main after #63581.
Additional information about the issue
Worth flagging because the same defect is currently reachable two ways, and neither has a published fix:
- This one β the
unstable/ast scanner in 7.0.2.
ts.createSourceFile in every published JS-API release. 5.9.3 and 6.0.3 both still hang; 6.0.3 predates the fix and release-6.0 has had no commits since. So there is no typescript version on npm where createSourceFile handles this input, and 7.x ships no createSourceFile to migrate to.
We hit (2) in CI. A tool of ours parses a fixed-length prefix of each source file to classify it, and one file's slice happened to end a JSDoc range just after a hyphen. The job produced no output for 18m57s until its 20-minute timeout killed it, four runs in a row, with nothing in the log to indicate a parse was stuck. Ordinary code containing identifier-*/ in a JSDoc reproduces it without any slicing β a type-aware ESLint run over such a file hangs the same way.
We worked around it with jsDocParsingMode: JSDocParsingMode.ParseNone, which avoids scanJsDocToken entirely. Filing this because the AST-scanner copy looks like it was simply missed when #63581 landed, and because a 6.0.x patch would close the larger half for anyone still on the JS API.
π Search Terms
scanJsDocToken, infinite loop,unstable/ast, scanner,-*/, #63580, #63581π Version & Regression Information
typescript@7.0.2-*/Β #63580, fixed onmainby Fix infinite loopΒ #63581 β but that fix changed onlysrc/compiler/scanner.ts. The copy of the scanner shipped astypescript/unstable/aststill has the unparenthesised loop, so the bug survives there.node_modules/typescript/dist/ast/scanner.js:2147in 7.0.2:&&binds tighter than||, andcharis only reassigned inside the left operand. Oncepos === endwith the last consumed character a-, the left conjunct short-circuits,charis never refreshed,char === minusstays true forever, andposincrements without bound.The
mainfix is the same one line β parenthesise the disjunction:β― Playground Link
Not applicable β reproduces against the
typescript/unstable/astentry point, which the playground does not expose.π» Code
π Actual behavior
Never returns; one core spins at 100%. Killed after 12s.
Changing
x-yytoxxyy(so the range no longer ends on a hyphen) printsreturnedimmediately, which isolates the trailing-as the trigger.π Expected behavior
Returns, as it does on
mainafter #63581.Additional information about the issue
Worth flagging because the same defect is currently reachable two ways, and neither has a published fix:
unstable/astscanner in 7.0.2.ts.createSourceFilein every published JS-API release. 5.9.3 and 6.0.3 both still hang; 6.0.3 predates the fix andrelease-6.0has had no commits since. So there is notypescriptversion on npm wherecreateSourceFilehandles this input, and 7.x ships nocreateSourceFileto migrate to.We hit (2) in CI. A tool of ours parses a fixed-length prefix of each source file to classify it, and one file's slice happened to end a JSDoc range just after a hyphen. The job produced no output for 18m57s until its 20-minute timeout killed it, four runs in a row, with nothing in the log to indicate a parse was stuck. Ordinary code containing
identifier-*/in a JSDoc reproduces it without any slicing β a type-aware ESLint run over such a file hangs the same way.We worked around it with
jsDocParsingMode: JSDocParsingMode.ParseNone, which avoidsscanJsDocTokenentirely. Filing this because the AST-scanner copy looks like it was simply missed when #63581 landed, and because a6.0.xpatch would close the larger half for anyone still on the JS API.