mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
cb2a5051af
## Root cause
The reactive-function backend (`codegen_reactive_function.rs`) only knew
how to turn a plain `BindingIdentifier` lvalue into an
`AssignmentTarget`. Any **destructuring reassignment** to an existing
binding — `[x] = arr`, `({a} = obj)`, `({a} = {a:{}}), ([x] = [[]])` —
hit an unported `invariant_err("Destructuring reassignment targets are
not yet ported to oxc")`.
`codegen_function` deliberately swallows any emission error and falls
back to an **empty body** (a shim from the de-Babel batches). So every
function containing such an assignment silently compiled to `function
f() {}` — params and body both dropped — with **no diagnostic**. This is
a hard miscompile, not a whitespace diff.
## Fix
Port `ox_binding_pattern_to_assignment_target` to recursively reshape
`BindingPattern` → `AssignmentTarget`, since oxc (unlike Babel) models
declaration patterns and assignment targets as distinct node families.
Adds handling for:
- array patterns → `ArrayAssignmentTarget` (elements + holes + rest)
- object patterns → `ObjectAssignmentTarget`, distinguishing shorthand
(`{a}` / `{a = 1}` → `AssignmentTargetPropertyIdentifier`) from keyed
(`{a: b}` → `AssignmentTargetPropertyProperty`)
- rest elements (`...x`)
- nested patterns and defaults (`AssignmentTargetWithDefault`)
Mirrors Babel, which reuses the same pattern nodes as the assignment
LHS.
## Impact
23 corpus snapshots changed — **all move toward the fork golden, none
regress**; 14 now match the fork exactly. Verified old-vs-new token
distance to each fork golden.
Within this batch (`reduce-reactive-deps` /
`propagate-scope-deps-hir-fork`):
- `ssa-renaming-via-destructuring-with-mutation` — was empty function,
now matches the fork exactly.
- `ssa-renaming-ternary-destruction` /
`ssa-renaming-ternary-destruction-with-mutation` — were empty functions,
now compile with **matching dependency sets, cache keys, scope
boundaries, and dead-code elimination**; the only residual is
codegen-only (sequence-expression parenthesisation `([x]=[[]], …)` vs
`(([x]=[[]]), …)` and object-literal whitespace).
Other improved fixtures across the corpus (also previously
empty-bodied): `destructuring`, `destructuring-assignment`,
`destructuring-assignment-array-default`,
`destructure-direct-reassignment`, `destructure-in-branch-ssa`,
`destructuring-object-pattern-within-rest`,
`escape-analysis-destructured-rest-element`,
`call-args-destructuring-assignment`,
`array-pattern-spread-creates-array`, the `nonmutated-spread-*` set,
`repro-reassign-props`, `switch-with-only-default`,
`new-mutability__ssa-renaming-ternary-destruction`.
## Remaining work (out of scope here)
The rest of the batch's diffs are pure codegen and live in
`oxc_codegen`, not the dependency passes:
- sequence-expression parenthesisation of assignment operands (`(a=[],
b)` vs `((a=[]), b)`)
- JSX multi-line wrapping, TS type printing, object/array-literal
whitespace
The two `try-catch-*` fixtures (stray `;`) belong to a sibling
`fix/rc-try-catch` branch and are left untouched.
78 lines
1.1 KiB
Plaintext
78 lines
1.1 KiB
Plaintext
---
|
|
source: crates/oxc_react_compiler/tests/snapshot.rs
|
|
input_file: crates/oxc_react_compiler/fixtures/destructuring.js
|
|
---
|
|
import { c as _c } from "react/compiler-runtime";
|
|
function foo(a, b, c) {
|
|
const $ = _c(18);
|
|
let d;
|
|
let h;
|
|
let t0;
|
|
if ($[0] !== a) {
|
|
[d, t0, ...h] = a;
|
|
$[0] = a;
|
|
$[1] = d;
|
|
$[2] = h;
|
|
$[3] = t0;
|
|
} else {
|
|
d = $[1];
|
|
h = $[2];
|
|
t0 = $[3];
|
|
}
|
|
const [t1] = t0;
|
|
let g;
|
|
let t2;
|
|
if ($[4] !== t1) {
|
|
({e: t2, ...g} = t1);
|
|
$[4] = t1;
|
|
$[5] = g;
|
|
$[6] = t2;
|
|
} else {
|
|
g = $[5];
|
|
t2 = $[6];
|
|
}
|
|
const { f } = t2;
|
|
const { l: t3, p } = b;
|
|
const { m: t4 } = t3;
|
|
let o;
|
|
let t5;
|
|
if ($[7] !== t4) {
|
|
[t5, ...o] = t4;
|
|
$[7] = t4;
|
|
$[8] = o;
|
|
$[9] = t5;
|
|
} else {
|
|
o = $[8];
|
|
t5 = $[9];
|
|
}
|
|
const [n] = t5;
|
|
let t6;
|
|
if ($[10] !== d || $[11] !== f || $[12] !== g || $[13] !== h || $[14] !== n || $[15] !== o || $[16] !== p) {
|
|
t6 = [
|
|
d,
|
|
f,
|
|
g,
|
|
h,
|
|
n,
|
|
o,
|
|
p
|
|
];
|
|
$[10] = d;
|
|
$[11] = f;
|
|
$[12] = g;
|
|
$[13] = h;
|
|
$[14] = n;
|
|
$[15] = o;
|
|
$[16] = p;
|
|
$[17] = t6;
|
|
} else {
|
|
t6 = $[17];
|
|
}
|
|
return t6;
|
|
}
|
|
export const FIXTURE_ENTRYPOINT = {
|
|
fn: foo,
|
|
params: ["TodoAdd"],
|
|
isComponent: "TodoAdd"
|
|
};
|