Related: #930 (missed lexical/var early errors), #929 (where this was found)
Invalid programs accepted
Found while gating #929 — it surfaced in that fix's 220-program corpus as a case wrong both before and after, and turned out to have nothing to do with static blocks. Every row was executed against node and zig-js, with each program wrapped in a never-called function so the verdict reflects parsing alone.
| program | node | zig-js |
|---|---|---|
try {} catch ([e]) { var e; } | SyntaxError | accepts |
try {} catch ({e}) { var e; } | SyntaxError | accepts |
try {} catch ([e]) { { var e; } } | SyntaxError | accepts |
try {} catch ([e]) { for (var e;;) break; } | SyntaxError | accepts |
try {} catch ([e]) { for (var e in {}); } | SyntaxError | accepts |
try {} catch ([e]) { for (var e of []); } | SyntaxError | accepts |
And the neighbours zig-js already gets right, which bound the fix:
| program | node | zig-js |
|---|---|---|
try {} catch (e) { var e; } | valid | accepts |
try {} catch (e) { for (var e;;) break; } | valid | accepts |
try {} catch (e) { for (var e in {}); } | valid | accepts |
try {} catch (e) { for (var e of []); } | valid | accepts |
try {} catch ([e]) { function e(){} } | SyntaxError | rejects |
try {} catch (e) { let e; } | SyntaxError | rejects |
The rule, as observed
A var may reuse a catch parameter's name only when the parameter is a plain BindingIdentifier — the Annex B.3.5 relaxation. For a destructuring pattern the ordinary early error applies: the pattern's BoundNames must not occur in the catch block's VarDeclaredNames, including vars hoisting out of nested blocks and for heads.
zig-js appears to apply the plain-identifier relaxation to destructuring parameters too, and also does not look through nested blocks for them.
Worth recording because I got it wrong from memory first: for-of is not an exception here. I expected catch (e) { for (var e of []); } to be an error on the recollection that B.3.5 excludes for-of heads; node accepts it, and zig-js agrees. So any fix should be driven by the executed table above, not by that recollection.
Suggested order
- Pin all twelve rows as a parser test.
- Find where catch parameter BoundNames are compared with the block's var names, and restrict the relaxation to the BindingIdentifier form. Note #928 already threads open lexical names through the block walk; a destructuring catch parameter is effectively a lexical binding for its block, so it may fit that machinery rather than a separate scan.