[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
This commit is contained in:
Joseph Savona
2025-06-18 12:58:16 -07:00
committed by GitHub
parent 66cfe048d3
commit df080d228b
46 changed files with 1912 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
## Input
```javascript
import {
Stringify,
mutate,
identity,
shallowCopy,
setPropertyByKey,
} from 'shared-runtime';
/**
* This fixture is similar to `bug-aliased-capture-aliased-mutate` and
* `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
* dependency extraction.
*
* NOTE: this fixture is currently valid, but will break with optimizations:
* - Scope and mutable-range based reordering may move the array creation
* *after* the `mutate(aliasedObj)` call. This is invalid if mutate
* reassigns inner properties.
* - RecycleInto or other deeper-equality optimizations may produce invalid
* output -- it may compare the array's contents / dependencies too early.
* - Runtime validation for immutable values will break if `mutate` does
* interior mutation of the value captured into the array.
*
* Before scope block creation, HIR looks like this:
* //
* // $1 is unscoped as obj's mutable range will be
* // extended in a later pass
* //
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* //
* // $3 gets assigned a scope as Array is an allocating
* // instruction, but this does *not* get extended or
* // merged into the later mutation site.
* // (explained in `bug-aliased-capture-aliased-mutate`)
* //
* $3@1 = Array[$2]
* ...
* $10@0 = LoadLocal shallowCopy@0[0, 12]
* $11 = LoadGlobal mutate
* $12 = $11($10@0[0, 12])
*
* When filling in scope dependencies, we find that it's incorrect to depend on
* PropertyLoads from obj as it hasn't completed its mutable range. Following
* the immutable / mutable-new typing system, we check the identity of obj to
* detect whether it was newly created (and thus mutable) in this render pass.
*
* HIR with scopes looks like this.
* bb0:
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
* bb1:
* $3@1 = Array[$2]
* goto bb2
* bb2:
* ...
*
* This is surprising as deps now is entirely decoupled from temporaries used
* by the block itself. scope @1's instructions now reference a value (1)
* produced outside its scope range and (2) not represented in its dependencies
*
* The right thing to do is to ensure that all Loads from a value get assigned
* the value's reactive scope. This also requires track mutating and aliasing
* separately from scope range. In this example, that would correctly merge
* the scopes of $3 with obj.
* Runtime validation and optimizations such as ReactiveGraph-based reordering
* require this as well.
*
* A tempting fix is to instead extend $3's ReactiveScope range up to include
* $2 (the PropertyLoad). This fixes dependency deduping but not reordering
* and mutability.
*/
function Component({prop}) {
let obj = shallowCopy(prop);
const aliasedObj = identity(obj);
// [obj.id] currently is assigned its own reactive scope
const id = [obj.id];
// Writing to the alias may reassign to previously captured references.
// The compiler currently produces valid output, but this breaks with
// reordering, recycleInto, and other potential optimizations.
mutate(aliasedObj);
setPropertyByKey(aliasedObj, 'id', prop.id + 1);
return <Stringify id={id} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{prop: {id: 1}}],
sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import {
Stringify,
mutate,
identity,
shallowCopy,
setPropertyByKey,
} from "shared-runtime";
/**
* This fixture is similar to `bug-aliased-capture-aliased-mutate` and
* `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
* dependency extraction.
*
* NOTE: this fixture is currently valid, but will break with optimizations:
* - Scope and mutable-range based reordering may move the array creation
* *after* the `mutate(aliasedObj)` call. This is invalid if mutate
* reassigns inner properties.
* - RecycleInto or other deeper-equality optimizations may produce invalid
* output -- it may compare the array's contents / dependencies too early.
* - Runtime validation for immutable values will break if `mutate` does
* interior mutation of the value captured into the array.
*
* Before scope block creation, HIR looks like this:
* //
* // $1 is unscoped as obj's mutable range will be
* // extended in a later pass
* //
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* //
* // $3 gets assigned a scope as Array is an allocating
* // instruction, but this does *not* get extended or
* // merged into the later mutation site.
* // (explained in `bug-aliased-capture-aliased-mutate`)
* //
* $3@1 = Array[$2]
* ...
* $10@0 = LoadLocal shallowCopy@0[0, 12]
* $11 = LoadGlobal mutate
* $12 = $11($10@0[0, 12])
*
* When filling in scope dependencies, we find that it's incorrect to depend on
* PropertyLoads from obj as it hasn't completed its mutable range. Following
* the immutable / mutable-new typing system, we check the identity of obj to
* detect whether it was newly created (and thus mutable) in this render pass.
*
* HIR with scopes looks like this.
* bb0:
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
* bb1:
* $3@1 = Array[$2]
* goto bb2
* bb2:
* ...
*
* This is surprising as deps now is entirely decoupled from temporaries used
* by the block itself. scope @1's instructions now reference a value (1)
* produced outside its scope range and (2) not represented in its dependencies
*
* The right thing to do is to ensure that all Loads from a value get assigned
* the value's reactive scope. This also requires track mutating and aliasing
* separately from scope range. In this example, that would correctly merge
* the scopes of $3 with obj.
* Runtime validation and optimizations such as ReactiveGraph-based reordering
* require this as well.
*
* A tempting fix is to instead extend $3's ReactiveScope range up to include
* $2 (the PropertyLoad). This fixes dependency deduping but not reordering
* and mutability.
*/
function Component(t0) {
const $ = _c(4);
const { prop } = t0;
let t1;
if ($[0] !== prop) {
const obj = shallowCopy(prop);
const aliasedObj = identity(obj);
let t2;
if ($[2] !== obj) {
t2 = [obj.id];
$[2] = obj;
$[3] = t2;
} else {
t2 = $[3];
}
const id = t2;
mutate(aliasedObj);
setPropertyByKey(aliasedObj, "id", prop.id + 1);
t1 = <Stringify id={id} />;
$[0] = prop;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ prop: { id: 1 } }],
sequentialRenders: [
{ prop: { id: 1 } },
{ prop: { id: 1 } },
{ prop: { id: 2 } },
],
};
```
### Eval output
(kind: ok) <div>{"id":[1]}</div>
<div>{"id":[1]}</div>
<div>{"id":[2]}</div>

View File

@@ -0,0 +1,93 @@
import {
Stringify,
mutate,
identity,
shallowCopy,
setPropertyByKey,
} from 'shared-runtime';
/**
* This fixture is similar to `bug-aliased-capture-aliased-mutate` and
* `nonmutating-capture-in-unsplittable-memo-block`, but with a focus on
* dependency extraction.
*
* NOTE: this fixture is currently valid, but will break with optimizations:
* - Scope and mutable-range based reordering may move the array creation
* *after* the `mutate(aliasedObj)` call. This is invalid if mutate
* reassigns inner properties.
* - RecycleInto or other deeper-equality optimizations may produce invalid
* output -- it may compare the array's contents / dependencies too early.
* - Runtime validation for immutable values will break if `mutate` does
* interior mutation of the value captured into the array.
*
* Before scope block creation, HIR looks like this:
* //
* // $1 is unscoped as obj's mutable range will be
* // extended in a later pass
* //
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* //
* // $3 gets assigned a scope as Array is an allocating
* // instruction, but this does *not* get extended or
* // merged into the later mutation site.
* // (explained in `bug-aliased-capture-aliased-mutate`)
* //
* $3@1 = Array[$2]
* ...
* $10@0 = LoadLocal shallowCopy@0[0, 12]
* $11 = LoadGlobal mutate
* $12 = $11($10@0[0, 12])
*
* When filling in scope dependencies, we find that it's incorrect to depend on
* PropertyLoads from obj as it hasn't completed its mutable range. Following
* the immutable / mutable-new typing system, we check the identity of obj to
* detect whether it was newly created (and thus mutable) in this render pass.
*
* HIR with scopes looks like this.
* bb0:
* $1 = LoadLocal obj@0[0:12]
* $2 = PropertyLoad $1.id
* scopeTerminal deps=[obj@0] block=bb1 fallt=bb2
* bb1:
* $3@1 = Array[$2]
* goto bb2
* bb2:
* ...
*
* This is surprising as deps now is entirely decoupled from temporaries used
* by the block itself. scope @1's instructions now reference a value (1)
* produced outside its scope range and (2) not represented in its dependencies
*
* The right thing to do is to ensure that all Loads from a value get assigned
* the value's reactive scope. This also requires track mutating and aliasing
* separately from scope range. In this example, that would correctly merge
* the scopes of $3 with obj.
* Runtime validation and optimizations such as ReactiveGraph-based reordering
* require this as well.
*
* A tempting fix is to instead extend $3's ReactiveScope range up to include
* $2 (the PropertyLoad). This fixes dependency deduping but not reordering
* and mutability.
*/
function Component({prop}) {
let obj = shallowCopy(prop);
const aliasedObj = identity(obj);
// [obj.id] currently is assigned its own reactive scope
const id = [obj.id];
// Writing to the alias may reassign to previously captured references.
// The compiler currently produces valid output, but this breaks with
// reordering, recycleInto, and other potential optimizations.
mutate(aliasedObj);
setPropertyByKey(aliasedObj, 'id', prop.id + 1);
return <Stringify id={id} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{prop: {id: 1}}],
sequentialRenders: [{prop: {id: 1}}, {prop: {id: 1}}, {prop: {id: 2}}],
};

View File

@@ -0,0 +1,133 @@
## Input
```javascript
import {Stringify} from 'shared-runtime';
/**
* Forked from array-map-simple.js
*
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
* used in a different lambda (getArrMap1).
*
* Here, we should try to determine if cb1 is actually called. In this case:
* - getArrMap1 is assumed to be called as it's passed to JSX
* - cb1 is not assumed to be called since it's only used as a call operand
*/
function useFoo({arr1, arr2}) {
const cb1 = e => arr1[0].value + e.value;
const getArrMap1 = () => arr1.map(cb1);
const cb2 = e => arr2[0].value + e.value;
const getArrMap2 = () => arr1.map(cb2);
return (
<Stringify
getArrMap1={getArrMap1}
getArrMap2={getArrMap2}
shouldInvokeFns={true}
/>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{arr1: [], arr2: []}],
sequentialRenders: [
{arr1: [], arr2: []},
{arr1: [], arr2: null},
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { Stringify } from "shared-runtime";
/**
* Forked from array-map-simple.js
*
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
* used in a different lambda (getArrMap1).
*
* Here, we should try to determine if cb1 is actually called. In this case:
* - getArrMap1 is assumed to be called as it's passed to JSX
* - cb1 is not assumed to be called since it's only used as a call operand
*/
function useFoo(t0) {
const $ = _c(13);
const { arr1, arr2 } = t0;
let t1;
if ($[0] !== arr1[0]) {
t1 = (e) => arr1[0].value + e.value;
$[0] = arr1[0];
$[1] = t1;
} else {
t1 = $[1];
}
const cb1 = t1;
let t2;
if ($[2] !== arr1 || $[3] !== cb1) {
t2 = () => arr1.map(cb1);
$[2] = arr1;
$[3] = cb1;
$[4] = t2;
} else {
t2 = $[4];
}
const getArrMap1 = t2;
let t3;
if ($[5] !== arr2) {
t3 = (e_0) => arr2[0].value + e_0.value;
$[5] = arr2;
$[6] = t3;
} else {
t3 = $[6];
}
const cb2 = t3;
let t4;
if ($[7] !== arr1 || $[8] !== cb2) {
t4 = () => arr1.map(cb2);
$[7] = arr1;
$[8] = cb2;
$[9] = t4;
} else {
t4 = $[9];
}
const getArrMap2 = t4;
let t5;
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
t5 = (
<Stringify
getArrMap1={getArrMap1}
getArrMap2={getArrMap2}
shouldInvokeFns={true}
/>
);
$[10] = getArrMap1;
$[11] = getArrMap2;
$[12] = t5;
} else {
t5 = $[12];
}
return t5;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{ arr1: [], arr2: [] }],
sequentialRenders: [
{ arr1: [], arr2: [] },
{ arr1: [], arr2: null },
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
],
};
```
### Eval output
(kind: ok) <div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
<div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
<div>{"getArrMap1":{"kind":"Function","result":[2,3]},"getArrMap2":{"kind":"Function","result":[0,1]},"shouldInvokeFns":true}</div>

View File

@@ -0,0 +1,35 @@
import {Stringify} from 'shared-runtime';
/**
* Forked from array-map-simple.js
*
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
* used in a different lambda (getArrMap1).
*
* Here, we should try to determine if cb1 is actually called. In this case:
* - getArrMap1 is assumed to be called as it's passed to JSX
* - cb1 is not assumed to be called since it's only used as a call operand
*/
function useFoo({arr1, arr2}) {
const cb1 = e => arr1[0].value + e.value;
const getArrMap1 = () => arr1.map(cb1);
const cb2 = e => arr2[0].value + e.value;
const getArrMap2 = () => arr1.map(cb2);
return (
<Stringify
getArrMap1={getArrMap1}
getArrMap2={getArrMap2}
shouldInvokeFns={true}
/>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{arr1: [], arr2: []}],
sequentialRenders: [
{arr1: [], arr2: []},
{arr1: [], arr2: null},
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
],
};

View File

@@ -0,0 +1,52 @@
## Input
```javascript
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0][1];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [['val1', 'val2']],
isComponent: false,
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
if ($[0] !== a) {
const x = [a];
y = {};
y = x[0][1];
$[0] = a;
$[1] = y;
} else {
y = $[1];
}
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [["val1", "val2"]],
isComponent: false,
};
```
### Eval output
(kind: ok) "val2"

View File

@@ -0,0 +1,15 @@
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0][1];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [['val1', 'val2']],
isComponent: false,
};

View File

@@ -0,0 +1,61 @@
## Input
```javascript
function bar(a, b) {
let x = [a, b];
let y = {};
let t = {};
(function () {
y = x[0][1];
t = x[1][0];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [
[1, 2],
[2, 3],
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a, b) {
const $ = _c(3);
let y;
if ($[0] !== a || $[1] !== b) {
const x = [a, b];
y = {};
let t = {};
y = x[0][1];
t = x[1][0];
$[0] = a;
$[1] = b;
$[2] = y;
} else {
y = $[2];
}
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [
[1, 2],
[2, 3],
],
};
```
### Eval output
(kind: ok) 2

View File

@@ -0,0 +1,19 @@
function bar(a, b) {
let x = [a, b];
let y = {};
let t = {};
(function () {
y = x[0][1];
t = x[1][0];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [
[1, 2],
[2, 3],
],
};

View File

@@ -0,0 +1,52 @@
## Input
```javascript
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0].a[1];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [{a: ['val1', 'val2']}],
isComponent: false,
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
if ($[0] !== a) {
const x = [a];
y = {};
y = x[0].a[1];
$[0] = a;
$[1] = y;
} else {
y = $[1];
}
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [{ a: ["val1", "val2"] }],
isComponent: false,
};
```
### Eval output
(kind: ok) "val2"

View File

@@ -0,0 +1,15 @@
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0].a[1];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: [{a: ['val1', 'val2']}],
isComponent: false,
};

View File

@@ -0,0 +1,50 @@
## Input
```javascript
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: ['TodoAdd'],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
if ($[0] !== a) {
const x = [a];
y = {};
y = x[0];
$[0] = a;
$[1] = y;
} else {
y = $[1];
}
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: ["TodoAdd"],
};
```
### Eval output
(kind: ok) "TodoAdd"

View File

@@ -0,0 +1,14 @@
function bar(a) {
let x = [a];
let y = {};
(function () {
y = x[0];
})();
return y;
}
export const FIXTURE_ENTRYPOINT = {
fn: bar,
params: ['TodoAdd'],
};

View File

@@ -0,0 +1,33 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
function Component() {
const date = Date.now();
const now = performance.now();
const rand = Math.random();
return <Foo date={date} now={now} rand={rand} />;
}
```
## Error
```
2 |
3 | function Component() {
> 4 | const date = Date.now();
| ^^^^^^^^ 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)
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)
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)
5 | const now = performance.now();
6 | const rand = Math.random();
7 | return <Foo date={date} now={now} rand={rand} />;
```

View File

@@ -0,0 +1,8 @@
// @validateNoImpureFunctionsInRender
function Component() {
const date = Date.now();
const now = performance.now();
const rand = Math.random();
return <Foo date={date} now={now} rand={rand} />;
}

View File

@@ -0,0 +1,24 @@
## Input
```javascript
function useHook(a, b) {
b.test = 1;
a.test = 2;
}
```
## Error
```
1 | function useHook(a, b) {
> 2 | b.test = 1;
| ^ InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (2:2)
3 | a.test = 2;
4 | }
5 |
```

View File

@@ -0,0 +1,4 @@
function useHook(a, b) {
b.test = 1;
a.test = 2;
}

View File

@@ -0,0 +1,29 @@
## Input
```javascript
let x = {a: 42};
function Component(props) {
foo(() => {
x.a = 10;
x.a = 20;
});
}
```
## Error
```
3 | function Component(props) {
4 | foo(() => {
> 5 | x.a = 10;
| ^ InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (5:5)
6 | x.a = 20;
7 | });
8 | }
```

View File

@@ -0,0 +1,8 @@
let x = {a: 42};
function Component(props) {
foo(() => {
x.a = 10;
x.a = 20;
});
}

View File

@@ -0,0 +1,29 @@
## Input
```javascript
function Component() {
const foo = () => {
// Cannot assign to globals
someUnknownGlobal = true;
moduleLocal = true;
};
foo();
}
```
## Error
```
2 | const foo = () => {
3 | // Cannot assign to globals
> 4 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ 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)
5 | moduleLocal = true;
6 | };
7 | foo();
```

View File

@@ -0,0 +1,8 @@
function Component() {
const foo = () => {
// Cannot assign to globals
someUnknownGlobal = true;
moduleLocal = true;
};
foo();
}

View File

@@ -0,0 +1,26 @@
## Input
```javascript
function Component() {
// Cannot assign to globals
someUnknownGlobal = true;
moduleLocal = true;
}
```
## Error
```
1 | function Component() {
2 | // Cannot assign to globals
> 3 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ 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)
4 | moduleLocal = true;
5 | }
6 |
```

View File

@@ -0,0 +1,5 @@
function Component() {
// Cannot assign to globals
someUnknownGlobal = true;
moduleLocal = true;
}

View File

@@ -0,0 +1,30 @@
## Input
```javascript
function Component(props) {
function hasErrors() {
let hasErrors = false;
if (props.items == null) {
hasErrors = true;
}
return hasErrors;
}
return hasErrors();
}
```
## Error
```
7 | return hasErrors;
8 | }
> 9 | return hasErrors();
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$15 (9:9)
10 | }
11 |
```

View File

@@ -0,0 +1,10 @@
function Component(props) {
function hasErrors() {
let hasErrors = false;
if (props.items == null) {
hasErrors = true;
}
return hasErrors;
}
return hasErrors();
}

View File

@@ -0,0 +1,58 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
arr.push({value: foo});
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{foo: 1}],
};
```
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect } from "react";
import { print } from "shared-runtime";
function Component(t0) {
const { foo } = t0;
const arr = [];
useEffect(() => print(arr[0]?.value), [arr[0]?.value]);
arr.push({ value: foo });
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ foo: 1 }],
};
```
## Logs
```
{"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"}}
{"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"}]}
{"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}
```
### Eval output
(kind: ok) [{"value":1}]
logs: [1]

View File

@@ -0,0 +1,17 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
import {print} from 'shared-runtime';
function Component({foo}) {
const arr = [];
// Taking either arr[0].value or arr as a dependency is reasonable
// as long as developers know what to expect.
useEffect(() => print(arr[0]?.value));
arr.push({value: foo});
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{foo: 1}],
};

View File

@@ -0,0 +1,57 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect, useRef} from 'react';
import {print} from 'shared-runtime';
function Component({arrRef}) {
// Avoid taking arr.current as a dependency
useEffect(() => print(arrRef.current));
arrRef.current.val = 2;
return arrRef;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arrRef: {current: {val: 'initial ref value'}}}],
};
```
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect, useRef } from "react";
import { print } from "shared-runtime";
function Component(t0) {
const { arrRef } = t0;
useEffect(() => print(arrRef.current), [arrRef]);
arrRef.current.val = 2;
return arrRef;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ arrRef: { current: { val: "initial ref value" } } }],
};
```
## Logs
```
{"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"}}
{"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"}]}
{"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}
```
### Eval output
(kind: ok) {"current":{"val":2}}
logs: [{ val: 2 }]

View File

@@ -0,0 +1,16 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect, useRef} from 'react';
import {print} from 'shared-runtime';
function Component({arrRef}) {
// Avoid taking arr.current as a dependency
useEffect(() => print(arrRef.current));
arrRef.current.val = 2;
return arrRef;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{arrRef: {current: {val: 'initial ref value'}}}],
};

View File

@@ -0,0 +1,56 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
function Component({foo}) {
const arr = [];
useEffect(() => {
arr.push(foo);
});
arr.push(2);
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{foo: 1}],
};
```
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import { useEffect } from "react";
function Component(t0) {
const { foo } = t0;
const arr = [];
useEffect(() => {
arr.push(foo);
}, [arr, foo]);
arr.push(2);
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ foo: 1 }],
};
```
## Logs
```
{"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"}}
{"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"}]}
{"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}
```
### Eval output
(kind: ok) [2]

View File

@@ -0,0 +1,16 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
import {useEffect} from 'react';
function Component({foo}) {
const arr = [];
useEffect(() => {
arr.push(foo);
});
arr.push(2);
return arr;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{foo: 1}],
};

View File

@@ -0,0 +1,69 @@
## Input
```javascript
import {identity, mutate} from 'shared-runtime';
function Component(props) {
const key = {};
const context = {
[key]: 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 } from "shared-runtime";
function Component(props) {
const $ = _c(5);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = {};
$[0] = t0;
} else {
t0 = $[0];
}
const key = t0;
let t1;
if ($[1] !== props.value) {
t1 = identity([props.value]);
$[1] = props.value;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[3] !== t1) {
t2 = { [key]: t1 };
$[3] = t1;
$[4] = t2;
} else {
t2 = $[4];
}
const context = t2;
mutate(key);
return context;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: 42 }],
};
```
### Eval output
(kind: ok) {"[object Object]":[42]}

View File

@@ -0,0 +1,15 @@
import {identity, mutate} from 'shared-runtime';
function Component(props) {
const key = {};
const context = {
[key]: identity([props.value]),
};
mutate(key);
return context;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{value: 42}],
};

View File

@@ -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]}

View File

@@ -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}],
};

View File

@@ -0,0 +1,60 @@
## Input
```javascript
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {print} from 'shared-runtime';
/*
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
*/
function ReactiveRefInEffect(props) {
const [_state1, setState1] = useRef('initial value');
const [_state2, setState2] = useRef('initial value');
let setState;
if (props.foo) {
setState = setState1;
} else {
setState = setState2;
}
useEffect(() => print(setState));
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useState } from "react";
import { print } from "shared-runtime";
/*
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
*/
function ReactiveRefInEffect(props) {
const $ = _c(2);
const [, setState1] = useRef("initial value");
const [, setState2] = useRef("initial value");
let setState;
if (props.foo) {
setState = setState1;
} else {
setState = setState2;
}
let t0;
if ($[0] !== setState) {
t0 = () => print(setState);
$[0] = setState;
$[1] = t0;
} else {
t0 = $[1];
}
useEffect(t0, [setState]);
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,18 @@
// @inferEffectDependencies
import {useEffect, useState} from 'react';
import {print} from 'shared-runtime';
/*
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
*/
function ReactiveRefInEffect(props) {
const [_state1, setState1] = useRef('initial value');
const [_state2, setState2] = useRef('initial value');
let setState;
if (props.foo) {
setState = setState1;
} else {
setState = setState2;
}
useEffect(() => print(setState));
}

View File

@@ -0,0 +1,64 @@
## Input
```javascript
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
function Foo({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
arr2.push(2);
return {arr, arr2};
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{propVal: 1}],
sequentialRenders: [{propVal: 1}, {propVal: 2}],
};
```
## Code
```javascript
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import { print } from "shared-runtime";
import useEffectWrapper from "useEffectWrapper";
function Foo({ propVal }) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
arr2.push(2);
return { arr, arr2 };
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ propVal: 1 }],
sequentialRenders: [{ propVal: 1 }, { propVal: 2 }],
};
```
## Logs
```
{"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"}}
{"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"}]}
{"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"}]}
{"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}
```
### Eval output
(kind: ok) {"arr":[1],"arr2":[2]}
{"arr":[2],"arr2":[2]}
logs: [[ 1 ],[ 2 ]]

View File

@@ -0,0 +1,19 @@
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
function Foo({propVal}) {
const arr = [propVal];
useEffectWrapper(() => print(arr));
const arr2 = [];
useEffectWrapper(() => arr2.push(propVal));
arr2.push(2);
return {arr, arr2};
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{propVal: 1}],
sequentialRenders: [{propVal: 1}, {propVal: 2}],
};

View File

@@ -0,0 +1,80 @@
## Input
```javascript
// @enableFire
import {fire} from 'react';
function Component({bar, baz}) {
const foo = () => {
console.log(bar);
};
useEffect(() => {
fire(foo(bar));
fire(baz(bar));
});
useEffect(() => {
fire(foo(bar));
});
return null;
}
```
## Code
```javascript
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire
import { fire } from "react";
function Component(t0) {
const $ = _c(9);
const { bar, baz } = t0;
let t1;
if ($[0] !== bar) {
t1 = () => {
console.log(bar);
};
$[0] = bar;
$[1] = t1;
} else {
t1 = $[1];
}
const foo = t1;
const t2 = useFire(foo);
const t3 = useFire(baz);
let t4;
if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) {
t4 = () => {
t2(bar);
t3(bar);
};
$[2] = bar;
$[3] = t2;
$[4] = t3;
$[5] = t4;
} else {
t4 = $[5];
}
useEffect(t4);
let t5;
if ($[6] !== bar || $[7] !== t2) {
t5 = () => {
t2(bar);
};
$[6] = bar;
$[7] = t2;
$[8] = t5;
} else {
t5 = $[8];
}
useEffect(t5);
return null;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,18 @@
// @enableFire
import {fire} from 'react';
function Component({bar, baz}) {
const foo = () => {
console.log(bar);
};
useEffect(() => {
fire(foo(bar));
fire(baz(bar));
});
useEffect(() => {
fire(foo(bar));
});
return null;
}

View File

@@ -0,0 +1,94 @@
## Input
```javascript
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
function Foo({arr1, arr2, foo}) {
const x = [arr1];
let y = [];
const getVal1 = useCallback(() => {
return {x: 2};
}, []);
const getVal2 = useCallback(() => {
return [y];
}, [foo ? (y = x.concat(arr2)) : y]);
return <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{arr1: [1, 2], arr2: [3, 4], foo: true}],
sequentialRenders: [
{arr1: [1, 2], arr2: [3, 4], foo: true},
{arr1: [1, 2], arr2: [3, 4], foo: false},
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useCallback } from "react";
import { Stringify } from "shared-runtime";
function Foo(t0) {
const $ = _c(8);
const { arr1, arr2, foo } = t0;
let getVal1;
let t1;
if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) {
const x = [arr1];
let y = [];
getVal1 = _temp;
t1 = () => [y];
foo ? (y = x.concat(arr2)) : y;
$[0] = arr1;
$[1] = arr2;
$[2] = foo;
$[3] = getVal1;
$[4] = t1;
} else {
getVal1 = $[3];
t1 = $[4];
}
const getVal2 = t1;
let t2;
if ($[5] !== getVal1 || $[6] !== getVal2) {
t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
$[5] = getVal1;
$[6] = getVal2;
$[7] = t2;
} else {
t2 = $[7];
}
return t2;
}
function _temp() {
return { x: 2 };
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ arr1: [1, 2], arr2: [3, 4], foo: true }],
sequentialRenders: [
{ arr1: [1, 2], arr2: [3, 4], foo: true },
{ arr1: [1, 2], arr2: [3, 4], foo: false },
],
};
```
### Eval output
(kind: ok) <div>{"val1":{"kind":"Function","result":{"x":2}},"val2":{"kind":"Function","result":[[[1,2],3,4]]},"shouldInvokeFns":true}</div>
<div>{"val1":{"kind":"Function","result":{"x":2}},"val2":{"kind":"Function","result":[[]]},"shouldInvokeFns":true}</div>

View File

@@ -0,0 +1,27 @@
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
function Foo({arr1, arr2, foo}) {
const x = [arr1];
let y = [];
const getVal1 = useCallback(() => {
return {x: 2};
}, []);
const getVal2 = useCallback(() => {
return [y];
}, [foo ? (y = x.concat(arr2)) : y]);
return <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{arr1: [1, 2], arr2: [3, 4], foo: true}],
sequentialRenders: [
{arr1: [1, 2], arr2: [3, 4], foo: true},
{arr1: [1, 2], arr2: [3, 4], foo: false},
],
};

View File

@@ -0,0 +1,77 @@
## Input
```javascript
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
// We currently produce invalid output (incorrect scoping for `y` declaration)
function useFoo(arr1, arr2) {
const x = [arr1];
let y;
const getVal = useCallback(() => {
return {y};
}, [((y = x.concat(arr2)), y)]);
return <Stringify getVal={getVal} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useCallback } from "react";
import { Stringify } from "shared-runtime";
// We currently produce invalid output (incorrect scoping for `y` declaration)
function useFoo(arr1, arr2) {
const $ = _c(5);
let t0;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
let y;
t0 = () => ({ y });
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = t0;
} else {
t0 = $[2];
}
const getVal = t0;
let t1;
if ($[3] !== getVal) {
t1 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
$[3] = getVal;
$[4] = t1;
} else {
t1 = $[4];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};
```
### Eval output
(kind: ok) <div>{"getVal":{"kind":"Function","result":{"y":[[1,2],3,4]}},"shouldInvokeFns":true}</div>

View File

@@ -0,0 +1,22 @@
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
// We currently produce invalid output (incorrect scoping for `y` declaration)
function useFoo(arr1, arr2) {
const x = [arr1];
let y;
const getVal = useCallback(() => {
return {y};
}, [((y = x.concat(arr2)), y)]);
return <Stringify getVal={getVal} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};

View File

@@ -0,0 +1,69 @@
## Input
```javascript
import {useMemo} from 'react';
function useFoo(arr1, arr2) {
const x = [arr1];
let y;
return useMemo(() => {
return {y};
}, [((y = x.concat(arr2)), y)]);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useMemo } from "react";
function useFoo(arr1, arr2) {
const $ = _c(5);
let y;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = y;
} else {
y = $[2];
}
let t0;
let t1;
if ($[3] !== y) {
t1 = { y };
$[3] = y;
$[4] = t1;
} else {
t1 = $[4];
}
t0 = t1;
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};
```
### Eval output
(kind: ok) {"y":[[1,2],3,4]}

View File

@@ -0,0 +1,18 @@
import {useMemo} from 'react';
function useFoo(arr1, arr2) {
const x = [arr1];
let y;
return useMemo(() => {
return {y};
}, [((y = x.concat(arr2)), y)]);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [
[1, 2],
[3, 4],
],
};