@samitouri / QOS-React-2 / commits / 9894c488e0

[compiler] Fix bug with reassigning function param in destructuring (#33624)

Closes #33577, a bug with ExtractScopeDeclarationsFromDestructuring and codegen when a function param is reassigned. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33624). * #33643 * #33642 * #33640 * #33625 * __->__ #33624

Joseph Savona committed Jun 25, 2025 at 11:10 UTC 9894c488e0d9a4d9759d80ba8666d4d094b894e9
4 files changed +83 -5
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+3 -5
@@ -349,11 +349,9 @@ function codegenReactiveFunction(
349 fn: ReactiveFunction,
350 ): Result<CodegenFunction, CompilerError> {
351 for (const param of fn.params) {
352 - if (param.kind === 'Identifier') {
353 - cx.temp.set(param.identifier.declarationId, null);
354 - } else {
355 - cx.temp.set(param.place.identifier.declarationId, null);
356 - }
352 + const place = param.kind === 'Identifier' ? param : param.place;
353 + cx.temp.set(place.identifier.declarationId, null);
354 + cx.declare(place.identifier);
355 }
356
357 const params = fn.params.map(param => convertParameter(param));
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/ExtractScopeDeclarationsFromDestructuring.ts
+4
@@ -79,6 +79,10 @@ export function extractScopeDeclarationsFromDestructuring(
79 fn: ReactiveFunction,
80 ): void {
81 const state = new State(fn.env);
82 + for (const param of fn.params) {
83 + const place = param.kind === 'Identifier' ? param : param.place;
84 + state.declared.add(place.identifier.declarationId);
85 + }
86 visitReactiveFunction(fn, new Visitor(), state);
87 }
88
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-reassign-props.expect.md new
+65
@@ -0,0 +1,65 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {Stringify, useIdentity} from 'shared-runtime';
6 +
7 +function Component({other, ...props}, ref) {
8 + [props, ref] = useIdentity([props, ref]);
9 + return <Stringify props={props} />;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{a: 0, b: 'hello', children: <div>Hello</div>}],
15 +};
16 +
17 +```
18 +
19 +## Code
20 +
21 +```javascript
22 +import { c as _c } from "react/compiler-runtime";
23 +import { Stringify, useIdentity } from "shared-runtime";
24 +
25 +function Component(t0, ref) {
26 + const $ = _c(7);
27 + let props;
28 + if ($[0] !== t0) {
29 + let { other, ...t1 } = t0;
30 + props = t1;
31 + $[0] = t0;
32 + $[1] = props;
33 + } else {
34 + props = $[1];
35 + }
36 + let t1;
37 + if ($[2] !== props || $[3] !== ref) {
38 + t1 = [props, ref];
39 + $[2] = props;
40 + $[3] = ref;
41 + $[4] = t1;
42 + } else {
43 + t1 = $[4];
44 + }
45 + [props, ref] = useIdentity(t1);
46 + let t2;
47 + if ($[5] !== props) {
48 + t2 = <Stringify props={props} />;
49 + $[5] = props;
50 + $[6] = t2;
51 + } else {
52 + t2 = $[6];
53 + }
54 + return t2;
55 +}
56 +
57 +export const FIXTURE_ENTRYPOINT = {
58 + fn: Component,
59 + params: [{ a: 0, b: "hello", children: <div>Hello</div> }],
60 +};
61 +
62 +```
63 +
64 +### Eval output
65 +(kind: ok) <div>{"props":{"a":0,"b":"hello","children":{"type":"div","key":null,"props":{"children":"Hello"},"_owner":"[[ cyclic ref *3 ]]","_store":{}}}}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-reassign-props.js new
+11
@@ -0,0 +1,11 @@
1 +import {Stringify, useIdentity} from 'shared-runtime';
2 +
3 +function Component({other, ...props}, ref) {
4 + [props, ref] = useIdentity([props, ref]);
5 + return <Stringify props={props} />;
6 +}
7 +
8 +export const FIXTURE_ENTRYPOINT = {
9 + fn: Component,
10 + params: [{a: 0, b: 'hello', children: <div>Hello</div>}],
11 +};