Repository: facebook/react
Pull request: [compiler] Handle member expr as computed property

This PR loosens the restriction on the types of computed properties we can handle.

Previously, we would disallow anything that is not an identifier because non-identifiers could be mutating. But member expressions are not mutating so we can treat them similar to identifiers.


Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
patch: @@ -1418,7 +1418,7 @@ function lowerObjectPropertyKey(
       name: key.node.value,
     };
   } else if (property.node.computed && key.isExpression()) {
-    if (!key.isIdentifier()) {
+    if (!key.isIdentifier() && !key.isMemberExpression()) {
       /*
        * NOTE: allowing complex key expressions can trigger a bug where a mutation is made conditional
        * see fixture

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.expect.md
patch: @@ -0,0 +1,37 @@
+
+## Input
+
+```javascript
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
+
+function Component(props) {
+  const obj = {mutateAndReturn};
+  const key = {};
+  const context = {
+    [obj.mutateAndReturn(key)]: identity([props.value]),
+  };
+  mutate(key);
+  return context;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{value: 42}],
+};
+
+```
+
+
+## Error
+
+```
+   5 |   const key = {};
+   6 |   const context = {
+>  7 |     [obj.mutateAndReturn(key)]: identity([props.value]),
+     |      ^^^^^^^^^^^^^^^^^^^^^^^^ Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression (7:7)
+   8 |   };
+   9 |   mutate(key);
+  10 |   return context;
+```
+          
+      
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-object-expression-member-expr-call.js
patch: @@ -0,0 +1,16 @@
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
+
+function Component(props) {
+  const obj = {mutateAndReturn};
+  const key = {};
+  const context = {
+    [obj.mutateAndReturn(key)]: identity([props.value]),
+  };
+  mutate(key);
+  return context;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{value: 42}],
+};

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-member.expect.md
patch: @@ -0,0 +1,53 @@
+
+## Input
+
+```javascript
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
+
+function Component(props) {
+  const key = {a: 'key'};
+  const context = {
+    [key.a]: identity([props.value]),
+  };
+  mutate(key);
+  return context;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{value: 42}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { identity, mutate, mutateAndReturn } from "shared-runtime";
+
+function Component(props) {
+  const $ = _c(2);
+  let context;
+  if ($[0] !== props.value) {
+    const key = { a: "key" };
+    context = { [key.a]: identity([props.value]) };
+
+    mutate(key);
+    $[0] = props.value;
+    $[1] = context;
+  } else {
+    context = $[1];
+  }
+  return context;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{ value: 42 }],
+};
+
+```
+      
+### Eval output
+(kind: ok) {"key":[42]}
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/object-expression-computed-member.js
patch: @@ -0,0 +1,15 @@
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
+
+function Component(props) {
+  const key = {a: 'key'};
+  const context = {
+    [key.a]: identity([props.value]),
+  };
+  mutate(key);
+  return context;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{value: 42}],
+};