Filed unassigned, found while implementing #10123. Nothing is red today. This is the
same defect class as #10123 — a file the gate could not read scores as a file with
nothing to report — one parser over.
Measured
ts.createSourceFile() is error-tolerant by design: it never throws on a syntax error,
it returns a SourceFile built by error recovery and records what went wrong in
parseDiagnostics. Every gate in this repo that walks TypeScript uses it, and a sweep
of scripts/ finds no gate reading parseDiagnostics (grep -rn "parseDiagnostics" scripts/ returns nothing).
The recovery is partial, and how much it loses depends on the break. Counting
x as any sites in a 3-site file, with the repo's own typescript:
clean : asAny=3 parseDiagnostics=0 statements=3
break at top : asAny=2 parseDiagnostics=1 statements=3
break inside body : asAny=2 parseDiagnostics=3 statements=2
unterminated tpl : asAny=0 parseDiagnostics=1 statements=1
The last row is the interesting one: one unterminated template literal takes the whole
file from 3 sites to 0, and the walker cannot tell that from a clean file. The gate
prints its green line.
The 15 gates on this parser today:
check-driver-memory-census.mjs check-org-identifier.mjs
check-durability-degradation-log-level.mjs check-resume-authority-declared.mjs
check-engine-double-contract.mjs check-route-envelope.mjs
check-filter-alias-parity.mjs check-startup-registry-verdict.mjs
check-init-service-contract.mjs check-tenant-chokepoint.mjs
check-kernel-hook-pairs.mjs check-verify-stand-in-erasure.mjs
check-meta-type-normalized.mjs check-where-matcher-conformance.mjs
check-wildcard-fallthrough.mjs
check-verify-stand-in-erasure.mjs is worth naming: lint.yml calls it "the third and
narrowest member" of the two ESLint ratchets #10123 fixed, so the family whose hole was
just closed has a third member with the same hole through a different parser.
Why it matters, and why it is not urgent
Same argument as #10123: the failure mode is a quiet green, on gates that exist because
something else cannot see what they see. It is latent for the same reason — the tree
parses today, and pnpm lint fails loudly on a file that does not — so the exposure is
the window between a file breaking and lint being run, plus any file lint does not cover.
Direction (not a decision)
The cheap shape mirrors #10123: after createSourceFile, refuse when
sf.parseDiagnostics is non-empty, naming the file and the first diagnostic. Two things
a card here has to decide rather than assume:
Refs: #10123 (the same class, ESLint side, where this was found) · #9367 (the precedent
for a class-wide defect across source-scanning gates).
Filed unassigned, found while implementing #10123. Nothing is red today. This is the
same defect class as #10123 — a file the gate could not read scores as a file with
nothing to report — one parser over.
Measured
ts.createSourceFile()is error-tolerant by design: it never throws on a syntax error,it returns a SourceFile built by error recovery and records what went wrong in
parseDiagnostics. Every gate in this repo that walks TypeScript uses it, and a sweepof
scripts/finds no gate readingparseDiagnostics(grep -rn "parseDiagnostics" scripts/returns nothing).The recovery is partial, and how much it loses depends on the break. Counting
x as anysites in a 3-site file, with the repo's owntypescript:The last row is the interesting one: one unterminated template literal takes the whole
file from 3 sites to 0, and the walker cannot tell that from a clean file. The gate
prints its green line.
The 15 gates on this parser today:
check-verify-stand-in-erasure.mjsis worth naming:lint.ymlcalls it "the third andnarrowest member" of the two ESLint ratchets #10123 fixed, so the family whose hole was
just closed has a third member with the same hole through a different parser.
Why it matters, and why it is not urgent
Same argument as #10123: the failure mode is a quiet green, on gates that exist because
something else cannot see what they see. It is latent for the same reason — the tree
parses today, and
pnpm lintfails loudly on a file that does not — so the exposure isthe window between a file breaking and lint being run, plus any file lint does not cover.
Direction (not a decision)
The cheap shape mirrors #10123: after
createSourceFile, refuse whensf.parseDiagnosticsis non-empty, naming the file and the first diagnostic. Two thingsa card here has to decide rather than assume:
parseDiagnosticsis not part of TypeScript's publicSourceFiletype (it is on theinternal shape). Whether to read it, or to get the same fact another way, is a real
choice with a maintenance cost either way.
The two ESLint ratchet gates swallow a fatal parse error as a non-matching message — an unparseable file scores clean instead of failing #10123 landed as
scripts/eslint-fatal-guard.mjs) versus per-gate checks, and whetherevery population deserves the refusal or only the ones that lint does not cover.
Refs: #10123 (the same class, ESLint side, where this was found) · #9367 (the precedent
for a class-wide defect across source-scanning gates).