[compiler] Copy fixtures affected by new inference (#33495)
--- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33495). * #33571 * #33558 * #33547 * #33543 * #33533 * #33532 * #33530 * #33526 * #33522 * #33518 * #33514 * #33513 * #33512 * #33504 * #33500 * #33497 * #33496 * __->__ #33495 * #33494 * #33572
Joseph Savona committed
Jun 18, 2025 at 12:58 UTC
df080d228bdf5260067235c64daaa57ec3cfac23
46 files changed
+1912
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/aliased-nested-scope-truncated-dep.expect.md
new
+221
@@ -0,0 +1,221 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {
6
+ Stringify,
7
+ mutate,
8
+ identity,
9
+ shallowCopy,
10
+ setPropertyByKey,
11
+} from 'shared-runtime';
12
+
13
+/**
14
+ * This fixture is similar to `bug-aliased-capture-aliased-mutate` and
15
+ * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
16
+ * dependency extraction.
17
+ *
18
+ * NOTE: this fixture is currently valid, but will break with optimizations:
19
+ * - Scope and mutable-range based reordering may move the array creation
20
+ * *after* the `mutate(aliasedObj)` call. This is invalid if mutate
21
+ * reassigns inner properties.
22
+ * - RecycleInto or other deeper-equality optimizations may produce invalid
23
+ * output -- it may compare the array's contents / dependencies too early.
24
+ * - Runtime validation for immutable values will break if `mutate` does
25
+ * interior mutation of the value captured into the array.
26
+ *
27
+ * Before scope block creation, HIR looks like this:
28
+ * //
29
+ * // $1 is unscoped as obj's mutable range will be
30
+ * // extended in a later pass
31
+ * //
32
+ * $1 = LoadLocal obj@0[0:12]
33
+ * $2 = PropertyLoad $1.id
34
+ * //
35
+ * // $3 gets assigned a scope as Array is an allocating
36
+ * // instruction, but this does *not* get extended or
37
+ * // merged into the later mutation site.
38
+ * // (explained in `bug-aliased-capture-aliased-mutate`)
39
+ * //
40
+ * $3@1 = Array[$2]
41
+ * ...
42
+ * $10@0 = LoadLocal shallowCopy@0[0, 12]
43
+ * $11 = LoadGlobal mutate
44
+ * $12 = $11($10@0[0, 12])
45
+ *
46
+ * When filling in scope dependencies, we find that it's incorrect to depend on
47
+ * PropertyLoads from obj as it hasn't completed its mutable range. Following
48
+ * the immutable / mutable-new typing system, we check the identity of obj to
49
+ * detect whether it was newly created (and thus mutable) in this render pass.
50
+ *
51
+ * HIR with scopes looks like this.
52
+ * bb0:
53
+ * $1 = LoadLocal obj@0[0:12]
54
+ * $2 = PropertyLoad $1.id
55
+ * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
56
+ * bb1:
57
+ * $3@1 = Array[$2]
58
+ * goto bb2
59
+ * bb2:
60
+ * ...
61
+ *
62
+ * This is surprising as deps now is entirely decoupled from temporaries used
63
+ * by the block itself. scope @1's instructions now reference a value (1)
64
+ * produced outside its scope range and (2) not represented in its dependencies
65
+ *
66
+ * The right thing to do is to ensure that all Loads from a value get assigned
67
+ * the value's reactive scope. This also requires track mutating and aliasing
68
+ * separately from scope range. In this example, that would correctly merge
69
+ * the scopes of $3 with obj.
70
+ * Runtime validation and optimizations such as ReactiveGraph-based reordering
71
+ * require this as well.
72
+ *
73
+ * A tempting fix is to instead extend $3's ReactiveScope range up to include
74
+ * $2 (the PropertyLoad). This fixes dependency deduping but not reordering
75
+ * and mutability.
76
+ */
77
+function Component({prop}) {
78
+ let obj = shallowCopy(prop);
79
+ const aliasedObj = identity(obj);
80
+
81
+ // [obj.id] currently is assigned its own reactive scope
82
+ const id = [obj.id];
83
+
84
+ // Writing to the alias may reassign to previously captured references.
85
+ // The compiler currently produces valid output, but this breaks with
86
+ // reordering, recycleInto, and other potential optimizations.
87
+ mutate(aliasedObj);
88
+ setPropertyByKey(aliasedObj, 'id', prop.id + 1);
89
+
90
+ return <Stringify id={id} />;
91
+}
92
+
93
+export const FIXTURE_ENTRYPOINT = {
94
+ fn: Component,
95
+ params: [{prop: {id: 1}}],
96
+ sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}],
97
+};
98
+
99
+```
100
+
101
+## Code
102
+
103
+```javascript
104
+import { c as _c } from "react/compiler-runtime";
105
+import {
106
+ Stringify,
107
+ mutate,
108
+ identity,
109
+ shallowCopy,
110
+ setPropertyByKey,
111
+} from "shared-runtime";
112
+
113
+/**
114
+ * This fixture is similar to `bug-aliased-capture-aliased-mutate` and
115
+ * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
116
+ * dependency extraction.
117
+ *
118
+ * NOTE: this fixture is currently valid, but will break with optimizations:
119
+ * - Scope and mutable-range based reordering may move the array creation
120
+ * *after* the `mutate(aliasedObj)` call. This is invalid if mutate
121
+ * reassigns inner properties.
122
+ * - RecycleInto or other deeper-equality optimizations may produce invalid
123
+ * output -- it may compare the array's contents / dependencies too early.
124
+ * - Runtime validation for immutable values will break if `mutate` does
125
+ * interior mutation of the value captured into the array.
126
+ *
127
+ * Before scope block creation, HIR looks like this:
128
+ * //
129
+ * // $1 is unscoped as obj's mutable range will be
130
+ * // extended in a later pass
131
+ * //
132
+ * $1 = LoadLocal obj@0[0:12]
133
+ * $2 = PropertyLoad $1.id
134
+ * //
135
+ * // $3 gets assigned a scope as Array is an allocating
136
+ * // instruction, but this does *not* get extended or
137
+ * // merged into the later mutation site.
138
+ * // (explained in `bug-aliased-capture-aliased-mutate`)
139
+ * //
140
+ * $3@1 = Array[$2]
141
+ * ...
142
+ * $10@0 = LoadLocal shallowCopy@0[0, 12]
143
+ * $11 = LoadGlobal mutate
144
+ * $12 = $11($10@0[0, 12])
145
+ *
146
+ * When filling in scope dependencies, we find that it's incorrect to depend on
147
+ * PropertyLoads from obj as it hasn't completed its mutable range. Following
148
+ * the immutable / mutable-new typing system, we check the identity of obj to
149
+ * detect whether it was newly created (and thus mutable) in this render pass.
150
+ *
151
+ * HIR with scopes looks like this.
152
+ * bb0:
153
+ * $1 = LoadLocal obj@0[0:12]
154
+ * $2 = PropertyLoad $1.id
155
+ * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
156
+ * bb1:
157
+ * $3@1 = Array[$2]
158
+ * goto bb2
159
+ * bb2:
160
+ * ...
161
+ *
162
+ * This is surprising as deps now is entirely decoupled from temporaries used
163
+ * by the block itself. scope @1's instructions now reference a value (1)
164
+ * produced outside its scope range and (2) not represented in its dependencies
165
+ *
166
+ * The right thing to do is to ensure that all Loads from a value get assigned
167
+ * the value's reactive scope. This also requires track mutating and aliasing
168
+ * separately from scope range. In this example, that would correctly merge
169
+ * the scopes of $3 with obj.
170
+ * Runtime validation and optimizations such as ReactiveGraph-based reordering
171
+ * require this as well.
172
+ *
173
+ * A tempting fix is to instead extend $3's ReactiveScope range up to include
174
+ * $2 (the PropertyLoad). This fixes dependency deduping but not reordering
175
+ * and mutability.
176
+ */
177
+function Component(t0) {
178
+ const $ = _c(4);
179
+ const { prop } = t0;
180
+ let t1;
181
+ if ($[0] !== prop) {
182
+ const obj = shallowCopy(prop);
183
+ const aliasedObj = identity(obj);
184
+ let t2;
185
+ if ($[2] !== obj) {
186
+ t2 = [obj.id];
187
+ $[2] = obj;
188
+ $[3] = t2;
189
+ } else {
190
+ t2 = $[3];
191
+ }
192
+ const id = t2;
193
+
194
+ mutate(aliasedObj);
195
+ setPropertyByKey(aliasedObj, "id", prop.id + 1);
196
+
197
+ t1 = <Stringify id={id} />;
198
+ $[0] = prop;
199
+ $[1] = t1;
200
+ } else {
201
+ t1 = $[1];
202
+ }
203
+ return t1;
204
+}
205
+
206
+export const FIXTURE_ENTRYPOINT = {
207
+ fn: Component,
208
+ params: [{ prop: { id: 1 } }],
209
+ sequentialRenders: [
210
+ { prop: { id: 1 } },
211
+ { prop: { id: 1 } },
212
+ { prop: { id: 2 } },
213
+ ],
214
+};
215
+
216
+```
217
+
218
+### Eval output
219
+(kind: ok) <div>{"id":[1]}</div>
220
+<div>{"id":[1]}</div>
221
+<div>{"id":[2]}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/aliased-nested-scope-truncated-dep.tsx
new
+93
@@ -0,0 +1,93 @@
1
+import {
2
+ Stringify,
3
+ mutate,
4
+ identity,
5
+ shallowCopy,
6
+ setPropertyByKey,
7
+} from 'shared-runtime';
8
+
9
+/**
10
+ * This fixture is similar to `bug-aliased-capture-aliased-mutate` and
11
+ * `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
12
+ * dependency extraction.
13
+ *
14
+ * NOTE: this fixture is currently valid, but will break with optimizations:
15
+ * - Scope and mutable-range based reordering may move the array creation
16
+ * *after* the `mutate(aliasedObj)` call. This is invalid if mutate
17
+ * reassigns inner properties.
18
+ * - RecycleInto or other deeper-equality optimizations may produce invalid
19
+ * output -- it may compare the array's contents / dependencies too early.
20
+ * - Runtime validation for immutable values will break if `mutate` does
21
+ * interior mutation of the value captured into the array.
22
+ *
23
+ * Before scope block creation, HIR looks like this:
24
+ * //
25
+ * // $1 is unscoped as obj's mutable range will be
26
+ * // extended in a later pass
27
+ * //
28
+ * $1 = LoadLocal obj@0[0:12]
29
+ * $2 = PropertyLoad $1.id
30
+ * //
31
+ * // $3 gets assigned a scope as Array is an allocating
32
+ * // instruction, but this does *not* get extended or
33
+ * // merged into the later mutation site.
34
+ * // (explained in `bug-aliased-capture-aliased-mutate`)
35
+ * //
36
+ * $3@1 = Array[$2]
37
+ * ...
38
+ * $10@0 = LoadLocal shallowCopy@0[0, 12]
39
+ * $11 = LoadGlobal mutate
40
+ * $12 = $11($10@0[0, 12])
41
+ *
42
+ * When filling in scope dependencies, we find that it's incorrect to depend on
43
+ * PropertyLoads from obj as it hasn't completed its mutable range. Following
44
+ * the immutable / mutable-new typing system, we check the identity of obj to
45
+ * detect whether it was newly created (and thus mutable) in this render pass.
46
+ *
47
+ * HIR with scopes looks like this.
48
+ * bb0:
49
+ * $1 = LoadLocal obj@0[0:12]
50
+ * $2 = PropertyLoad $1.id
51
+ * scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
52
+ * bb1:
53
+ * $3@1 = Array[$2]
54
+ * goto bb2
55
+ * bb2:
56
+ * ...
57
+ *
58
+ * This is surprising as deps now is entirely decoupled from temporaries used
59
+ * by the block itself. scope @1's instructions now reference a value (1)
60
+ * produced outside its scope range and (2) not represented in its dependencies
61
+ *
62
+ * The right thing to do is to ensure that all Loads from a value get assigned
63
+ * the value's reactive scope. This also requires track mutating and aliasing
64
+ * separately from scope range. In this example, that would correctly merge
65
+ * the scopes of $3 with obj.
66
+ * Runtime validation and optimizations such as ReactiveGraph-based reordering
67
+ * require this as well.
68
+ *
69
+ * A tempting fix is to instead extend $3's ReactiveScope range up to include
70
+ * $2 (the PropertyLoad). This fixes dependency deduping but not reordering
71
+ * and mutability.
72
+ */
73
+function Component({prop}) {
74
+ let obj = shallowCopy(prop);
75
+ const aliasedObj = identity(obj);
76
+
77
+ // [obj.id] currently is assigned its own reactive scope
78
+ const id = [obj.id];
79
+
80
+ // Writing to the alias may reassign to previously captured references.
81
+ // The compiler currently produces valid output, but this breaks with
82
+ // reordering, recycleInto, and other potential optimizations.
83
+ mutate(aliasedObj);
84
+ setPropertyByKey(aliasedObj, 'id', prop.id + 1);
85
+
86
+ return <Stringify id={id} />;
87
+}
88
+
89
+export const FIXTURE_ENTRYPOINT = {
90
+ fn: Component,
91
+ params: [{prop: {id: 1}}],
92
+ sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}],
93
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-named-callback-cross-context.expect.md
new
+133
@@ -0,0 +1,133 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {Stringify} from 'shared-runtime';
6
+
7
+/**
8
+ * Forked from array-map-simple.js
9
+ *
10
+ * Named lambdas (e.g. cb1) may be defined in the top scope of a function and
11
+ * used in a different lambda (getArrMap1).
12
+ *
13
+ * Here, we should try to determine if cb1 is actually called. In this case:
14
+ * - getArrMap1 is assumed to be called as it's passed to JSX
15
+ * - cb1 is not assumed to be called since it's only used as a call operand
16
+ */
17
+function useFoo({arr1, arr2}) {
18
+ const cb1 = e => arr1[0].value + e.value;
19
+ const getArrMap1 = () => arr1.map(cb1);
20
+ const cb2 = e => arr2[0].value + e.value;
21
+ const getArrMap2 = () => arr1.map(cb2);
22
+ return (
23
+ <Stringify
24
+ getArrMap1={getArrMap1}
25
+ getArrMap2={getArrMap2}
26
+ shouldInvokeFns={true}
27
+ />
28
+ );
29
+}
30
+
31
+export const FIXTURE_ENTRYPOINT = {
32
+ fn: useFoo,
33
+ params: [{arr1: [], arr2: []}],
34
+ sequentialRenders: [
35
+ {arr1: [], arr2: []},
36
+ {arr1: [], arr2: null},
37
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
38
+ ],
39
+};
40
+
41
+```
42
+
43
+## Code
44
+
45
+```javascript
46
+import { c as _c } from "react/compiler-runtime";
47
+import { Stringify } from "shared-runtime";
48
+
49
+/**
50
+ * Forked from array-map-simple.js
51
+ *
52
+ * Named lambdas (e.g. cb1) may be defined in the top scope of a function and
53
+ * used in a different lambda (getArrMap1).
54
+ *
55
+ * Here, we should try to determine if cb1 is actually called. In this case:
56
+ * - getArrMap1 is assumed to be called as it's passed to JSX
57
+ * - cb1 is not assumed to be called since it's only used as a call operand
58
+ */
59
+function useFoo(t0) {
60
+ const $ = _c(13);
61
+ const { arr1, arr2 } = t0;
62
+ let t1;
63
+ if ($[0] !== arr1[0]) {
64
+ t1 = (e) => arr1[0].value + e.value;
65
+ $[0] = arr1[0];
66
+ $[1] = t1;
67
+ } else {
68
+ t1 = $[1];
69
+ }
70
+ const cb1 = t1;
71
+ let t2;
72
+ if ($[2] !== arr1 || $[3] !== cb1) {
73
+ t2 = () => arr1.map(cb1);
74
+ $[2] = arr1;
75
+ $[3] = cb1;
76
+ $[4] = t2;
77
+ } else {
78
+ t2 = $[4];
79
+ }
80
+ const getArrMap1 = t2;
81
+ let t3;
82
+ if ($[5] !== arr2) {
83
+ t3 = (e_0) => arr2[0].value + e_0.value;
84
+ $[5] = arr2;
85
+ $[6] = t3;
86
+ } else {
87
+ t3 = $[6];
88
+ }
89
+ const cb2 = t3;
90
+ let t4;
91
+ if ($[7] !== arr1 || $[8] !== cb2) {
92
+ t4 = () => arr1.map(cb2);
93
+ $[7] = arr1;
94
+ $[8] = cb2;
95
+ $[9] = t4;
96
+ } else {
97
+ t4 = $[9];
98
+ }
99
+ const getArrMap2 = t4;
100
+ let t5;
101
+ if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
102
+ t5 = (
103
+ <Stringify
104
+ getArrMap1={getArrMap1}
105
+ getArrMap2={getArrMap2}
106
+ shouldInvokeFns={true}
107
+ />
108
+ );
109
+ $[10] = getArrMap1;
110
+ $[11] = getArrMap2;
111
+ $[12] = t5;
112
+ } else {
113
+ t5 = $[12];
114
+ }
115
+ return t5;
116
+}
117
+
118
+export const FIXTURE_ENTRYPOINT = {
119
+ fn: useFoo,
120
+ params: [{ arr1: [], arr2: [] }],
121
+ sequentialRenders: [
122
+ { arr1: [], arr2: [] },
123
+ { arr1: [], arr2: null },
124
+ { arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
125
+ ],
126
+};
127
+
128
+```
129
+
130
+### Eval output
131
+(kind: ok) <div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
132
+<div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
133
+<div>{"getArrMap1":{"kind":"Function","result":[2,3]},"getArrMap2":{"kind":"Function","result":[0,1]},"shouldInvokeFns":true}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/array-map-named-callback-cross-context.js
new
+35
@@ -0,0 +1,35 @@
1
+import {Stringify} from 'shared-runtime';
2
+
3
+/**
4
+ * Forked from array-map-simple.js
5
+ *
6
+ * Named lambdas (e.g. cb1) may be defined in the top scope of a function and
7
+ * used in a different lambda (getArrMap1).
8
+ *
9
+ * Here, we should try to determine if cb1 is actually called. In this case:
10
+ * - getArrMap1 is assumed to be called as it's passed to JSX
11
+ * - cb1 is not assumed to be called since it's only used as a call operand
12
+ */
13
+function useFoo({arr1, arr2}) {
14
+ const cb1 = e => arr1[0].value + e.value;
15
+ const getArrMap1 = () => arr1.map(cb1);
16
+ const cb2 = e => arr2[0].value + e.value;
17
+ const getArrMap2 = () => arr1.map(cb2);
18
+ return (
19
+ <Stringify
20
+ getArrMap1={getArrMap1}
21
+ getArrMap2={getArrMap2}
22
+ shouldInvokeFns={true}
23
+ />
24
+ );
25
+}
26
+
27
+export const FIXTURE_ENTRYPOINT = {
28
+ fn: useFoo,
29
+ params: [{arr1: [], arr2: []}],
30
+ sequentialRenders: [
31
+ {arr1: [], arr2: []},
32
+ {arr1: [], arr2: null},
33
+ {arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
34
+ ],
35
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-2-iife.expect.md
new
+52
@@ -0,0 +1,52 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function bar(a) {
6
+ let x = [a];
7
+ let y = {};
8
+ (function () {
9
+ y = x[0][1];
10
+ })();
11
+
12
+ return y;
13
+}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: bar,
17
+ params: [['val1', 'val2']],
18
+ isComponent: false,
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+function bar(a) {
28
+ const $ = _c(2);
29
+ let y;
30
+ if ($[0] !== a) {
31
+ const x = [a];
32
+ y = {};
33
+
34
+ y = x[0][1];
35
+ $[0] = a;
36
+ $[1] = y;
37
+ } else {
38
+ y = $[1];
39
+ }
40
+ return y;
41
+}
42
+
43
+export const FIXTURE_ENTRYPOINT = {
44
+ fn: bar,
45
+ params: [["val1", "val2"]],
46
+ isComponent: false,
47
+};
48
+
49
+```
50
+
51
+### Eval output
52
+(kind: ok) "val2"
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-2-iife.js
new
+15
@@ -0,0 +1,15 @@
1
+function bar(a) {
2
+ let x = [a];
3
+ let y = {};
4
+ (function () {
5
+ y = x[0][1];
6
+ })();
7
+
8
+ return y;
9
+}
10
+
11
+export const FIXTURE_ENTRYPOINT = {
12
+ fn: bar,
13
+ params: [['val1', 'val2']],
14
+ isComponent: false,
15
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-3-iife.expect.md
new
+61
@@ -0,0 +1,61 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function bar(a, b) {
6
+ let x = [a, b];
7
+ let y = {};
8
+ let t = {};
9
+ (function () {
10
+ y = x[0][1];
11
+ t = x[1][0];
12
+ })();
13
+
14
+ return y;
15
+}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: bar,
19
+ params: [
20
+ [1, 2],
21
+ [2, 3],
22
+ ],
23
+};
24
+
25
+```
26
+
27
+## Code
28
+
29
+```javascript
30
+import { c as _c } from "react/compiler-runtime";
31
+function bar(a, b) {
32
+ const $ = _c(3);
33
+ let y;
34
+ if ($[0] !== a || $[1] !== b) {
35
+ const x = [a, b];
36
+ y = {};
37
+ let t = {};
38
+
39
+ y = x[0][1];
40
+ t = x[1][0];
41
+ $[0] = a;
42
+ $[1] = b;
43
+ $[2] = y;
44
+ } else {
45
+ y = $[2];
46
+ }
47
+ return y;
48
+}
49
+
50
+export const FIXTURE_ENTRYPOINT = {
51
+ fn: bar,
52
+ params: [
53
+ [1, 2],
54
+ [2, 3],
55
+ ],
56
+};
57
+
58
+```
59
+
60
+### Eval output
61
+(kind: ok) 2
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-3-iife.js
new
+19
@@ -0,0 +1,19 @@
1
+function bar(a, b) {
2
+ let x = [a, b];
3
+ let y = {};
4
+ let t = {};
5
+ (function () {
6
+ y = x[0][1];
7
+ t = x[1][0];
8
+ })();
9
+
10
+ return y;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: bar,
15
+ params: [
16
+ [1, 2],
17
+ [2, 3],
18
+ ],
19
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-4-iife.expect.md
new
+52
@@ -0,0 +1,52 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function bar(a) {
6
+ let x = [a];
7
+ let y = {};
8
+ (function () {
9
+ y = x[0].a[1];
10
+ })();
11
+
12
+ return y;
13
+}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: bar,
17
+ params: [{a: ['val1', 'val2']}],
18
+ isComponent: false,
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+function bar(a) {
28
+ const $ = _c(2);
29
+ let y;
30
+ if ($[0] !== a) {
31
+ const x = [a];
32
+ y = {};
33
+
34
+ y = x[0].a[1];
35
+ $[0] = a;
36
+ $[1] = y;
37
+ } else {
38
+ y = $[1];
39
+ }
40
+ return y;
41
+}
42
+
43
+export const FIXTURE_ENTRYPOINT = {
44
+ fn: bar,
45
+ params: [{ a: ["val1", "val2"] }],
46
+ isComponent: false,
47
+};
48
+
49
+```
50
+
51
+### Eval output
52
+(kind: ok) "val2"
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-4-iife.js
new
+15
@@ -0,0 +1,15 @@
1
+function bar(a) {
2
+ let x = [a];
3
+ let y = {};
4
+ (function () {
5
+ y = x[0].a[1];
6
+ })();
7
+
8
+ return y;
9
+}
10
+
11
+export const FIXTURE_ENTRYPOINT = {
12
+ fn: bar,
13
+ params: [{a: ['val1', 'val2']}],
14
+ isComponent: false,
15
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-iife.expect.md
new
+50
@@ -0,0 +1,50 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function bar(a) {
6
+ let x = [a];
7
+ let y = {};
8
+ (function () {
9
+ y = x[0];
10
+ })();
11
+
12
+ return y;
13
+}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: bar,
17
+ params: ['TodoAdd'],
18
+};
19
+
20
+```
21
+
22
+## Code
23
+
24
+```javascript
25
+import { c as _c } from "react/compiler-runtime";
26
+function bar(a) {
27
+ const $ = _c(2);
28
+ let y;
29
+ if ($[0] !== a) {
30
+ const x = [a];
31
+ y = {};
32
+
33
+ y = x[0];
34
+ $[0] = a;
35
+ $[1] = y;
36
+ } else {
37
+ y = $[1];
38
+ }
39
+ return y;
40
+}
41
+
42
+export const FIXTURE_ENTRYPOINT = {
43
+ fn: bar,
44
+ params: ["TodoAdd"],
45
+};
46
+
47
+```
48
+
49
+### Eval output
50
+(kind: ok) "TodoAdd"
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/capturing-function-alias-computed-load-iife.js
new
+14
@@ -0,0 +1,14 @@
1
+function bar(a) {
2
+ let x = [a];
3
+ let y = {};
4
+ (function () {
5
+ y = x[0];
6
+ })();
7
+
8
+ return y;
9
+}
10
+
11
+export const FIXTURE_ENTRYPOINT = {
12
+ fn: bar,
13
+ params: ['TodoAdd'],
14
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-impure-functions-in-render.expect.md
new
+33
@@ -0,0 +1,33 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @validateNoImpureFunctionsInRender
6
+
7
+function Component() {
8
+ const date = Date.now();
9
+ const now = performance.now();
10
+ const rand = Math.random();
11
+ return <Foo date={date} now={now} rand={rand} />;
12
+}
13
+
14
+```
15
+
16
+
17
+## Error
18
+
19
+```
20
+ 2 |
21
+ 3 | function Component() {
22
+> 4 | const date = Date.now();
23
+ | ^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
24
+
25
+InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `performance.now` is an impure function whose results may change on every call (5:5)
26
+
27
+InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Math.random` is an impure function whose results may change on every call (6:6)
28
+ 5 | const now = performance.now();
29
+ 6 | const rand = Math.random();
30
+ 7 | return <Foo date={date} now={now} rand={rand} />;
31
+```
32
+
33
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-impure-functions-in-render.js
new
+8
@@ -0,0 +1,8 @@
1
+// @validateNoImpureFunctionsInRender
2
+
3
+function Component() {
4
+ const date = Date.now();
5
+ const now = performance.now();
6
+ const rand = Math.random();
7
+ return <Foo date={date} now={now} rand={rand} />;
8
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.mutate-hook-argument.expect.md
new
+24
@@ -0,0 +1,24 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function useHook(a, b) {
6
+ b.test = 1;
7
+ a.test = 2;
8
+}
9
+
10
+```
11
+
12
+
13
+## Error
14
+
15
+```
16
+ 1 | function useHook(a, b) {
17
+> 2 | b.test = 1;
18
+ | ^ InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (2:2)
19
+ 3 | a.test = 2;
20
+ 4 | }
21
+ 5 |
22
+```
23
+
24
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.mutate-hook-argument.js
new
+4
@@ -0,0 +1,4 @@
1
+function useHook(a, b) {
2
+ b.test = 1;
3
+ a.test = 2;
4
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.not-useEffect-external-mutate.expect.md
new
+29
@@ -0,0 +1,29 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+let x = {a: 42};
6
+
7
+function Component(props) {
8
+ foo(() => {
9
+ x.a = 10;
10
+ x.a = 20;
11
+ });
12
+}
13
+
14
+```
15
+
16
+
17
+## Error
18
+
19
+```
20
+ 3 | function Component(props) {
21
+ 4 | foo(() => {
22
+> 5 | x.a = 10;
23
+ | ^ InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (5:5)
24
+ 6 | x.a = 20;
25
+ 7 | });
26
+ 8 | }
27
+```
28
+
29
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.not-useEffect-external-mutate.js
new
+8
@@ -0,0 +1,8 @@
1
+let x = {a: 42};
2
+
3
+function Component(props) {
4
+ foo(() => {
5
+ x.a = 10;
6
+ x.a = 20;
7
+ });
8
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.reassignment-to-global-indirect.expect.md
new
+29
@@ -0,0 +1,29 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ const foo = () => {
7
+ // Cannot assign to globals
8
+ someUnknownGlobal = true;
9
+ moduleLocal = true;
10
+ };
11
+ foo();
12
+}
13
+
14
+```
15
+
16
+
17
+## Error
18
+
19
+```
20
+ 2 | const foo = () => {
21
+ 3 | // Cannot assign to globals
22
+> 4 | someUnknownGlobal = true;
23
+ | ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
24
+ 5 | moduleLocal = true;
25
+ 6 | };
26
+ 7 | foo();
27
+```
28
+
29
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.reassignment-to-global-indirect.js
new
+8
@@ -0,0 +1,8 @@
1
+function Component() {
2
+ const foo = () => {
3
+ // Cannot assign to globals
4
+ someUnknownGlobal = true;
5
+ moduleLocal = true;
6
+ };
7
+ foo();
8
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.reassignment-to-global.expect.md
new
+26
@@ -0,0 +1,26 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ // Cannot assign to globals
7
+ someUnknownGlobal = true;
8
+ moduleLocal = true;
9
+}
10
+
11
+```
12
+
13
+
14
+## Error
15
+
16
+```
17
+ 1 | function Component() {
18
+ 2 | // Cannot assign to globals
19
+> 3 | someUnknownGlobal = true;
20
+ | ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
21
+ 4 | moduleLocal = true;
22
+ 5 | }
23
+ 6 |
24
+```
25
+
26
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.reassignment-to-global.js
new
+5
@@ -0,0 +1,5 @@
1
+function Component() {
2
+ // Cannot assign to globals
3
+ someUnknownGlobal = true;
4
+ moduleLocal = true;
5
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.todo-repro-named-function-with-shadowed-local-same-name.expect.md
new
+30
@@ -0,0 +1,30 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ function hasErrors() {
7
+ let hasErrors = false;
8
+ if (props.items == null) {
9
+ hasErrors = true;
10
+ }
11
+ return hasErrors;
12
+ }
13
+ return hasErrors();
14
+}
15
+
16
+```
17
+
18
+
19
+## Error
20
+
21
+```
22
+ 7 | return hasErrors;
23
+ 8 | }
24
+> 9 | return hasErrors();
25
+ | ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$15 (9:9)
26
+ 10 | }
27
+ 11 |
28
+```
29
+
30
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.todo-repro-named-function-with-shadowed-local-same-name.js
new
+10
@@ -0,0 +1,10 @@
1
+function Component(props) {
2
+ function hasErrors() {
3
+ let hasErrors = false;
4
+ if (props.items == null) {
5
+ hasErrors = true;
6
+ }
7
+ return hasErrors;
8
+ }
9
+ return hasErrors();
10
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-optional-chain.expect.md
new
+58
@@ -0,0 +1,58 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
6
+import {useEffect} from 'react';
7
+import {print} from 'shared-runtime';
8
+
9
+function Component({foo}) {
10
+ const arr = [];
11
+ // Taking either arr[0].value or arr as a dependency is reasonable
12
+ // as long as developers know what to expect.
13
+ useEffect(() => print(arr[0]?.value));
14
+ arr.push({value: foo});
15
+ return arr;
16
+}
17
+
18
+export const FIXTURE_ENTRYPOINT = {
19
+ fn: Component,
20
+ params: [{foo: 1}],
21
+};
22
+
23
+```
24
+
25
+## Code
26
+
27
+```javascript
28
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
29
+import { useEffect } from "react";
30
+import { print } from "shared-runtime";
31
+
32
+function Component(t0) {
33
+ const { foo } = t0;
34
+ const arr = [];
35
+
36
+ useEffect(() => print(arr[0]?.value), [arr[0]?.value]);
37
+ arr.push({ value: foo });
38
+ return arr;
39
+}
40
+
41
+export const FIXTURE_ENTRYPOINT = {
42
+ fn: Component,
43
+ params: [{ foo: 1 }],
44
+};
45
+
46
+```
47
+
48
+## Logs
49
+
50
+```
51
+{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":10,"column":2,"index":345},"end":{"line":10,"column":5,"index":348},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
52
+{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":304},"end":{"line":9,"column":39,"index":341},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":326},"end":{"line":9,"column":27,"index":329},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
53
+{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
54
+```
55
+
56
+### Eval output
57
+(kind: ok) [{"value":1}]
58
+logs: [1]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-optional-chain.js
new
+17
@@ -0,0 +1,17 @@
1
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
2
+import {useEffect} from 'react';
3
+import {print} from 'shared-runtime';
4
+
5
+function Component({foo}) {
6
+ const arr = [];
7
+ // Taking either arr[0].value or arr as a dependency is reasonable
8
+ // as long as developers know what to expect.
9
+ useEffect(() => print(arr[0]?.value));
10
+ arr.push({value: foo});
11
+ return arr;
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: Component,
16
+ params: [{foo: 1}],
17
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-ref-access.expect.md
new
+57
@@ -0,0 +1,57 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
6
+
7
+import {useEffect, useRef} from 'react';
8
+import {print} from 'shared-runtime';
9
+
10
+function Component({arrRef}) {
11
+ // Avoid taking arr.current as a dependency
12
+ useEffect(() => print(arrRef.current));
13
+ arrRef.current.val = 2;
14
+ return arrRef;
15
+}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: Component,
19
+ params: [{arrRef: {current: {val: 'initial ref value'}}}],
20
+};
21
+
22
+```
23
+
24
+## Code
25
+
26
+```javascript
27
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
28
+
29
+import { useEffect, useRef } from "react";
30
+import { print } from "shared-runtime";
31
+
32
+function Component(t0) {
33
+ const { arrRef } = t0;
34
+
35
+ useEffect(() => print(arrRef.current), [arrRef]);
36
+ arrRef.current.val = 2;
37
+ return arrRef;
38
+}
39
+
40
+export const FIXTURE_ENTRYPOINT = {
41
+ fn: Component,
42
+ params: [{ arrRef: { current: { val: "initial ref value" } } }],
43
+};
44
+
45
+```
46
+
47
+## Logs
48
+
49
+```
50
+{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"loc":{"start":{"line":9,"column":2,"index":269},"end":{"line":9,"column":16,"index":283},"filename":"mutate-after-useeffect-ref-access.ts"},"suggestions":null,"severity":"InvalidReact"}}
51
+{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":227},"end":{"line":8,"column":40,"index":265},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":249},"end":{"line":8,"column":30,"index":255},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
52
+{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
53
+```
54
+
55
+### Eval output
56
+(kind: ok) {"current":{"val":2}}
57
+logs: [{ val: 2 }]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect-ref-access.js
new
+16
@@ -0,0 +1,16 @@
1
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
2
+
3
+import {useEffect, useRef} from 'react';
4
+import {print} from 'shared-runtime';
5
+
6
+function Component({arrRef}) {
7
+ // Avoid taking arr.current as a dependency
8
+ useEffect(() => print(arrRef.current));
9
+ arrRef.current.val = 2;
10
+ return arrRef;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: Component,
15
+ params: [{arrRef: {current: {val: 'initial ref value'}}}],
16
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect.expect.md
new
+56
@@ -0,0 +1,56 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
6
+import {useEffect} from 'react';
7
+
8
+function Component({foo}) {
9
+ const arr = [];
10
+ useEffect(() => {
11
+ arr.push(foo);
12
+ });
13
+ arr.push(2);
14
+ return arr;
15
+}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: Component,
19
+ params: [{foo: 1}],
20
+};
21
+
22
+```
23
+
24
+## Code
25
+
26
+```javascript
27
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
28
+import { useEffect } from "react";
29
+
30
+function Component(t0) {
31
+ const { foo } = t0;
32
+ const arr = [];
33
+ useEffect(() => {
34
+ arr.push(foo);
35
+ }, [arr, foo]);
36
+ arr.push(2);
37
+ return arr;
38
+}
39
+
40
+export const FIXTURE_ENTRYPOINT = {
41
+ fn: Component,
42
+ params: [{ foo: 1 }],
43
+};
44
+
45
+```
46
+
47
+## Logs
48
+
49
+```
50
+{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":9,"column":2,"index":194},"end":{"line":9,"column":5,"index":197},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
51
+{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":149},"end":{"line":8,"column":4,"index":190},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":180},"end":{"line":7,"column":16,"index":183},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
52
+{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
53
+```
54
+
55
+### Eval output
56
+(kind: ok) [2]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/mutate-after-useeffect.js
new
+16
@@ -0,0 +1,16 @@
1
+// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
2
+import {useEffect} from 'react';
3
+
4
+function Component({foo}) {
5
+ const arr = [];
6
+ useEffect(() => {
7
+ arr.push(foo);
8
+ });
9
+ arr.push(2);
10
+ return arr;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: Component,
15
+ params: [{foo: 1}],
16
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/object-expression-computed-key-object-mutated-later.expect.md
new
+69
@@ -0,0 +1,69 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {identity, mutate} from 'shared-runtime';
6
+
7
+function Component(props) {
8
+ const key = {};
9
+ const context = {
10
+ [key]: identity([props.value]),
11
+ };
12
+ mutate(key);
13
+ return context;
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: Component,
18
+ params: [{value: 42}],
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+import { identity, mutate } from "shared-runtime";
28
+
29
+function Component(props) {
30
+ const $ = _c(5);
31
+ let t0;
32
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
33
+ t0 = {};
34
+ $[0] = t0;
35
+ } else {
36
+ t0 = $[0];
37
+ }
38
+ const key = t0;
39
+ let t1;
40
+ if ($[1] !== props.value) {
41
+ t1 = identity([props.value]);
42
+ $[1] = props.value;
43
+ $[2] = t1;
44
+ } else {
45
+ t1 = $[2];
46
+ }
47
+ let t2;
48
+ if ($[3] !== t1) {
49
+ t2 = { [key]: t1 };
50
+ $[3] = t1;
51
+ $[4] = t2;
52
+ } else {
53
+ t2 = $[4];
54
+ }
55
+ const context = t2;
56
+
57
+ mutate(key);
58
+ return context;
59
+}
60
+
61
+export const FIXTURE_ENTRYPOINT = {
62
+ fn: Component,
63
+ params: [{ value: 42 }],
64
+};
65
+
66
+```
67
+
68
+### Eval output
69
+(kind: ok) {"[object Object]":[42]}
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/object-expression-computed-key-object-mutated-later.js
new
+15
@@ -0,0 +1,15 @@
1
+import {identity, mutate} from 'shared-runtime';
2
+
3
+function Component(props) {
4
+ const key = {};
5
+ const context = {
6
+ [key]: identity([props.value]),
7
+ };
8
+ mutate(key);
9
+ return context;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: Component,
14
+ params: [{value: 42}],
15
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/object-expression-computed-member.expect.md
new
+53
@@ -0,0 +1,53 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
6
+
7
+function Component(props) {
8
+ const key = {a: 'key'};
9
+ const context = {
10
+ [key.a]: identity([props.value]),
11
+ };
12
+ mutate(key);
13
+ return context;
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: Component,
18
+ params: [{value: 42}],
19
+};
20
+
21
+```
22
+
23
+## Code
24
+
25
+```javascript
26
+import { c as _c } from "react/compiler-runtime";
27
+import { identity, mutate, mutateAndReturn } from "shared-runtime";
28
+
29
+function Component(props) {
30
+ const $ = _c(2);
31
+ let context;
32
+ if ($[0] !== props.value) {
33
+ const key = { a: "key" };
34
+ context = { [key.a]: identity([props.value]) };
35
+
36
+ mutate(key);
37
+ $[0] = props.value;
38
+ $[1] = context;
39
+ } else {
40
+ context = $[1];
41
+ }
42
+ return context;
43
+}
44
+
45
+export const FIXTURE_ENTRYPOINT = {
46
+ fn: Component,
47
+ params: [{ value: 42 }],
48
+};
49
+
50
+```
51
+
52
+### Eval output
53
+(kind: ok) {"key":[42]}
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/object-expression-computed-member.js
new
+15
@@ -0,0 +1,15 @@
1
+import {identity, mutate, mutateAndReturn} from 'shared-runtime';
2
+
3
+function Component(props) {
4
+ const key = {a: 'key'};
5
+ const context = {
6
+ [key.a]: identity([props.value]),
7
+ };
8
+ mutate(key);
9
+ return context;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: Component,
14
+ params: [{value: 42}],
15
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/reactive-setState.expect.md
new
+60
@@ -0,0 +1,60 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies
6
+import {useEffect, useState} from 'react';
7
+import {print} from 'shared-runtime';
8
+
9
+/*
10
+ * setState types are not enough to determine to omit from deps. Must also take reactivity into account.
11
+ */
12
+function ReactiveRefInEffect(props) {
13
+ const [_state1, setState1] = useRef('initial value');
14
+ const [_state2, setState2] = useRef('initial value');
15
+ let setState;
16
+ if (props.foo) {
17
+ setState = setState1;
18
+ } else {
19
+ setState = setState2;
20
+ }
21
+ useEffect(() => print(setState));
22
+}
23
+
24
+```
25
+
26
+## Code
27
+
28
+```javascript
29
+import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
30
+import { useEffect, useState } from "react";
31
+import { print } from "shared-runtime";
32
+
33
+/*
34
+ * setState types are not enough to determine to omit from deps. Must also take reactivity into account.
35
+ */
36
+function ReactiveRefInEffect(props) {
37
+ const $ = _c(2);
38
+ const [, setState1] = useRef("initial value");
39
+ const [, setState2] = useRef("initial value");
40
+ let setState;
41
+ if (props.foo) {
42
+ setState = setState1;
43
+ } else {
44
+ setState = setState2;
45
+ }
46
+ let t0;
47
+ if ($[0] !== setState) {
48
+ t0 = () => print(setState);
49
+ $[0] = setState;
50
+ $[1] = t0;
51
+ } else {
52
+ t0 = $[1];
53
+ }
54
+ useEffect(t0, [setState]);
55
+}
56
+
57
+```
58
+
59
+### Eval output
60
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/reactive-setState.js
new
+18
@@ -0,0 +1,18 @@
1
+// @inferEffectDependencies
2
+import {useEffect, useState} from 'react';
3
+import {print} from 'shared-runtime';
4
+
5
+/*
6
+ * setState types are not enough to determine to omit from deps. Must also take reactivity into account.
7
+ */
8
+function ReactiveRefInEffect(props) {
9
+ const [_state1, setState1] = useRef('initial value');
10
+ const [_state2, setState2] = useRef('initial value');
11
+ let setState;
12
+ if (props.foo) {
13
+ setState = setState1;
14
+ } else {
15
+ setState = setState2;
16
+ }
17
+ useEffect(() => print(setState));
18
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.expect.md
new
+64
@@ -0,0 +1,64 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
6
+import {print} from 'shared-runtime';
7
+import useEffectWrapper from 'useEffectWrapper';
8
+
9
+function Foo({propVal}) {
10
+ const arr = [propVal];
11
+ useEffectWrapper(() => print(arr));
12
+
13
+ const arr2 = [];
14
+ useEffectWrapper(() => arr2.push(propVal));
15
+ arr2.push(2);
16
+ return {arr, arr2};
17
+}
18
+
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: Foo,
21
+ params: [{propVal: 1}],
22
+ sequentialRenders: [{propVal: 1}, {propVal: 2}],
23
+};
24
+
25
+```
26
+
27
+## Code
28
+
29
+```javascript
30
+// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
31
+import { print } from "shared-runtime";
32
+import useEffectWrapper from "useEffectWrapper";
33
+
34
+function Foo({ propVal }) {
35
+ const arr = [propVal];
36
+ useEffectWrapper(() => print(arr));
37
+
38
+ const arr2 = [];
39
+ useEffectWrapper(() => arr2.push(propVal));
40
+ arr2.push(2);
41
+ return { arr, arr2 };
42
+}
43
+
44
+export const FIXTURE_ENTRYPOINT = {
45
+ fn: Foo,
46
+ params: [{ propVal: 1 }],
47
+ sequentialRenders: [{ propVal: 1 }, { propVal: 2 }],
48
+};
49
+
50
+```
51
+
52
+## Logs
53
+
54
+```
55
+{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":11,"column":2,"index":320},"end":{"line":11,"column":6,"index":324},"filename":"retry-no-emit.ts","identifierName":"arr2"},"suggestions":null,"severity":"InvalidReact"}}
56
+{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":7,"column":2,"index":216},"end":{"line":7,"column":36,"index":250},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":7,"column":31,"index":245},"end":{"line":7,"column":34,"index":248},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
57
+{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":10,"column":2,"index":274},"end":{"line":10,"column":44,"index":316},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":35,"index":307},"end":{"line":10,"column":42,"index":314},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
58
+{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
59
+```
60
+
61
+### Eval output
62
+(kind: ok) {"arr":[1],"arr2":[2]}
63
+{"arr":[2],"arr2":[2]}
64
+logs: [[ 1 ],[ 2 ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/retry-no-emit.js
new
+19
@@ -0,0 +1,19 @@
1
+// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
2
+import {print} from 'shared-runtime';
3
+import useEffectWrapper from 'useEffectWrapper';
4
+
5
+function Foo({propVal}) {
6
+ const arr = [propVal];
7
+ useEffectWrapper(() => print(arr));
8
+
9
+ const arr2 = [];
10
+ useEffectWrapper(() => arr2.push(propVal));
11
+ arr2.push(2);
12
+ return {arr, arr2};
13
+}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: Foo,
17
+ params: [{propVal: 1}],
18
+ sequentialRenders: [{propVal: 1}, {propVal: 2}],
19
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/shared-hook-calls.expect.md
new
+80
@@ -0,0 +1,80 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @enableFire
6
+import {fire} from 'react';
7
+
8
+function Component({bar, baz}) {
9
+ const foo = () => {
10
+ console.log(bar);
11
+ };
12
+ useEffect(() => {
13
+ fire(foo(bar));
14
+ fire(baz(bar));
15
+ });
16
+
17
+ useEffect(() => {
18
+ fire(foo(bar));
19
+ });
20
+
21
+ return null;
22
+}
23
+
24
+```
25
+
26
+## Code
27
+
28
+```javascript
29
+import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire
30
+import { fire } from "react";
31
+
32
+function Component(t0) {
33
+ const $ = _c(9);
34
+ const { bar, baz } = t0;
35
+ let t1;
36
+ if ($[0] !== bar) {
37
+ t1 = () => {
38
+ console.log(bar);
39
+ };
40
+ $[0] = bar;
41
+ $[1] = t1;
42
+ } else {
43
+ t1 = $[1];
44
+ }
45
+ const foo = t1;
46
+ const t2 = useFire(foo);
47
+ const t3 = useFire(baz);
48
+ let t4;
49
+ if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) {
50
+ t4 = () => {
51
+ t2(bar);
52
+ t3(bar);
53
+ };
54
+ $[2] = bar;
55
+ $[3] = t2;
56
+ $[4] = t3;
57
+ $[5] = t4;
58
+ } else {
59
+ t4 = $[5];
60
+ }
61
+ useEffect(t4);
62
+ let t5;
63
+ if ($[6] !== bar || $[7] !== t2) {
64
+ t5 = () => {
65
+ t2(bar);
66
+ };
67
+ $[6] = bar;
68
+ $[7] = t2;
69
+ $[8] = t5;
70
+ } else {
71
+ t5 = $[8];
72
+ }
73
+ useEffect(t5);
74
+ return null;
75
+}
76
+
77
+```
78
+
79
+### Eval output
80
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/shared-hook-calls.js
new
+18
@@ -0,0 +1,18 @@
1
+// @enableFire
2
+import {fire} from 'react';
3
+
4
+function Component({bar, baz}) {
5
+ const foo = () => {
6
+ console.log(bar);
7
+ };
8
+ useEffect(() => {
9
+ fire(foo(bar));
10
+ fire(baz(bar));
11
+ });
12
+
13
+ useEffect(() => {
14
+ fire(foo(bar));
15
+ });
16
+
17
+ return null;
18
+}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useCallback-reordering-deplist-controlflow.expect.md
new
+94
@@ -0,0 +1,94 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {useCallback} from 'react';
6
+import {Stringify} from 'shared-runtime';
7
+
8
+function Foo({arr1, arr2, foo}) {
9
+ const x = [arr1];
10
+
11
+ let y = [];
12
+
13
+ const getVal1 = useCallback(() => {
14
+ return {x: 2};
15
+ }, []);
16
+
17
+ const getVal2 = useCallback(() => {
18
+ return [y];
19
+ }, [foo ? (y = x.concat(arr2)) : y]);
20
+
21
+ return <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
22
+}
23
+
24
+export const FIXTURE_ENTRYPOINT = {
25
+ fn: Foo,
26
+ params: [{arr1: [1, 2], arr2: [3, 4], foo: true}],
27
+ sequentialRenders: [
28
+ {arr1: [1, 2], arr2: [3, 4], foo: true},
29
+ {arr1: [1, 2], arr2: [3, 4], foo: false},
30
+ ],
31
+};
32
+
33
+```
34
+
35
+## Code
36
+
37
+```javascript
38
+import { c as _c } from "react/compiler-runtime";
39
+import { useCallback } from "react";
40
+import { Stringify } from "shared-runtime";
41
+
42
+function Foo(t0) {
43
+ const $ = _c(8);
44
+ const { arr1, arr2, foo } = t0;
45
+ let getVal1;
46
+ let t1;
47
+ if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) {
48
+ const x = [arr1];
49
+
50
+ let y = [];
51
+
52
+ getVal1 = _temp;
53
+
54
+ t1 = () => [y];
55
+ foo ? (y = x.concat(arr2)) : y;
56
+ $[0] = arr1;
57
+ $[1] = arr2;
58
+ $[2] = foo;
59
+ $[3] = getVal1;
60
+ $[4] = t1;
61
+ } else {
62
+ getVal1 = $[3];
63
+ t1 = $[4];
64
+ }
65
+ const getVal2 = t1;
66
+ let t2;
67
+ if ($[5] !== getVal1 || $[6] !== getVal2) {
68
+ t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
69
+ $[5] = getVal1;
70
+ $[6] = getVal2;
71
+ $[7] = t2;
72
+ } else {
73
+ t2 = $[7];
74
+ }
75
+ return t2;
76
+}
77
+function _temp() {
78
+ return { x: 2 };
79
+}
80
+
81
+export const FIXTURE_ENTRYPOINT = {
82
+ fn: Foo,
83
+ params: [{ arr1: [1, 2], arr2: [3, 4], foo: true }],
84
+ sequentialRenders: [
85
+ { arr1: [1, 2], arr2: [3, 4], foo: true },
86
+ { arr1: [1, 2], arr2: [3, 4], foo: false },
87
+ ],
88
+};
89
+
90
+```
91
+
92
+### Eval output
93
+(kind: ok) <div>{"val1":{"kind":"Function","result":{"x":2}},"val2":{"kind":"Function","result":[[[1,2],3,4]]},"shouldInvokeFns":true}</div>
94
+<div>{"val1":{"kind":"Function","result":{"x":2}},"val2":{"kind":"Function","result":[[]]},"shouldInvokeFns":true}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useCallback-reordering-deplist-controlflow.tsx
new
+27
@@ -0,0 +1,27 @@
1
+import {useCallback} from 'react';
2
+import {Stringify} from 'shared-runtime';
3
+
4
+function Foo({arr1, arr2, foo}) {
5
+ const x = [arr1];
6
+
7
+ let y = [];
8
+
9
+ const getVal1 = useCallback(() => {
10
+ return {x: 2};
11
+ }, []);
12
+
13
+ const getVal2 = useCallback(() => {
14
+ return [y];
15
+ }, [foo ? (y = x.concat(arr2)) : y]);
16
+
17
+ return <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
18
+}
19
+
20
+export const FIXTURE_ENTRYPOINT = {
21
+ fn: Foo,
22
+ params: [{arr1: [1, 2], arr2: [3, 4], foo: true}],
23
+ sequentialRenders: [
24
+ {arr1: [1, 2], arr2: [3, 4], foo: true},
25
+ {arr1: [1, 2], arr2: [3, 4], foo: false},
26
+ ],
27
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useCallback-reordering-depslist-assignment.expect.md
new
+77
@@ -0,0 +1,77 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {useCallback} from 'react';
6
+import {Stringify} from 'shared-runtime';
7
+
8
+// We currently produce invalid output (incorrect scoping for `y` declaration)
9
+function useFoo(arr1, arr2) {
10
+ const x = [arr1];
11
+
12
+ let y;
13
+ const getVal = useCallback(() => {
14
+ return {y};
15
+ }, [((y = x.concat(arr2)), y)]);
16
+
17
+ return <Stringify getVal={getVal} shouldInvokeFns={true} />;
18
+}
19
+
20
+export const FIXTURE_ENTRYPOINT = {
21
+ fn: useFoo,
22
+ params: [
23
+ [1, 2],
24
+ [3, 4],
25
+ ],
26
+};
27
+
28
+```
29
+
30
+## Code
31
+
32
+```javascript
33
+import { c as _c } from "react/compiler-runtime";
34
+import { useCallback } from "react";
35
+import { Stringify } from "shared-runtime";
36
+
37
+// We currently produce invalid output (incorrect scoping for `y` declaration)
38
+function useFoo(arr1, arr2) {
39
+ const $ = _c(5);
40
+ let t0;
41
+ if ($[0] !== arr1 || $[1] !== arr2) {
42
+ const x = [arr1];
43
+
44
+ let y;
45
+ t0 = () => ({ y });
46
+
47
+ (y = x.concat(arr2)), y;
48
+ $[0] = arr1;
49
+ $[1] = arr2;
50
+ $[2] = t0;
51
+ } else {
52
+ t0 = $[2];
53
+ }
54
+ const getVal = t0;
55
+ let t1;
56
+ if ($[3] !== getVal) {
57
+ t1 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
58
+ $[3] = getVal;
59
+ $[4] = t1;
60
+ } else {
61
+ t1 = $[4];
62
+ }
63
+ return t1;
64
+}
65
+
66
+export const FIXTURE_ENTRYPOINT = {
67
+ fn: useFoo,
68
+ params: [
69
+ [1, 2],
70
+ [3, 4],
71
+ ],
72
+};
73
+
74
+```
75
+
76
+### Eval output
77
+(kind: ok) <div>{"getVal":{"kind":"Function","result":{"y":[[1,2],3,4]}},"shouldInvokeFns":true}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useCallback-reordering-depslist-assignment.tsx
new
+22
@@ -0,0 +1,22 @@
1
+import {useCallback} from 'react';
2
+import {Stringify} from 'shared-runtime';
3
+
4
+// We currently produce invalid output (incorrect scoping for `y` declaration)
5
+function useFoo(arr1, arr2) {
6
+ const x = [arr1];
7
+
8
+ let y;
9
+ const getVal = useCallback(() => {
10
+ return {y};
11
+ }, [((y = x.concat(arr2)), y)]);
12
+
13
+ return <Stringify getVal={getVal} shouldInvokeFns={true} />;
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: useFoo,
18
+ params: [
19
+ [1, 2],
20
+ [3, 4],
21
+ ],
22
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useMemo-reordering-depslist-assignment.expect.md
new
+69
@@ -0,0 +1,69 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {useMemo} from 'react';
6
+
7
+function useFoo(arr1, arr2) {
8
+ const x = [arr1];
9
+
10
+ let y;
11
+ return useMemo(() => {
12
+ return {y};
13
+ }, [((y = x.concat(arr2)), y)]);
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: useFoo,
18
+ params: [
19
+ [1, 2],
20
+ [3, 4],
21
+ ],
22
+};
23
+
24
+```
25
+
26
+## Code
27
+
28
+```javascript
29
+import { c as _c } from "react/compiler-runtime";
30
+import { useMemo } from "react";
31
+
32
+function useFoo(arr1, arr2) {
33
+ const $ = _c(5);
34
+ let y;
35
+ if ($[0] !== arr1 || $[1] !== arr2) {
36
+ const x = [arr1];
37
+
38
+ (y = x.concat(arr2)), y;
39
+ $[0] = arr1;
40
+ $[1] = arr2;
41
+ $[2] = y;
42
+ } else {
43
+ y = $[2];
44
+ }
45
+ let t0;
46
+ let t1;
47
+ if ($[3] !== y) {
48
+ t1 = { y };
49
+ $[3] = y;
50
+ $[4] = t1;
51
+ } else {
52
+ t1 = $[4];
53
+ }
54
+ t0 = t1;
55
+ return t0;
56
+}
57
+
58
+export const FIXTURE_ENTRYPOINT = {
59
+ fn: useFoo,
60
+ params: [
61
+ [1, 2],
62
+ [3, 4],
63
+ ],
64
+};
65
+
66
+```
67
+
68
+### Eval output
69
+(kind: ok) {"y":[[1,2],3,4]}
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/useMemo-reordering-depslist-assignment.ts
new
+18
@@ -0,0 +1,18 @@
1
+import {useMemo} from 'react';
2
+
3
+function useFoo(arr1, arr2) {
4
+ const x = [arr1];
5
+
6
+ let y;
7
+ return useMemo(() => {
8
+ return {y};
9
+ }, [((y = x.concat(arr2)), y)]);
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: useFoo,
14
+ params: [
15
+ [1, 2],
16
+ [3, 4],
17
+ ],
18
+};