What
Found while implementing round 16's fix for #2632 (PR #2612, docs/plans/issue-2088.md).
introducesShadowedBinding's switch_body case (src/extractors/javascript.ts, the arm handling an unbraced case/default clause) checks each statement for lexical_declaration (via declarationDeclaresName) and, by name field, function_declaration/generator_function_declaration/class_declaration — the identical enumeration its statement_block case uses, and the identical enumeration that (pre-round-16) was missing using_declaration in both places. Round 16 closed the gap for condition 4's resolution question (findResolvingScopeNode) by adding a using_declaration disjunct scoped specifically to a statement_block ancestor — matching exactly what #2632 itself described (a block-scoped using shadow) and nothing more.
The switch_body case carries the identical enumeration gap: an unbraced case/default clause's own using run = mk(); is invisible to introducesShadowedBinding's switch_body arm for the identical reason statement_block's was, and — by the same reasoning #2632 itself gives for why this is a genuine (not merely conservative) gap for condition 4's resolution question specifically — a same-named module-level decoy shadowed only by such a case-level using declaration would resolve to the decoy with full confidence via findResolvingScopeNode, rather than failing safe.
This is not covered by round 16's own fix: that fix's new disjunct in findResolvingScopeNode is gated on current.type === 'statement_block' specifically, matching #2632's own repro shape exactly — it does not check a switch_body ancestor at all.
Direction
Per the exclusion ledger's now-explicit direction labels (see docs/plans/issue-2088.md's Success Criteria section, round 16): this is UNDER-escape if it manifests for condition 4's resolution question, the identical direction #2632 itself is — live code would be reported dead, not a recall trade-off. Per the standing rule the same round establishes, this may not be filed as an accepted limitation once confirmed; it must be fixed. This issue exists to track investigation and closure, not to document it as permanent scope.
Suggested next step
Confirm the switch_body case actually reaches this code path for an unbraced case/default clause containing a using declaration (verify against tree-sitter-javascript@0.25.0's grammar the same way #2632 did for statement_block), then close it the same way round 16 closed #2632: a matching disjunct in findResolvingScopeNode (not a widening of introducesShadowedBinding, for the same reason #2632's own round-16 fix avoided that), mirrored in the Rust engine, with its own escape-fallback fixture.
Where
What
Found while implementing round 16's fix for #2632 (PR #2612,
docs/plans/issue-2088.md).introducesShadowedBinding'sswitch_bodycase (src/extractors/javascript.ts, the arm handling an unbracedcase/defaultclause) checks each statement forlexical_declaration(viadeclarationDeclaresName) and, by name field,function_declaration/generator_function_declaration/class_declaration— the identical enumeration itsstatement_blockcase uses, and the identical enumeration that (pre-round-16) was missingusing_declarationin both places. Round 16 closed the gap for condition 4's resolution question (findResolvingScopeNode) by adding ausing_declarationdisjunct scoped specifically to astatement_blockancestor — matching exactly what #2632 itself described (a block-scopedusingshadow) and nothing more.The
switch_bodycase carries the identical enumeration gap: an unbracedcase/defaultclause's ownusing run = mk();is invisible tointroducesShadowedBinding'sswitch_bodyarm for the identical reasonstatement_block's was, and — by the same reasoning #2632 itself gives for why this is a genuine (not merely conservative) gap for condition 4's resolution question specifically — a same-named module-level decoy shadowed only by such a case-levelusingdeclaration would resolve to the decoy with full confidence viafindResolvingScopeNode, rather than failing safe.This is not covered by round 16's own fix: that fix's new disjunct in
findResolvingScopeNodeis gated oncurrent.type === 'statement_block'specifically, matching #2632's own repro shape exactly — it does not check aswitch_bodyancestor at all.Direction
Per the exclusion ledger's now-explicit direction labels (see
docs/plans/issue-2088.md's Success Criteria section, round 16): this isUNDER-escapeif it manifests for condition 4's resolution question, the identical direction #2632 itself is — live code would be reported dead, not a recall trade-off. Per the standing rule the same round establishes, this may not be filed as an accepted limitation once confirmed; it must be fixed. This issue exists to track investigation and closure, not to document it as permanent scope.Suggested next step
Confirm the
switch_bodycase actually reaches this code path for an unbracedcase/defaultclause containing ausingdeclaration (verify againsttree-sitter-javascript@0.25.0's grammar the same way #2632 did forstatement_block), then close it the same way round 16 closed #2632: a matching disjunct infindResolvingScopeNode(not a widening ofintroducesShadowedBinding, for the same reason #2632's own round-16 fix avoided that), mirrored in the Rust engine, with its own escape-fallback fixture.Where
src/extractors/javascript.ts(introducesShadowedBinding'sswitch_bodycase)docs/plans/issue-2088.md— WU-2'sfindResolvingScopeNode(round 16's ownusing_declarationdisjunct, scoped tostatement_blockonly)statement_blockcase of this identical gap, closed round 16)