mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
fix(packages/codegen): preserve in restriction through yield arguments (#26421)
`packages/codegen` can turn valid JavaScript into unparseable output when a `yield` expression appears in a classic `for` loop initializer and its argument contains an `in` operator.
For example, this input is valid:
```js
function* g(o) {
for (yield (1 in o); false;);
}
```
The JavaScript printer previously dropped the required grouping:
```js
function* g(o) {
for (yield 1 in o; false;);
}
```
An unparenthesized `in` is not allowed at this position in a classic `for` initializer. The same problem affects `yield*`, nested yields, and assignments inside the yield argument.
The printer now preserves the parentheses:
```js
function* g(o) {
for (yield (1 in o); false;);
}
```
The `for` printer already marks its initializer with `CTX_FORBID_IN`, but `printYieldExpression` discarded that context and printed its argument with `CTX_NONE`. The yield dispatch now passes its incoming context to `printYieldExpression`, which forwards only `CTX_FORBID_IN` to the argument when the yield expression is unwrapped. The argument retains its existing `PREC_YIELD` precedence.
When precedence requires parentheses around the entire yield expression, those parentheses already permit `in` inside. The argument context is cleared in that case, avoiding redundant inner parentheses:
```js
// Input
function* g(o) {
for ((yield (1 in o)) + 1; false;);
}
// Output
function* g(o) {
for ((yield 1 in o) + 1; false;);
}
```
Both `yield` and `yield*` use this path. Outside a restricted initializer, ordinary yields continue to omit unnecessary grouping: `yield (1 in o)` prints as `yield 1 in o`.
The correction applies to both ordinary and source-map-enabled builds and brings the JavaScript printer in line with the Rust fix in #26413.
This commit is contained in:
@@ -178,7 +178,7 @@ export function printExpression(
|
||||
printAwaitExpression(node, state, precedence, ctx);
|
||||
break;
|
||||
case "YieldExpression":
|
||||
printYieldExpression(node, state, precedence);
|
||||
printYieldExpression(node, state, precedence, ctx);
|
||||
break;
|
||||
case "ImportExpression":
|
||||
printImportExpression(node, state, precedence, ctx);
|
||||
@@ -922,8 +922,10 @@ function printYieldExpression(
|
||||
node: ESTree.YieldExpression,
|
||||
state: State,
|
||||
precedence: number,
|
||||
ctx: number,
|
||||
): void {
|
||||
const wrap = precedence >= PREC_ASSIGN;
|
||||
const argumentCtx = wrap ? CTX_NONE : ctx & CTX_FORBID_IN;
|
||||
if (wrap) write(state, "(", CAT_OTHER);
|
||||
|
||||
printSpaceBeforeIdentifier(state);
|
||||
@@ -933,7 +935,7 @@ function printYieldExpression(
|
||||
|
||||
if (node.argument != null) {
|
||||
write(state, " ", CAT_OTHER);
|
||||
printExpression(node.argument, state, PREC_YIELD, CTX_NONE);
|
||||
printExpression(node.argument, state, PREC_YIELD, argumentCtx);
|
||||
}
|
||||
|
||||
if (wrap) write(state, ")", CAT_CLOSE_BRACKET);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
function* g(o) {
|
||||
for (yield (1 in o); false;);
|
||||
for (x = yield (1 in o); false;);
|
||||
for (yield yield (1 in o); false;);
|
||||
for (yield (x = (1 in o)); false;);
|
||||
for ((yield (1 in o)) + 1; false;);
|
||||
yield (1 in o);
|
||||
|
||||
for (yield* (1 in o); false;);
|
||||
for (x = yield* (1 in o); false;);
|
||||
for (yield* yield (1 in o); false;);
|
||||
for (yield* (x = (1 in o)); false;);
|
||||
for ((yield* (1 in o)) + 1; false;);
|
||||
yield* (1 in o);
|
||||
}
|
||||
Reference in New Issue
Block a user