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.
This commit is contained in:
Joe Savona
2024-03-22 11:46:28 -07:00
parent bc516409a6
commit e7d0c81d13
6 changed files with 180 additions and 12 deletions
@@ -967,6 +967,20 @@ function codegenForInit(
init: ReactiveValue
): t.Expression | t.VariableDeclaration | null {
if (init.kind === "SequenceExpression") {
for (const instr of init.instructions) {
if (instr.value.kind === "DeclareContext") {
CompilerError.throwTodo({
reason: `Support for loops where the index variable is a context variable`,
loc: instr.loc,
description:
instr.value.lvalue.place.identifier.name != null
? `'${instr.value.lvalue.place.identifier.name.value}' is a context variable`
: null,
suggestions: null,
});
}
}
const body = codegenBlock(
cx,
init.instructions.map((instruction) => ({
@@ -983,7 +997,7 @@ function codegenForInit(
{
reason: "Expected a variable declaration",
loc: init.loc,
description: null,
description: `Got ${instr.type}`,
suggestions: null,
}
);
@@ -0,0 +1,57 @@
## Input
```javascript
import { useHook } from "shared-runtime";
function Component(props) {
const data = useHook();
const items = [];
// NOTE: `item` is a context variable because it's reassigned and also referenced
// within a closure, the `onClick` handler of each item
for (let key in props.data) {
key = key ?? null; // no-op reassignment to force a context variable
items.push(
<div key={key} onClick={() => data.set(key)}>
{key}
</div>
);
}
return <div>{items}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ data: { a: "a", b: true, c: "hello" } }],
};
```
## Error
```
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 | }
| ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
16 | return <div>{items}</div>;
17 | }
18 |
```
@@ -0,0 +1,22 @@
import { useHook } from "shared-runtime";
function Component(props) {
const data = useHook();
const items = [];
// NOTE: `item` is a context variable because it's reassigned and also referenced
// within a closure, the `onClick` handler of each item
for (let key in props.data) {
key = key ?? null; // no-op reassignment to force a context variable
items.push(
<div key={key} onClick={() => data.set(key)}>
{key}
</div>
);
}
return <div>{items}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ data: { a: "a", b: true, c: "hello" } }],
};
@@ -19,17 +19,13 @@ function Component() {
## Error
```
4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
5 | // within a closure, the `onClick` handler of each item
> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 8 | }
| ^^^^ [ReactForget] Invariant: Expected a variable declaration (6:8)
9 | return items;
10 | }
11 |
4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
5 | // within a closure, the `onClick` handler of each item
> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
| ^^^^^^^^^^^ [ReactForget] Todo: Support for loops where the index variable is a context variable. 'i' is a context variable (6:6)
7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
8 | }
9 | return items;
```
@@ -0,0 +1,57 @@
## Input
```javascript
import { useHook } from "shared-runtime";
function Component(props) {
const data = useHook();
const items = [];
// NOTE: `item` is a context variable because it's reassigned and also referenced
// within a closure, the `onClick` handler of each item
for (let item of props.data) {
item = item ?? {}; // reassignment to force a context variable
items.push(
<div key={item.id} onClick={() => data.set(item)}>
{item.id}
</div>
);
}
return <div>{items}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ data: [{ id: "1" }, { id: "2" }] }],
};
```
## Error
```
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 | }
| ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
16 | return <div>{items}</div>;
17 | }
18 |
```
@@ -0,0 +1,22 @@
import { useHook } from "shared-runtime";
function Component(props) {
const data = useHook();
const items = [];
// NOTE: `item` is a context variable because it's reassigned and also referenced
// within a closure, the `onClick` handler of each item
for (let item of props.data) {
item = item ?? {}; // reassignment to force a context variable
items.push(
<div key={item.id} onClick={() => data.set(item)}>
{item.id}
</div>
);
}
return <div>{items}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ data: [{ id: "1" }, { id: "2" }] }],
};