Skip to content

Commit e096403

Browse files
authored
[compiler] Infer types for properties after holes in array patterns (#34847)
In InferTypes when we infer types for properties during destructuring, we were breaking out of the loop when we encounter a hole in the array. Instead we should just skip that element and continue inferring later properties. Closes #34748 --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34847). * #34855 * __->__ #34847
1 parent 1873ad7 commit e096403

9 files changed

+192
-47
lines changed

compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ function* generateInstructionTypes(
393393
shapeId: BuiltInArrayId,
394394
});
395395
} else {
396-
break;
396+
continue;
397397
}
398398
}
399399
} else {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
## Input
3+
4+
```javascript
5+
function Component(props) {
6+
// Intentionally don't bind state, this repros a bug where we didn't
7+
// infer the type of destructured properties after a hole in the array
8+
let [, setState] = useState();
9+
setState(1);
10+
return props.foo;
11+
}
12+
13+
export const FIXTURE_ENTRYPOINT = {
14+
fn: Component,
15+
params: ['TodoAdd'],
16+
isComponent: 'TodoAdd',
17+
};
18+
19+
```
20+
21+
22+
## Error
23+
24+
```
25+
Found 1 error:
26+
27+
Error: Calling setState during render may trigger an infinite loop
28+
29+
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState).
30+
31+
error.invalid-setState-in-render-unbound-state.ts:5:2
32+
3 | // infer the type of destructured properties after a hole in the array
33+
4 | let [, setState] = useState();
34+
> 5 | setState(1);
35+
| ^^^^^^^^ Found setState() in render
36+
6 | return props.foo;
37+
7 | }
38+
8 |
39+
```
40+
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function Component(props) {
2+
// Intentionally don't bind state, this repros a bug where we didn't
3+
// infer the type of destructured properties after a hole in the array
4+
let [, setState] = useState();
5+
setState(1);
6+
return props.foo;
7+
}
8+
9+
export const FIXTURE_ENTRYPOINT = {
10+
fn: Component,
11+
params: ['TodoAdd'],
12+
isComponent: 'TodoAdd',
13+
};

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.expect.md

Lines changed: 0 additions & 35 deletions
This file was deleted.

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.js

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validatePreserveExistingMemoizationGuarantees
6+
import {useCallback, useTransition} from 'react';
7+
8+
function useFoo() {
9+
const [, /* isPending intentionally not captured */ start] = useTransition();
10+
11+
return useCallback(() => {
12+
start();
13+
}, []);
14+
}
15+
16+
export const FIXTURE_ENTRYPOINT = {
17+
fn: useFoo,
18+
params: [],
19+
};
20+
21+
```
22+
23+
## Code
24+
25+
```javascript
26+
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
27+
import { useCallback, useTransition } from "react";
28+
29+
function useFoo() {
30+
const $ = _c(1);
31+
const [, start] = useTransition();
32+
let t0;
33+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
34+
t0 = () => {
35+
start();
36+
};
37+
$[0] = t0;
38+
} else {
39+
t0 = $[0];
40+
}
41+
return t0;
42+
}
43+
44+
export const FIXTURE_ENTRYPOINT = {
45+
fn: useFoo,
46+
params: [],
47+
};
48+
49+
```
50+
51+
### Eval output
52+
(kind: ok) "[[ function params=0 ]]"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @validatePreserveExistingMemoizationGuarantees
2+
import {useCallback, useTransition} from 'react';
3+
4+
function useFoo() {
5+
const [, /* isPending intentionally not captured */ start] = useTransition();
6+
7+
return useCallback(() => {
8+
start();
9+
}, []);
10+
}
11+
12+
export const FIXTURE_ENTRYPOINT = {
13+
fn: useFoo,
14+
params: [],
15+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validatePreserveExistingMemoizationGuarantees
6+
import {useCallback, useTransition} from 'react';
7+
8+
function useFoo() {
9+
const [, /* state value intentionally not captured */ setState] = useState();
10+
11+
return useCallback(() => {
12+
setState(x => x + 1);
13+
}, []);
14+
}
15+
16+
export const FIXTURE_ENTRYPOINT = {
17+
fn: useFoo,
18+
params: [],
19+
};
20+
21+
```
22+
23+
## Code
24+
25+
```javascript
26+
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
27+
import { useCallback, useTransition } from "react";
28+
29+
function useFoo() {
30+
const $ = _c(1);
31+
const [, setState] = useState();
32+
let t0;
33+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
34+
t0 = () => {
35+
setState(_temp);
36+
};
37+
$[0] = t0;
38+
} else {
39+
t0 = $[0];
40+
}
41+
return t0;
42+
}
43+
function _temp(x) {
44+
return x + 1;
45+
}
46+
47+
export const FIXTURE_ENTRYPOINT = {
48+
fn: useFoo,
49+
params: [],
50+
};
51+
52+
```
53+
54+
### Eval output
55+
(kind: exception) useState is not defined
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @validatePreserveExistingMemoizationGuarantees
2+
import {useCallback, useTransition} from 'react';
3+
4+
function useFoo() {
5+
const [, /* state value intentionally not captured */ setState] = useState();
6+
7+
return useCallback(() => {
8+
setState(x => x + 1);
9+
}, []);
10+
}
11+
12+
export const FIXTURE_ENTRYPOINT = {
13+
fn: useFoo,
14+
params: [],
15+
};

0 commit comments

Comments
 (0)