Files
oxc-project__oxc/crates/oxc_react_compiler/tests/snapshots/array-pattern-spread-creates-array.snap
Boshen cb2a5051af fix(react_compiler): codegen destructuring reassignment targets (#24131)
## 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.
2026-07-04 10:43:43 +08:00

63 lines
1.3 KiB
Plaintext

---
source: crates/oxc_react_compiler/tests/snapshot.rs
input_file: crates/oxc_react_compiler/fixtures/array-pattern-spread-creates-array.js
---
import { c as _c } from "react/compiler-runtime";
// @validatePreserveExistingMemoizationGuarantees @validateExhaustiveMemoizationDependencies:false
import { useMemo } from "react";
import { makeObject_Primitives, ValidateMemoization } from "shared-runtime";
function Component(props) {
const $ = _c(9);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = makeObject_Primitives();
$[0] = t0;
} else {
t0 = $[0];
}
const x = t0;
let rest;
if ($[1] !== props.array) {
[, ...rest] = props.array;
rest.push(x);
$[1] = props.array;
$[2] = rest;
} else {
rest = $[2];
}
const rest_0 = rest;
let t1;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t1 = <ValidateMemoization inputs={[]} output={x} />;
$[3] = t1;
} else {
t1 = $[3];
}
let t2;
if ($[4] !== props.array) {
t2 = [props.array];
$[4] = props.array;
$[5] = t2;
} else {
t2 = $[5];
}
let t3;
if ($[6] !== rest_0 || $[7] !== t2) {
t3 = <>{t1}<ValidateMemoization inputs={t2} output={rest_0} /></>;
$[6] = rest_0;
$[7] = t2;
$[8] = t3;
} else {
t3 = $[8];
}
return t3;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ array: [
0,
1,
2
] }]
};