mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
fix(parser): allow in in arrow block bodies within for initializers (#26395)
## Summary
Allow the `in` operator inside an arrow function’s block body when the arrow appears in a `for` initializer:
```js
for (() => { a in b; };;);
```
This is valid JavaScript, but the parser previously reported a missing semicolon at `in`. The block now parses successfully, including async arrows and arrows with a single unparenthesized parameter.
## Cause and fix
A `for` initializer disables `Context::In` so that an unparenthesized `in` can delimit a `for…in` statement. Arrow block bodies inherited that disabled context through `parse_arrow_function_expression_body`, which called `parse_function_body` directly.
The block-body branch now calls `parse_function_body` through `context_add(Context::In, ...)`. This enables `in` throughout the function body and restores the enclosing context afterward. It also permits nested concise arrows inside that block to use `in`.
The change is confined to block bodies because concise expression bodies inherit the surrounding grammar’s `In` parameter. For example, this remains invalid:
```js
for (var f = () => a in b;;);
```
Restoring the enclosing context also keeps this invalid, rather than accidentally treating `a in b` as a regular initializer expression after parsing the first arrow:
```js
for (var f = () => {}, value = a in b;;);
```
This commit is contained in:
@@ -313,7 +313,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
self.ctx = self.ctx.and_await(r#async).and_yield(false);
|
||||
|
||||
let body = if self.at(Kind::LCurly) {
|
||||
ArrowFunctionBody::FunctionBody(self.parse_function_body())
|
||||
ArrowFunctionBody::FunctionBody(
|
||||
self.context_add(Context::In, Self::parse_function_body),
|
||||
)
|
||||
} else {
|
||||
// Remove TopLevel context for arrow function expression body
|
||||
let expr = self.context_remove(Context::TopLevel, |p| {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
for (var f = () => {}, value = a in b;;);
|
||||
@@ -0,0 +1 @@
|
||||
for (var f = () => a in b;;);
|
||||
@@ -0,0 +1,5 @@
|
||||
for (() => { a in b; };;);
|
||||
for (async () => { a in b; };;);
|
||||
for (x => { return x in b; };;);
|
||||
for (async x => { return x in b; };;);
|
||||
for (() => { () => a in b; };;);
|
||||
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 85/85 (100.00%)
|
||||
Positive Passed: 85/85 (100.00%)
|
||||
AST Parsed : 86/86 (100.00%)
|
||||
Positive Passed: 86/86 (100.00%)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 85/85 (100.00%)
|
||||
Positive Passed: 85/85 (100.00%)
|
||||
AST Parsed : 86/86 (100.00%)
|
||||
Positive Passed: 86/86 (100.00%)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 270/270 (100.00%)
|
||||
Positive Passed: 270/270 (100.00%)
|
||||
AST Parsed : 273/273 (100.00%)
|
||||
Positive Passed: 273/273 (100.00%)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 85/85 (100.00%)
|
||||
Positive Passed: 85/85 (100.00%)
|
||||
Negative Passed: 185/185 (100.00%)
|
||||
AST Parsed : 86/86 (100.00%)
|
||||
Positive Passed: 86/86 (100.00%)
|
||||
Negative Passed: 187/187 (100.00%)
|
||||
|
||||
× Cannot assign to 'arguments' in strict mode
|
||||
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
|
||||
@@ -136,6 +136,28 @@ Negative Passed: 185/185 (100.00%)
|
||||
╰────
|
||||
note: Classes are always strict mode code
|
||||
|
||||
× Only a single declaration is allowed in a `for...in` statement
|
||||
╭─[misc/fail/arrow-block-body-in-context.js:1:6]
|
||||
1 │ for (var f = () => {}, value = a in b;;);
|
||||
· ───────────────────────────
|
||||
╰────
|
||||
|
||||
× Expected `)` but found `;`
|
||||
╭─[misc/fail/arrow-block-body-in-context.js:1:38]
|
||||
1 │ for (var f = () => {}, value = a in b;;);
|
||||
· ┬ ┬
|
||||
· │ ╰── `)` expected
|
||||
· ╰── Opened here
|
||||
╰────
|
||||
|
||||
× Expected `)` but found `;`
|
||||
╭─[misc/fail/arrow-expression-body-in.js:1:26]
|
||||
1 │ for (var f = () => a in b;;);
|
||||
· ┬ ┬
|
||||
· │ ╰── `)` expected
|
||||
· ╰── Opened here
|
||||
╰────
|
||||
|
||||
× Illegal break statement
|
||||
╭─[misc/fail/arrow-function-jumps.js:1:25]
|
||||
1 │ while (true) { (() => { break; }); }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 85/85 (100.00%)
|
||||
Positive Passed: 81/85 (95.29%)
|
||||
AST Parsed : 86/86 (100.00%)
|
||||
Positive Passed: 82/86 (95.35%)
|
||||
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["private"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
transformer_misc Summary:
|
||||
AST Parsed : 85/85 (100.00%)
|
||||
Positive Passed: 85/85 (100.00%)
|
||||
AST Parsed : 86/86 (100.00%)
|
||||
Positive Passed: 86/86 (100.00%)
|
||||
|
||||
Reference in New Issue
Block a user