Skip to content

compat-eslint: dogfood-corpus parity sweep + 6 drift fixes it caught#93

Closed
johnsoncodehk wants to merge 1 commit into
masterfrom
refactor/parity-sweep-dogfood
Closed

compat-eslint: dogfood-corpus parity sweep + 6 drift fixes it caught#93
johnsoncodehk wants to merge 1 commit into
masterfrom
refactor/parity-sweep-dogfood

Conversation

@johnsoncodehk
Copy link
Copy Markdown
Owner

Summary

Extends the bottom-up parity sweep from the 15-fixture set to the dogfood corpus — every production .ts file in the monorepo, ~65k type asserts and ~64.7k parent asserts. The broader walk surfaced six real drift bugs that the architectural gates (SyntheticLazyNode, KnownEstreeType, navigation tables) couldn't catch on their own; all six are fixed here.

Drift surface caught + fixed

# Pattern Fix
1 class X { y: T[] } — type child of PropertyDeclaration / MethodDeclaration / GetAccessor missed TSTypeAnnotation wrapper Add 4 entries to TYPE_SLOT_TRIGGERS
2 function f(x): x is T — TypePredicate nested TSTypeAnnotation wasn't materialised on bottom-up walk Add TypePredicate trigger
3 function f(x = 1) — parameter binding inside AssignmentPattern reported parent.type === 'FunctionDeclaration' Re-parent in AssignmentPatternNode constructor (same as BindingAssignmentPatternNode)
4 export { … } / \${x}`— bottom-up walker builtTSNamedExports/TSTemplateSpan` GenericTSNode wrappers Add to SKIP_AS_PARENT
5 type X = { [K in keyof T]: … } — bottom-up wrapped K in TSTypeParameter from SHAPES dispatch; eager exposes it bare on TSMappedType.key Skip TypeParameter as parent when TS parent is MappedType
6 import('foo') / typeof import('foo') — TSLiteralType wrapper between StringLiteral and TSImportType / TSTypeQuery Skip LiteralType (when in ImportType) + flatten top-down argument getter + drill TSTypeQuery.exprName

Documented divergences that remain

Three known scaffolding gaps surface but the rule corpus doesn't query them — the sweep filter accepts them with structural reasoning (when a wrapper is missing on one side, parent AND grandparent comparisons are off-by-one and skipped together):

  • ChainExpression scaffolding — lazy single outer wrapper vs eager per-link
  • TSInterfaceHeritageinterface X extends Y wraps Y in eager; lazy emits bare Identifier
  • TSAssertClauseimport x from 'y' assert {…} wraps entries in eager; lazy emits ImportAttribute children directly

Adding either wrapper class is mechanical — extends SyntheticLazyNode + nav-table entry + remove the corresponding accept-skip line. The sweep continues to flag any additional gaps as the architecture evolves.

Test plan

  • predicate-coverage (152/152, 16 ignored)
  • lazy-estree.test: 15-fixture parity sweep (169 type / 154 parent asserts) + dogfood sweep (65k type / 64.7k parent asserts) + factory tests + phantom-types invariant — all pass
  • scope-compat (24/24 clean), selector-analysis, ts-ast-scan, compat-pipeline, jsx-react-x
  • dogfood.js — 30 files × 107 rules, 0 divergences, 0 crashes
  • Dify cold 4.9-5.8s on this branch vs 5.1-5.5s on master — no regression

The 15-fixture parity sweep covers known bug classes; once it passes,
the next drift surface is uncovered slot/wrapper combinations in real
production code. Replay the same node-level invariant across the
dogfood corpus — every TS file in the monorepo, ~65k type asserts and
~64.7k parent asserts.

Six bugs surfaced by the broader sweep, fixed in this PR:

1. PropertyDeclaration / MethodDeclaration / GetAccessor missing from
   TYPE_SLOT_TRIGGERS. Bottom-up materialise of a type child of a
   class field / method skipped the synthetic TSTypeAnnotation
   wrapper, landing parent on PropertyDefinition / MethodDefinition
   directly.

2. TypePredicate not in TYPE_SLOT_TRIGGERS. `function f(x): x is T`
   has a nested TSTypeAnnotation around the inner type; without the
   trigger, bottom-up of T landed parent on TSTypePredicate without
   the inner wrapper.

3. AssignmentPatternNode (parameter default value) didn't re-parent
   its inner binding. `function f(x = 1)` — the bottom-up Identifier
   x reported parent.type === 'FunctionDeclaration' instead of
   'AssignmentPattern'. Same fix pattern as
   BindingAssignmentPatternNode (which was already correct).

4. NamedExports / TemplateSpan missing from SKIP_AS_PARENT. ESTree
   flattens both — the wrappers have no ESTree counterpart — but the
   walker built them as GenericTSNode, producing 'TSNamedExports' /
   'TSTemplateSpan' parent types.

5. MappedType TypeParameter scaffolding. `type X = { [K in keyof T]: ... }`
   — typescript-estree exposes K bare on TSMappedType.key, but lazy
   built a TSTypeParameter wrapper from SHAPES dispatch when the
   walker climbed through ts.TypeParameter. Skip TypeParameter as
   parent when its TS parent is MappedType.

6. ImportType argument LiteralType. `import('foo')` — eager exposes
   the bare StringLiteral as TSImportType.argument; lazy wrapped it
   in TSLiteralType. Skip the wrapper in SKIP_AS_PARENT when the TS
   parent is ImportType, plus flatten the top-down argument getter
   to skip the same wrapper. Drill TSTypeQuery.exprName for the
   typeof-import case.

Three documented divergences carry forward as accept-skips in the
sweep — each is a known scaffolding choice or missing wrapper that
the rule corpus doesn't observe:

- ChainExpression scaffolding (lazy single outer wrapper vs eager
  per-link)
- TSInterfaceHeritage missing (interface extends Y wraps Y in eager;
  lazy emits the bare Identifier)
- TSAssertClause missing (import-assertion wraps entries in eager;
  lazy emits ImportAttribute children directly)

The skip filter is structural: when a known wrapper is missing on
either side, the comparison is skipped at parent AND grandparent
levels (otherwise the off-by-one ripples). Re-investigating these
gaps later is straightforward — adding the wrapper class +
removing the skip entry.

Tested: predicate-coverage, lazy-estree, scope-compat, selector-
analysis, ts-ast-scan, compat-pipeline, jsx-react-x — all pass.
Bench/dogfood: 0 divergences across 30 files × 107 rules. Dify cold
4.9-5.8s on this branch vs 5.1-5.5s on master — within noise.
@johnsoncodehk
Copy link
Copy Markdown
Owner Author

Superseded by #95 — squashed and consolidated.

@johnsoncodehk johnsoncodehk deleted the refactor/parity-sweep-dogfood branch May 27, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant