[repro] Repro for control flow bug in PropagateScopeDependency
--- Thanks to @josephsavona for finding this bug. This is another example of why we really want hir-everywhere. Forget output currently nullthrows because we believe `obj.a` is run unconditionally in source (missing the break/returns out of this scope)
Mofei Zhang committed
Mar 21, 2024 at 17:44 UTC
e047db8d037f63884a41575ea733c27fe8bd1f14
9 files changed
+357
-4
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/bug-reduce-reactive-deps-break-in-scope.expect.md
new
+63
@@ -0,0 +1,63 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function useFoo({ obj, objIsNull }) {
6
+ const x = [];
7
+ b0: {
8
+ if (objIsNull) {
9
+ break b0;
10
+ }
11
+ x.push(obj.a);
12
+ }
13
+ return x;
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: useFoo,
18
+ params: [{ obj: null, objIsNull: true }],
19
+ sequentialRenders: [
20
+ { obj: null, objIsNull: true },
21
+ { obj: { a: 2 }, objIsNull: false },
22
+ ],
23
+};
24
+
25
+```
26
+
27
+## Code
28
+
29
+```javascript
30
+import { unstable_useMemoCache as useMemoCache } from "react";
31
+function useFoo(t0) {
32
+ const $ = useMemoCache(3);
33
+ const { obj, objIsNull } = t0;
34
+ let x;
35
+ if ($[0] !== objIsNull || $[1] !== obj.a) {
36
+ x = [];
37
+ bb1: {
38
+ if (objIsNull) {
39
+ break bb1;
40
+ }
41
+
42
+ x.push(obj.a);
43
+ }
44
+ $[0] = objIsNull;
45
+ $[1] = obj.a;
46
+ $[2] = x;
47
+ } else {
48
+ x = $[2];
49
+ }
50
+ return x;
51
+}
52
+
53
+export const FIXTURE_ENTRYPOINT = {
54
+ fn: useFoo,
55
+ params: [{ obj: null, objIsNull: true }],
56
+ sequentialRenders: [
57
+ { obj: null, objIsNull: true },
58
+ { obj: { a: 2 }, objIsNull: false },
59
+ ],
60
+};
61
+
62
+```
63
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/bug-reduce-reactive-deps-break-in-scope.ts
new
+19
@@ -0,0 +1,19 @@
1
+function useFoo({ obj, objIsNull }) {
2
+ const x = [];
3
+ b0: {
4
+ if (objIsNull) {
5
+ break b0;
6
+ }
7
+ x.push(obj.a);
8
+ }
9
+ return x;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: useFoo,
14
+ params: [{ obj: null, objIsNull: true }],
15
+ sequentialRenders: [
16
+ { obj: null, objIsNull: true },
17
+ { obj: { a: 2 }, objIsNull: false },
18
+ ],
19
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/bug-reduce-reactive-deps-return-in-scope.expect.md
new
+69
@@ -0,0 +1,69 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function useFoo({ obj, objIsNull }) {
6
+ const x = [];
7
+ if (objIsNull) {
8
+ return;
9
+ }
10
+ x.push(obj.b);
11
+ return x;
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: useFoo,
16
+ params: [{ obj: null, objIsNull: true }],
17
+ sequentialRenders: [
18
+ { obj: null, objIsNull: true },
19
+ { obj: { a: 2 }, objIsNull: false },
20
+ ],
21
+};
22
+
23
+```
24
+
25
+## Code
26
+
27
+```javascript
28
+import { unstable_useMemoCache as useMemoCache } from "react";
29
+function useFoo(t0) {
30
+ const $ = useMemoCache(4);
31
+ const { obj, objIsNull } = t0;
32
+ let x;
33
+ let t1;
34
+ if ($[0] !== objIsNull || $[1] !== obj.b) {
35
+ t1 = Symbol.for("react.early_return_sentinel");
36
+ bb7: {
37
+ x = [];
38
+ if (objIsNull) {
39
+ t1 = undefined;
40
+ break bb7;
41
+ }
42
+
43
+ x.push(obj.b);
44
+ }
45
+ $[0] = objIsNull;
46
+ $[1] = obj.b;
47
+ $[2] = x;
48
+ $[3] = t1;
49
+ } else {
50
+ x = $[2];
51
+ t1 = $[3];
52
+ }
53
+ if (t1 !== Symbol.for("react.early_return_sentinel")) {
54
+ return t1;
55
+ }
56
+ return x;
57
+}
58
+
59
+export const FIXTURE_ENTRYPOINT = {
60
+ fn: useFoo,
61
+ params: [{ obj: null, objIsNull: true }],
62
+ sequentialRenders: [
63
+ { obj: null, objIsNull: true },
64
+ { obj: { a: 2 }, objIsNull: false },
65
+ ],
66
+};
67
+
68
+```
69
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/bug-reduce-reactive-deps-return-in-scope.ts
new
+17
@@ -0,0 +1,17 @@
1
+function useFoo({ obj, objIsNull }) {
2
+ const x = [];
3
+ if (objIsNull) {
4
+ return;
5
+ }
6
+ x.push(obj.b);
7
+ return x;
8
+}
9
+
10
+export const FIXTURE_ENTRYPOINT = {
11
+ fn: useFoo,
12
+ params: [{ obj: null, objIsNull: true }],
13
+ sequentialRenders: [
14
+ { obj: null, objIsNull: true },
15
+ { obj: { a: 2 }, objIsNull: false },
16
+ ],
17
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-cond-deps-break-in-scope.expect.md
new
+70
@@ -0,0 +1,70 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function useFoo({ obj, objIsNull }) {
6
+ const x = [];
7
+ b0: {
8
+ if (objIsNull) {
9
+ break b0;
10
+ } else {
11
+ x.push(obj.a);
12
+ }
13
+ x.push(obj.b);
14
+ }
15
+ return x;
16
+}
17
+
18
+export const FIXTURE_ENTRYPOINT = {
19
+ fn: useFoo,
20
+ params: [{ obj: null, objIsNull: true }],
21
+ sequentialRenders: [
22
+ { obj: null, objIsNull: true },
23
+ { obj: { a: 2 }, objIsNull: false },
24
+ ],
25
+};
26
+
27
+```
28
+
29
+## Code
30
+
31
+```javascript
32
+import { unstable_useMemoCache as useMemoCache } from "react";
33
+function useFoo(t0) {
34
+ const $ = useMemoCache(3);
35
+ const { obj, objIsNull } = t0;
36
+ let x;
37
+ if ($[0] !== objIsNull || $[1] !== obj) {
38
+ x = [];
39
+ bb1: {
40
+ if (objIsNull) {
41
+ break bb1;
42
+ } else {
43
+ x.push(obj.a);
44
+ }
45
+
46
+ x.push(obj.b);
47
+ }
48
+ $[0] = objIsNull;
49
+ $[1] = obj;
50
+ $[2] = x;
51
+ } else {
52
+ x = $[2];
53
+ }
54
+ return x;
55
+}
56
+
57
+export const FIXTURE_ENTRYPOINT = {
58
+ fn: useFoo,
59
+ params: [{ obj: null, objIsNull: true }],
60
+ sequentialRenders: [
61
+ { obj: null, objIsNull: true },
62
+ { obj: { a: 2 }, objIsNull: false },
63
+ ],
64
+};
65
+
66
+```
67
+
68
+### Eval output
69
+(kind: ok) []
70
+[2,null]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-cond-deps-break-in-scope.ts
new
+21
@@ -0,0 +1,21 @@
1
+function useFoo({ obj, objIsNull }) {
2
+ const x = [];
3
+ b0: {
4
+ if (objIsNull) {
5
+ break b0;
6
+ } else {
7
+ x.push(obj.a);
8
+ }
9
+ x.push(obj.b);
10
+ }
11
+ return x;
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: useFoo,
16
+ params: [{ obj: null, objIsNull: true }],
17
+ sequentialRenders: [
18
+ { obj: null, objIsNull: true },
19
+ { obj: { a: 2 }, objIsNull: false },
20
+ ],
21
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-cond-deps-return-in-scope.expect.md
new
+76
@@ -0,0 +1,76 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function useFoo({ obj, objIsNull }) {
6
+ const x = [];
7
+ if (objIsNull) {
8
+ return;
9
+ } else {
10
+ x.push(obj.a);
11
+ }
12
+ x.push(obj.b);
13
+ return x;
14
+}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: useFoo,
18
+ params: [{ obj: null, objIsNull: true }],
19
+ sequentialRenders: [
20
+ { obj: null, objIsNull: true },
21
+ { obj: { a: 2 }, objIsNull: false },
22
+ ],
23
+};
24
+
25
+```
26
+
27
+## Code
28
+
29
+```javascript
30
+import { unstable_useMemoCache as useMemoCache } from "react";
31
+function useFoo(t0) {
32
+ const $ = useMemoCache(4);
33
+ const { obj, objIsNull } = t0;
34
+ let x;
35
+ let t1;
36
+ if ($[0] !== objIsNull || $[1] !== obj) {
37
+ t1 = Symbol.for("react.early_return_sentinel");
38
+ bb8: {
39
+ x = [];
40
+ if (objIsNull) {
41
+ t1 = undefined;
42
+ break bb8;
43
+ } else {
44
+ x.push(obj.a);
45
+ }
46
+
47
+ x.push(obj.b);
48
+ }
49
+ $[0] = objIsNull;
50
+ $[1] = obj;
51
+ $[2] = x;
52
+ $[3] = t1;
53
+ } else {
54
+ x = $[2];
55
+ t1 = $[3];
56
+ }
57
+ if (t1 !== Symbol.for("react.early_return_sentinel")) {
58
+ return t1;
59
+ }
60
+ return x;
61
+}
62
+
63
+export const FIXTURE_ENTRYPOINT = {
64
+ fn: useFoo,
65
+ params: [{ obj: null, objIsNull: true }],
66
+ sequentialRenders: [
67
+ { obj: null, objIsNull: true },
68
+ { obj: { a: 2 }, objIsNull: false },
69
+ ],
70
+};
71
+
72
+```
73
+
74
+### Eval output
75
+(kind: ok)
76
+[2,null]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-cond-deps-return-in-scope.ts
new
+19
@@ -0,0 +1,19 @@
1
+function useFoo({ obj, objIsNull }) {
2
+ const x = [];
3
+ if (objIsNull) {
4
+ return;
5
+ } else {
6
+ x.push(obj.a);
7
+ }
8
+ x.push(obj.b);
9
+ return x;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: useFoo,
14
+ params: [{ obj: null, objIsNull: true }],
15
+ sequentialRenders: [
16
+ { obj: null, objIsNull: true },
17
+ { obj: { a: 2 }, objIsNull: false },
18
+ ],
19
+};
compiler/packages/snap/src/SproutTodoFilter.ts
+3
-4
@@ -531,10 +531,9 @@ const skipFilter = new Set([
531
"rules-of-hooks/rules-of-hooks-93dc5d5e538a",
532
"rules-of-hooks/rules-of-hooks-69521d94fa03",
533
534
- // bug
535
- "bug-jsx-memberexpr-tag-in-lambda",
536
- "bug-invalid-code-when-bailout",
537
- "component-syntax-ref-gating.flow",
534
+ // bugs
535
+ "bug-reduce-reactive-deps-return-in-scope",
536
+ "bug-reduce-reactive-deps-break-in-scope",
537
538
// 'react-forget-runtime' not yet supported
539
"flag-enable-emit-hook-guards",