@samitouri / QOS-React-2 / commits / b2f44c103b

Fixtures demonstrating incorrect block scoping due to MergeConsecutiveBlocks

Fixtures from T173102122 and T173101739 demonstrating cases where MergeConsecutiveBlocks can move code out of its correct block scope, changing behavior or breaking the program, in cases where a control flow structure (such as switch) only has one non-returning control flow path. In these cases, the non-returning path gets merged with the fallthrough, effectively lifting that code out of the control flow structure and moving it into the outer scope. This can create dead code or just invalid code (with references to variables that are not in scope). Sprout fails on both of these fixtures: <img width="812" alt="Screenshot 2024-01-23 at 11 25 36 AM" src="https://github.com/facebook/react-forget/assets/6425824/d397ea22-3fa3-436e-b655-09a45781274b">

Joe Savona committed Jan 23, 2024 at 11:40 UTC b2f44c103b45b3bdded2e140b14042101a59b829
5 files changed +175
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/block-scoping-switch-dead-code.expect.md new
+54
@@ -0,0 +1,54 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function useHook(a, b) {
6 + switch (a) {
7 + case 1:
8 + if (b == null) {
9 + return;
10 + }
11 + console.log(b);
12 + break;
13 + case 2:
14 + return;
15 + default:
16 + return;
17 + }
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: useHook,
22 + params: [1, "foo"],
23 +};
24 +
25 +```
26 +
27 +## Code
28 +
29 +```javascript
30 +function useHook(a, b) {
31 + switch (a) {
32 + case 1: {
33 + if (b == null) {
34 + return;
35 + }
36 + }
37 + case 2: {
38 + return;
39 + }
40 + default: {
41 + return;
42 + }
43 + }
44 +
45 + console.log(b);
46 +}
47 +
48 +export const FIXTURE_ENTRYPOINT = {
49 + fn: useHook,
50 + params: [1, "foo"],
51 +};
52 +
53 +```
54 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/block-scoping-switch-dead-code.js new
+19
@@ -0,0 +1,19 @@
1 +function useHook(a, b) {
2 + switch (a) {
3 + case 1:
4 + if (b == null) {
5 + return;
6 + }
7 + console.log(b);
8 + break;
9 + case 2:
10 + return;
11 + default:
12 + return;
13 + }
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: useHook,
18 + params: [1, "foo"],
19 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/block-scoping-switch-variable-scoping.expect.md new
+77
@@ -0,0 +1,77 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { useMemo } from "react";
6 +
7 +function Component(props) {
8 + const outerHandlers = useMemo(() => {
9 + let handlers = { value: props.value };
10 + switch (props.test) {
11 + case true: {
12 + console.log(handlers.value);
13 + break;
14 + }
15 + default: {
16 + }
17 + }
18 + return handlers;
19 + });
20 + return outerHandlers;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: Component,
25 + params: [{ test: true, value: "hello" }],
26 +};
27 +
28 +```
29 +
30 +## Code
31 +
32 +```javascript
33 +import { useMemo, unstable_useMemoCache as useMemoCache } from "react";
34 +
35 +function Component(props) {
36 + const $ = useMemoCache(2);
37 + let t22;
38 + bb2: {
39 + let t0;
40 + if ($[0] !== props.value) {
41 + t0 = { value: props.value };
42 + $[0] = props.value;
43 + $[1] = t0;
44 + } else {
45 + t0 = $[1];
46 + }
47 + const handlers = t0;
48 + switch (props.test) {
49 + case true: {
50 + console.log(handlers.value);
51 + break bb2;
52 + }
53 + default: {
54 + }
55 + }
56 + }
57 +
58 + t22 = handlers;
59 + const outerHandlers = t22;
60 + return outerHandlers;
61 +}
62 +
63 +export const FIXTURE_ENTRYPOINT = {
64 + fn: Component,
65 + params: [{ test: true, value: "hello" }],
66 +};
67 +
68 +```
69 +
70 +### Eval output
71 +(kind: exception) handlers.foo is not a function
72 +logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
73 + '\n' +
74 + ' at WrapperTestComponent (<project_root>/packages/sprout/dist/runner-evaluator.js:55:26)\n' +
75 + '\n' +
76 + 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
77 + 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/block-scoping-switch-variable-scoping.js new
+22
@@ -0,0 +1,22 @@
1 +import { useMemo } from "react";
2 +
3 +function Component(props) {
4 + const outerHandlers = useMemo(() => {
5 + let handlers = { value: props.value };
6 + switch (props.test) {
7 + case true: {
8 + console.log(handlers.value);
9 + break;
10 + }
11 + default: {
12 + }
13 + }
14 + return handlers;
15 + });
16 + return outerHandlers;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{ test: true, value: "hello" }],
22 +};
compiler/packages/sprout/src/SproutTodoFilter.ts
+3
@@ -517,6 +517,9 @@ const skipFilter = new Set([
517 "bug-invalid-code-when-bailout",
518 "component-syntax-ref-gating.flow",
519
520 + "block-scoping-switch-dead-code",
521 + "block-scoping-switch-variable-scoping",
522 +
523 // 'react-forget-runtime' not yet supported
524 "flag-enable-emit-hook-guards",
525 ]);