@samitouri / QOS-React / commits / 59e37b088b

[HIR] Detect more cases of invalid block nesting

ghstack-source-id: 9e08fa69807ee1b5e99675f9aefc8acd13c16827 Pull Request resolved: https://github.com/facebook/react-forget/pull/2899

Mofei Zhang committed Apr 26, 2024 at 12:40 UTC 59e37b088b4f7c8711c90a9248701ab6a4c08a21
5 files changed +94 -11
compiler/packages/babel-plugin-react-forget/src/HIR/AssertValidBlockNesting.ts
+18 -11
@@ -124,17 +124,24 @@ export function assertValidBlockNesting(fn: HIRFunction): void {
124
125 blocks.sort(nestedRangeComparator);
126
127 - for (let i = 1; i < blocks.length; i++) {
128 - const last = blocks[i - 1];
127 + let active: Array<Block> = [];
128 + for (let i = 0; i < blocks.length; i++) {
129 const curr = blocks[i];
130 -
131 - const blocksDisjoint = curr.start >= last.end;
132 - const blocksNested = curr.end <= last.end;
133 -
134 - CompilerError.invariant(blocksDisjoint || blocksNested, {
135 - reason: "Invalid nesting in program blocks or scopes",
136 - description: `Blocks overlap but are not nested: ${last.kind}@${last.id}(${last.start}:${last.end}) ${curr.kind}@${curr.id}(${curr.start}:${curr.end})`,
137 - loc: GeneratedSource,
138 - });
130 + for (let i = active.length - 1; i >= 0; i--) {
131 + const maybeParent = active[i];
132 + const disjoint = curr.start >= maybeParent.end;
133 + const nested = curr.end <= maybeParent.end;
134 + CompilerError.invariant(disjoint || nested, {
135 + reason: "Invalid nesting in program blocks or scopes",
136 + description: `Blocks overlap but are not nested: ${maybeParent.kind}@${maybeParent.id}(${maybeParent.start}:${maybeParent.end}) ${curr.kind}@${curr.id}(${curr.start}:${curr.end})`,
137 + loc: GeneratedSource,
138 + });
139 + if (disjoint) {
140 + active.length = i;
141 + } else {
142 + break;
143 + }
144 + }
145 + active.push(curr);
146 }
147 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.bug-repro-trycatch-nested-overlapping-range.expect.md new
+26
@@ -0,0 +1,26 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo() {
6 + try {
7 + let thing = null;
8 + if (cond) {
9 + thing = makeObject();
10 + }
11 + if (otherCond) {
12 + mutate(thing);
13 + }
14 + } catch {}
15 +}
16 +
17 +```
18 +
19 +
20 +## Error
21 +
22 +```
23 +Invariant: Invalid nesting in program blocks or scopes. Blocks overlap but are not nested: Scope@0(2:24) ProgramBlockSubtree@17(18:26)
24 +```
25 +
26 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.bug-repro-trycatch-nested-overlapping-range.js new
+11
@@ -0,0 +1,11 @@
1 +function Foo() {
2 + try {
3 + let thing = null;
4 + if (cond) {
5 + thing = makeObject();
6 + }
7 + if (otherCond) {
8 + mutate(thing);
9 + }
10 + } catch {}
11 +}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.repro-bug-ref-mutable-range.expect.md new
+27
@@ -0,0 +1,27 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo(props, ref) {
6 + const value = {};
7 + if (cond1) {
8 + mutate(value);
9 + return <Child ref={ref} />;
10 + }
11 + mutate(value);
12 + if (cond2) {
13 + return <Child ref={identity(ref)} />;
14 + }
15 + return value;
16 +}
17 +
18 +```
19 +
20 +
21 +## Error
22 +
23 +```
24 +Invariant: Invalid nesting in program blocks or scopes. Blocks overlap but are not nested: Scope@0(1:21) ProgramBlockSubtree@1(16:23)
25 +```
26 +
27 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.repro-bug-ref-mutable-range.js new
+12
@@ -0,0 +1,12 @@
1 +function Foo(props, ref) {
2 + const value = {};
3 + if (cond1) {
4 + mutate(value);
5 + return <Child ref={ref} />;
6 + }
7 + mutate(value);
8 + if (cond2) {
9 + return <Child ref={identity(ref)} />;
10 + }
11 + return value;
12 +}