5We hope to add fuzzing capabilities to Sprout, synthesizing sets of program inputs based on type and/or effect annotations.
6
7▶Sprout is now enabled for all fixtures! If Sprout cannot execute your fixture due to some technical limitations, add your fixture to [`SproutTodoFilter.ts`](./src/SproutTodoFilter.ts) with a comment explaining why.
8
9### Sprout CLI
· · ·
58
59#### Notes
60▶- If your fixture needs to import from an external module, we currently only support importing from `react` (see Milestones todo list).
61
62- Any fixture can use React hooks, but they need to be first imported. We may later enforce that only `isComponent: true` fixtures can use React hooks.
· · ·
69- Sprout currently runs each fixture in an iife to prevent variable collisions, but it does not run fixtures in isolation. Please do not mutate any external state in fixtures.
70
71▶- Sprout does not run fixtures listed in [`SproutTodoFilter.ts`](./src/SproutTodoFilter.ts), even in filter mode.
72
73### Milestones:
· · ·
75- [✅] Make Sprout CLI -runnable and report results in process exit code.
76- [✅] Enable Sprout by default and run it in the Github Actions pipeline.
77▶- [🚧] Make all existing test fixtures Sprout compatible (see `SproutTodoFilter.ts`). This involves each fixture being annotated with `FIXTURE_ENTRYPOINT` and using shared functions and/or defining its own helpers.
78 - 77 done, ~410 to go
79- [✅] *(optional)* Store Sprout output as snapshot files. i.e. each fixture could have a `fixture.js`, `fixture.snap.md`, and `fixture.sprout.md`.
1▶# Rust port: e2e parity TODO
2
3Status snapshot (after the current stack lands):
· · ·
36 local or context"). Rust handles the same code without tripping the
37 invariant.
38▶- `error.todo-jsx-intrinsic-tag-matches-local-binding.js` — SWC pipeline
39 emits a Todo bailout (`[hoisting] EnterSSA: Expected identifier to be
40 defined before being used`) that the Babel path does not.
· · ·
39▶ emits a Todo bailout (`[hoisting] EnterSSA: Expected identifier to be
40 defined before being used`) that the Babel path does not.
41- `error.todo-repro-named-function-with-shadowed-local-same-name.js` —
· · ·
41▶- `error.todo-repro-named-function-with-shadowed-local-same-name.js` —
42 Babel errors; SWC compiles.
43- `new-mutability/error.todo-repro-named-function-with-shadowed-local-same-name.js`
· · ·
43▶- `new-mutability/error.todo-repro-named-function-with-shadowed-local-same-name.js`
44 — same as above with the new mutation-aliasing model enabled.
45- `error.todo-rust-as-expression-assignment-target.tsx` — Babel errors;
+ 10 more matches in this file
30Context variables cannot be destructured using the `Destructure` instruction.
31
32▶**Error (Todo):**
33```
34Support destructuring of context variables
· · ·
36
37### Rule 3: Unhandled Instruction Variants
38▶If an instruction has lvalues that the pass does not handle, it throws a Todo error.
39
40**Error (Todo):**
· · ·
40▶**Error (Todo):**
41```
42ValidateContextVariableLValues: unhandled instruction variant
· · ·
103 // Check for destructuring of context variable
104 if (prev.kind === 'destructure' || kind === 'destructure') {
105▶ CompilerError.throwTodo({
106 reason: `Support destructuring of context variables`,
107 ...
· · ·
125
126### Destructuring Patterns
127▶Each operand in a destructure pattern is visited individually, marked as 'destructure' kind. If the same identifier was previously used as a context variable, a Todo error is thrown since destructuring of context variables is not yet supported.
128
129### Update Expressions
+ 6 more matches in this file
112Multiple node types can share the same location (e.g., a `VariableDeclarator` and its `Identifier` child). The pass tracks all node types for each location.
113
114▶## TODOs
115From the file documentation:
116> There's one big gotcha with this validation: it only works if the "important" original nodes are not optimized away by the compiler.
· · ·
120## Example
121
122▶### Fixture: `error.todo-missing-source-locations.js`
123
124**Input:**
· · ·
173Found 25 errors:
174
175▶Todo: Important source location missing in generated code
176Source location for Identifier is missing in the generated output...
177
· · ·
178▶error.todo-missing-source-locations.ts:4:9
179> 4 | function Component({prop1, prop2}) {
180 | ^^^^^^^^^
· · ·
181
182▶Todo: Important source location missing in generated code
183Source location for VariableDeclaration is missing in the generated output...
184
+ 4 more matches in this file
1016. **Array.push and Similar**: Uses legacy signature system with `Store` effect on receiver and `Capture` of arguments.
102
103▶## TODOs
104- `// TODO: using InstructionValue as a bit of a hack, but it's pragmatic` - context variable initialization
105- `// TODO: call applyEffect() instead` - try-catch aliasing
· · ·
104▶- `// TODO: using InstructionValue as a bit of a hack, but it's pragmatic` - context variable initialization
105- `// TODO: call applyEffect() instead` - try-catch aliasing
106- `// TODO: make sure we're also validating against global mutations somewhere` - global mutation validation for effects/event handlers
· · ·
105▶- `// TODO: call applyEffect() instead` - try-catch aliasing
106- `// TODO: make sure we're also validating against global mutations somewhere` - global mutation validation for effects/event handlers
107- `// TODO; include "render" here?` - whether to track Render effects in function hasTrackedSideEffects
· · ·
106▶- `// TODO: make sure we're also validating against global mutations somewhere` - global mutation validation for effects/event handlers
107- `// TODO; include "render" here?` - whether to track Render effects in function hasTrackedSideEffects
108- `// TODO: consider using persistent data structures to make clone cheaper` - performance optimization for state cloning
· · ·
107▶- `// TODO; include "render" here?` - whether to track Render effects in function hasTrackedSideEffects
108- `// TODO: consider using persistent data structures to make clone cheaper` - performance optimization for state cloning
109- `// TODO check this` and `// TODO: what kind here???` - DeclareLocal value kinds
+ 2 more matches in this file
86
87### Finally blocks
88▶The validation currently has TODOs for handling try/catch/finally properly. Files like `error.todo-invalid-jsx-in-try-with-finally.js` indicate these are known unsupported cases.
89
90## TODOs
· · ·
90▶## TODOs
91Based on fixture naming patterns:
92- `error.todo-invalid-jsx-in-try-with-finally.js` - Try blocks with finally clauses
· · ·
92▶- `error.todo-invalid-jsx-in-try-with-finally.js` - Try blocks with finally clauses
93- `error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js` - Nested try/catch in try with finally
94
· · ·
93▶- `error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js` - Nested try/catch in try with finally
94
95## Example
37 - Patterns (object/array): create temporary Place, then emit destructuring assignments
38 - Rest elements: wrap in SpreadPattern
39▶ - Unsupported: emit Todo error
40
413. **Body Processing**:
· · ·
1088. **Unsupported Syntax**: `var` declarations, `with` statements, inline `class` declarations, `eval` - emit appropriate errors
109
110▶## TODOs
111- `returnTypeAnnotation: null, // TODO: extract the actual return type node if present`
112- `TODO(gsn): In the future, we could only pass in the context identifiers that are actually used by this function and its nested functions`
· · ·
111▶- `returnTypeAnnotation: null, // TODO: extract the actual return type node if present`
112- `TODO(gsn): In the future, we could only pass in the context identifiers that are actually used by this function and its nested functions`
113- Multiple `// TODO remove type cast` in destructuring pattern handling
· · ·
112▶- `TODO(gsn): In the future, we could only pass in the context identifiers that are actually used by this function and its nested functions`
113- Multiple `// TODO remove type cast` in destructuring pattern handling
114- `// TODO: should JSX namespaced names be handled here as well?`
· · ·
113▶- Multiple `// TODO remove type cast` in destructuring pattern handling
114- `// TODO: should JSX namespaced names be handled here as well?`
115
+ 1 more matches in this file
71- `StoreContext` with `Const` kind does propagate the rvalue type to enable ref inference through context variables
72
73▶## TODOs
741. **Hook vs Function type ambiguity**:
75 > "TODO: callee could be a hook or a function, so this type equation isn't correct. We should change Hook to a subtype of Function or change unifier logic."
· · ·
75▶ > "TODO: callee could be a hook or a function, so this type equation isn't correct. We should change Hook to a subtype of Function or change unifier logic."
76
772. **PropertyStore rvalue inference**:
· · ·
78▶ > "TODO: consider using the rvalue type here" - Currently uses a dummy type for PropertyStore to avoid inferring rvalue types from lvalue assignments.
79
80## Example
72
73### Value Blocks with DCE
74▶There's a TODO for handling reassignment in value blocks where the original declaration was removed by DCE.
75
76### Parameters and Context Variables
· · ·
80`++x` and `x--` always mark the variable as `Let`, even if used inline.
81
82▶## TODOs
83```typescript
84CompilerError.invariant(block.kind !== 'value', {
· · ·
85▶ reason: `TODO: Handle reassignment in a value block where the original
86 declaration was removed by dead code elimination (DCE)`,
87 ...
106The terminal's successor might be a block (not value block), which is handled specially.
107
108▶## TODOs
1091. `// TODO: consider pruning activeScopes per instruction` - Currently, `activeScopes` is only pruned at block start points. Some scopes may no longer be active by the time a goto is encountered.
110
· · ·
109▶1. `// TODO: consider pruning activeScopes per instruction` - Currently, `activeScopes` is only pruned at block start points. Some scopes may no longer be active by the time a goto is encountered.
110
1112. `// TODO: add a variant of eachTerminalSuccessor() that visits _all_ successors, not just those that are direct successors for normal control-flow ordering.` - The current implementation uses `mapTerminalSuccessors` which may not visit all successors in all cases.
· · ·
111▶2. `// TODO: add a variant of eachTerminalSuccessor() that visits _all_ successors, not just those that are direct successors for normal control-flow ordering.` - The current implementation uses `mapTerminalSuccessors` which may not visit all successors in all cases.
112
113## Example
45- **Immutable captures**: `ImmutableCapture`, `Freeze`, `Create`, `Impure`, `Render` effects do not contribute to marking context variables as `Capture`
46
47▶## TODOs
48- No TODO comments in the pass itself
49
· · ·
48▶- No TODO comments in the pass itself
49
50## Example
9export const FIXTURE_ENTRYPOINT = {
10 fn: foo,
11▶ params: ['TodoAdd'],
12 isComponent: 'TodoAdd',
13};
· · ·
12▶ isComponent: 'TodoAdd',
13};
14
· · ·
24export const FIXTURE_ENTRYPOINT = {
25 fn: foo,
26▶ params: ["TodoAdd"],
27 isComponent: "TodoAdd",
28};
· · ·
27▶ isComponent: "TodoAdd",
28};
29
10export const FIXTURE_ENTRYPOINT = {
11 fn: f,
12▶ params: ['TodoAdd'],
13 isComponent: 'TodoAdd',
14};
· · ·
13▶ isComponent: 'TodoAdd',
14};
15
· · ·
26export const FIXTURE_ENTRYPOINT = {
27 fn: f,
28▶ params: ["TodoAdd"],
29 isComponent: "TodoAdd",
30};
· · ·
29▶ isComponent: "TodoAdd",
30};
31
7The Rust port should be structurally as close to the TypeScript as possible: viewing the TS and Rust side by side, the logic should look, read, and feel similar while working naturally in Rust.
8
9▶**Current status**: M1-M13 fully implemented. All statement types, expression types, destructuring, function expressions, JSX, switch/try-catch, for-of/in, optional chaining, and recursive lowering are complete. No `todo!()` stubs remain. `cargo check` passes. Remaining work: test against fixtures and fix divergences from TypeScript output.
10
11**Known issues to fix:**
· · ·
232Following the port notes:
233- `CompilerError.invariant(cond, ...)` → `if !cond { panic!(...) }` or dedicated `compiler_invariant!` macro
234▶- `CompilerError.throwTodo(...)` → `return Err(CompilerDiagnostic::todo(...))`
235- `builder.recordError(...)` → `builder.record_error(...)` (accumulates on Environment)
236- Non-null assertions (`!`) → `.unwrap()` or `.expect("...")`
· · ·
238The `lower()` function returns `Result<HirFunction, CompilerError>` for invariant/thrown errors, while accumulated errors go to `env.errors`.
239
240▶### 6. `todo!()` Strategy for Incremental Implementation
241
242BuildHIR is too large (4555 lines) for a single implementation pass. Use Rust's `todo!()` macro to stub unimplemented branches:
· · ·
242▶BuildHIR is too large (4555 lines) for a single implementation pass. Use Rust's `todo!()` macro to stub unimplemented branches:
243
244```rust
· · ·
249 ast::Statement::BlockStatement(s) => lower_block_statement(builder, s),
250 // Stubbed — will be filled in later milestones
251▶ ast::Statement::ForStatement(_) => todo!("lower ForStatement"),
252 ast::Statement::WhileStatement(_) => todo!("lower WhileStatement"),
253 ast::Statement::SwitchStatement(_) => todo!("lower SwitchStatement"),
+ 12 more matches in this file
10export const FIXTURE_ENTRYPOINT = {
11 fn: t,
12▶ params: ['TodoAdd'],
13 isComponent: 'TodoAdd',
14};
· · ·
13▶ isComponent: 'TodoAdd',
14};
15
· · ·
26export const FIXTURE_ENTRYPOINT = {
27 fn: t,
28▶ params: ["TodoAdd"],
29 isComponent: "TodoAdd",
30};
· · ·
29▶ isComponent: "TodoAdd",
30};
31
10export const FIXTURE_ENTRYPOINT = {
11 fn: t,
12▶ params: ['TodoAdd'],
13 isComponent: 'TodoAdd',
14};
· · ·
13▶ isComponent: 'TodoAdd',
14};
15
· · ·
26export const FIXTURE_ENTRYPOINT = {
27 fn: t,
28▶ params: ["TodoAdd"],
29 isComponent: "TodoAdd",
30};
· · ·
29▶ isComponent: "TodoAdd",
30};
31
11export const FIXTURE_ENTRYPOINT = {
12 fn: component,
13▶ params: ['TodoAdd'],
14 isComponent: 'TodoAdd',
15};
· · ·
14▶ isComponent: 'TodoAdd',
15};
16
· · ·
27export const FIXTURE_ENTRYPOINT = {
28 fn: component,
29▶ params: ["TodoAdd"],
30 isComponent: "TodoAdd",
31};
· · ·
30▶ isComponent: "TodoAdd",
31};
32
14export const FIXTURE_ENTRYPOINT = {
15 fn: foo,
16▶ params: ['TodoAdd'],
17 isComponent: 'TodoAdd',
18};
· · ·
17▶ isComponent: 'TodoAdd',
18};
19
· · ·
29export const FIXTURE_ENTRYPOINT = {
30 fn: foo,
31▶ params: ["TodoAdd"],
32 isComponent: "TodoAdd",
33};
· · ·
32▶ isComponent: "TodoAdd",
33};
34
10export const FIXTURE_ENTRYPOINT = {
11 fn: Foo,
12▶ params: ['TodoAdd'],
13 isComponent: 'TodoAdd',
14};
· · ·
13▶ isComponent: 'TodoAdd',
14};
15
· · ·
26export const FIXTURE_ENTRYPOINT = {
27 fn: Foo,
28▶ params: ["TodoAdd"],
29 isComponent: "TodoAdd",
30};
· · ·
29▶ isComponent: "TodoAdd",
30};
31
12export const FIXTURE_ENTRYPOINT = {
13 fn: foo,
14▶ params: ['TodoAdd'],
15 isComponent: 'TodoAdd',
16};
· · ·
15▶ isComponent: 'TodoAdd',
16};
17
· · ·
31export const FIXTURE_ENTRYPOINT = {
32 fn: foo,
33▶ params: ["TodoAdd"],
34 isComponent: "TodoAdd",
35};
· · ·
34▶ isComponent: "TodoAdd",
35};
36