Repository: facebook/react
Pull request: [compiler] Fix bug with reassigning function param in destructuring


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

Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
patch: @@ -349,11 +349,9 @@ function codegenReactiveFunction(
   fn: ReactiveFunction,
 ): Result<CodegenFunction, CompilerError> {
   for (const param of fn.params) {
-    if (param.kind === 'Identifier') {
-      cx.temp.set(param.identifier.declarationId, null);
-    } else {
-      cx.temp.set(param.place.identifier.declarationId, null);
-    }
+    const place = param.kind === 'Identifier' ? param : param.place;
+    cx.temp.set(place.identifier.declarationId, null);
+    cx.declare(place.identifier);
   }
 
   const params = fn.params.map(param => convertParameter(param));

filename: compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/ExtractScopeDeclarationsFromDestructuring.ts
patch: @@ -79,6 +79,10 @@ export function extractScopeDeclarationsFromDestructuring(
   fn: ReactiveFunction,
 ): void {
   const state = new State(fn.env);
+  for (const param of fn.params) {
+    const place = param.kind === 'Identifier' ? param : param.place;
+    state.declared.add(place.identifier.declarationId);
+  }
   visitReactiveFunction(fn, new Visitor(), state);
 }
 

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-reassign-props.expect.md
patch: @@ -0,0 +1,65 @@
+
+## Input
+
+```javascript
+import {Stringify, useIdentity} from 'shared-runtime';
+
+function Component({other, ...props}, ref) {
+  [props, ref] = useIdentity([props, ref]);
+  return <Stringify props={props} />;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{a: 0, b: 'hello', children: <div>Hello</div>}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { Stringify, useIdentity } from "shared-runtime";
+
+function Component(t0, ref) {
+  const $ = _c(7);
+  let props;
+  if ($[0] !== t0) {
+    let { other, ...t1 } = t0;
+    props = t1;
+    $[0] = t0;
+    $[1] = props;
+  } else {
+    props = $[1];
+  }
+  let t1;
+  if ($[2] !== props || $[3] !== ref) {
+    t1 = [props, ref];
+    $[2] = props;
+    $[3] = ref;
+    $[4] = t1;
+  } else {
+    t1 = $[4];
+  }
+  [props, ref] = useIdentity(t1);
+  let t2;
+  if ($[5] !== props) {
+    t2 = <Stringify props={props} />;
+    $[5] = props;
+    $[6] = t2;
+  } else {
+    t2 = $[6];
+  }
+  return t2;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{ a: 0, b: "hello", children: <div>Hello</div> }],
+};
+
+```
+      
+### Eval output
+(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

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-reassign-props.js
patch: @@ -0,0 +1,11 @@
+import {Stringify, useIdentity} from 'shared-runtime';
+
+function Component({other, ...props}, ref) {
+  [props, ref] = useIdentity([props, ref]);
+  return <Stringify props={props} />;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{a: 0, b: 'hello', children: <div>Hello</div>}],
+};