Skip to content

unstable/ast: scanJsDocToken infinite-loops when a scan range ends on a trailing '-' (fix from #63581 not carried into the AST scanner)Β #64135

Description

@nightcabin1

πŸ”Ž 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:

  1. This one β€” the unstable/ast scanner in 7.0.2.
  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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions