@samitouri / QOS-React-1 / commits / e7d0c81d13

Add todo for for loops with context iterator variable

Detect the previous case — for loops where the iterator is a context variables — and throw a todo rather than hitting the invariant.

Joe Savona committed Mar 22, 2024 at 11:46 UTC e7d0c81d13a0358916bcb4db5a5ad944098e482a
6 files changed +180 -12
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+15 -1
@@ -967,6 +967,20 @@ function codegenForInit(
967 init: ReactiveValue
968 ): t.Expression | t.VariableDeclaration | null {
969 if (init.kind === "SequenceExpression") {
970 + for (const instr of init.instructions) {
971 + if (instr.value.kind === "DeclareContext") {
972 + CompilerError.throwTodo({
973 + reason: `Support for loops where the index variable is a context variable`,
974 + loc: instr.loc,
975 + description:
976 + instr.value.lvalue.place.identifier.name != null
977 + ? `'${instr.value.lvalue.place.identifier.name.value}' is a context variable`
978 + : null,
979 + suggestions: null,
980 + });
981 + }
982 + }
983 +
984 const body = codegenBlock(
985 cx,
986 init.instructions.map((instruction) => ({
@@ -983,7 +997,7 @@ function codegenForInit(
997 {
998 reason: "Expected a variable declaration",
999 loc: init.loc,
986 - description: null,
1000 + description: `Got ${instr.type}`,
1001 suggestions: null,
1002 }
1003 );
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md new
+57
@@ -0,0 +1,57 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { useHook } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const data = useHook();
9 + const items = [];
10 + // NOTE: `item` is a context variable because it's reassigned and also referenced
11 + // within a closure, the `onClick` handler of each item
12 + for (let key in props.data) {
13 + key = key ?? null; // no-op reassignment to force a context variable
14 + items.push(
15 + <div key={key} onClick={() => data.set(key)}>
16 + {key}
17 + </div>
18 + );
19 + }
20 + return <div>{items}</div>;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: Component,
25 + params: [{ data: { a: "a", b: true, c: "hello" } }],
26 +};
27 +
28 +```
29 +
30 +
31 +## Error
32 +
33 +```
34 + 6 | // NOTE: `item` is a context variable because it's reassigned and also referenced
35 + 7 | // within a closure, the `onClick` handler of each item
36 +> 8 | for (let key in props.data) {
37 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38 +> 9 | key = key ?? null; // no-op reassignment to force a context variable
39 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 +> 10 | items.push(
41 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 +> 11 | <div key={key} onClick={() => data.set(key)}>
43 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44 +> 12 | {key}
45 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46 +> 13 | </div>
47 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48 +> 14 | );
49 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50 +> 15 | }
51 + | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
52 + 16 | return <div>{items}</div>;
53 + 17 | }
54 + 18 |
55 +```
56 +
57 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js new
+22
@@ -0,0 +1,22 @@
1 +import { useHook } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const data = useHook();
5 + const items = [];
6 + // NOTE: `item` is a context variable because it's reassigned and also referenced
7 + // within a closure, the `onClick` handler of each item
8 + for (let key in props.data) {
9 + key = key ?? null; // no-op reassignment to force a context variable
10 + items.push(
11 + <div key={key} onClick={() => data.set(key)}>
12 + {key}
13 + </div>
14 + );
15 + }
16 + return <div>{items}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{ data: { a: "a", b: true, c: "hello" } }],
22 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md
+7 -11
@@ -19,17 +19,13 @@ function Component() {
19 ## Error
20
21 ```
22 - 4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
23 - 5 | // within a closure, the `onClick` handler of each item
24 -> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
25 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26 -> 7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
27 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28 -> 8 | }
29 - | ^^^^ [ReactForget] Invariant: Expected a variable declaration (6:8)
30 - 9 | return items;
31 - 10 | }
32 - 11 |
22 + 4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
23 + 5 | // within a closure, the `onClick` handler of each item
24 +> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
25 + | ^^^^^^^^^^^ [ReactForget] Todo: Support for loops where the index variable is a context variable. 'i' is a context variable (6:6)
26 + 7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
27 + 8 | }
28 + 9 | return items;
29 ```
30
31
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md new
+57
@@ -0,0 +1,57 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { useHook } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const data = useHook();
9 + const items = [];
10 + // NOTE: `item` is a context variable because it's reassigned and also referenced
11 + // within a closure, the `onClick` handler of each item
12 + for (let item of props.data) {
13 + item = item ?? {}; // reassignment to force a context variable
14 + items.push(
15 + <div key={item.id} onClick={() => data.set(item)}>
16 + {item.id}
17 + </div>
18 + );
19 + }
20 + return <div>{items}</div>;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: Component,
25 + params: [{ data: [{ id: "1" }, { id: "2" }] }],
26 +};
27 +
28 +```
29 +
30 +
31 +## Error
32 +
33 +```
34 + 6 | // NOTE: `item` is a context variable because it's reassigned and also referenced
35 + 7 | // within a closure, the `onClick` handler of each item
36 +> 8 | for (let item of props.data) {
37 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38 +> 9 | item = item ?? {}; // reassignment to force a context variable
39 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 +> 10 | items.push(
41 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 +> 11 | <div key={item.id} onClick={() => data.set(item)}>
43 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44 +> 12 | {item.id}
45 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46 +> 13 | </div>
47 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48 +> 14 | );
49 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50 +> 15 | }
51 + | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
52 + 16 | return <div>{items}</div>;
53 + 17 | }
54 + 18 |
55 +```
56 +
57 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js new
+22
@@ -0,0 +1,22 @@
1 +import { useHook } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const data = useHook();
5 + const items = [];
6 + // NOTE: `item` is a context variable because it's reassigned and also referenced
7 + // within a closure, the `onClick` handler of each item
8 + for (let item of props.data) {
9 + item = item ?? {}; // reassignment to force a context variable
10 + items.push(
11 + <div key={item.id} onClick={() => data.set(item)}>
12 + {item.id}
13 + </div>
14 + );
15 + }
16 + return <div>{items}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{ data: [{ id: "1" }, { id: "2" }] }],
22 +};