diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts index 8411844626..70c8df8ee0 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -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, } ); diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md new file mode 100644 index 0000000000..712f86617f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md @@ -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( +
data.set(key)}> + {key} +
+ ); + } + return
{items}
; +} + +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 |
data.set(key)}> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 12 | {key} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 13 |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 14 | ); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 15 | } + | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15) + 16 | return
{items}
; + 17 | } + 18 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js new file mode 100644 index 0000000000..34facb7f8f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js @@ -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( +
data.set(key)}> + {key} +
+ ); + } + return
{items}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ data: { a: "a", b: true, c: "hello" } }], +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md index a2fb2d6d60..f342283f83 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md @@ -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( 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( data.set(i)} />); + 8 | } + 9 | return items; ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md new file mode 100644 index 0000000000..3ad92cbd2b --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md @@ -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( +
data.set(item)}> + {item.id} +
+ ); + } + return
{items}
; +} + +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 |
data.set(item)}> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 12 | {item.id} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 13 |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 14 | ); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> 15 | } + | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15) + 16 | return
{items}
; + 17 | } + 18 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js new file mode 100644 index 0000000000..f0ba5094a5 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js @@ -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( +
data.set(item)}> + {item.id} +
+ ); + } + return
{items}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ data: [{ id: "1" }, { id: "2" }] }], +};