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
+};