[compiler] Phase 3: Make lower() always produce HIRFunction (#35878)
--- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35878). * #35888 * #35884 * #35883 * #35882 * #35881 * #35880 * #35879 * __->__ #35878
Joseph Savona committed
Feb 23, 2026 at 16:05 UTC
d6558f36e2f1de6d0504de0fc6ed2f4f621aa655
10 files changed
+207
-262
compiler/fault-tolerance-overview.md
+35
-32
@@ -75,49 +75,49 @@ Change `runWithEnvironment` to run all passes and check for errors at the end in
75
76
Currently `lower()` returns `Result<HIRFunction, CompilerError>`. It already accumulates errors internally via `builder.errors`, but returns `Err` when errors exist. Change it to always return `Ok(hir)` while recording errors on the environment.
77
78
-- [ ] **3.1 Change `lower` to always return HIRFunction** (`src/HIR/BuildHIR.ts`)
78
+- [x] **3.1 Change `lower` to always return HIRFunction** (`src/HIR/BuildHIR.ts`)
79
- Change return type from `Result<HIRFunction, CompilerError>` to `HIRFunction`
80
- - Instead of returning `Err(builder.errors)` at line 227-229, record errors on `env` via `env.recordError(builder.errors)` and return the (partial) HIR
80
+ - Instead of returning `Err(builder.errors)` at line 227-229, record errors on `env` via `env.recordErrors(builder.errors)` and return the (partial) HIR
81
- Update the pipeline to call `lower(func, env)` directly instead of `lower(func, env).unwrap()`
82
+ - Added try/catch around body lowering to catch thrown CompilerErrors (e.g., from `resolveBinding`) and record them
83
83
-- [ ] **3.2 Handle `var` declarations as `let`** (`src/HIR/BuildHIR.ts`, line ~855)
84
- - Currently throws `Todo("Handle var kinds in VariableDeclaration")`
85
- - Instead: record the Todo error on env, then treat the `var` as `let` and continue lowering
84
+- [x] **3.2 Handle `var` declarations as `let`** (`src/HIR/BuildHIR.ts`, line ~855)
85
+ - Record the Todo error, then treat `var` as `let` and continue lowering (instead of skipping the declaration)
86
87
-- [ ] **3.3 Handle `try/finally` by pruning `finally`** (`src/HIR/BuildHIR.ts`, lines ~1281-1296)
88
- - Currently throws Todo for `try` without `catch` and `try` with `finally`
89
- - Instead: record the Todo error, then lower the `try/catch` portion only (put the `finally` block content in the fallthrough of the try/catch)
87
+- [x] **3.3 Handle `try/finally` by pruning `finally`** (`src/HIR/BuildHIR.ts`, lines ~1281-1296)
88
+ - Already handled: `try` without `catch` pushes error and returns; `try` with `finally` pushes error and continues with `try/catch` portion only
89
91
-- [ ] **3.4 Handle `eval()` via UnsupportedNode** (`src/HIR/BuildHIR.ts`, line ~3568)
92
- - Currently throws `UnsupportedSyntax("The 'eval' function is not supported")`
93
- - Instead: record the error, emit an `UnsupportedNode` instruction value with the original AST node
90
+- [x] **3.4 Handle `eval()` via UnsupportedNode** (`src/HIR/BuildHIR.ts`, line ~3568)
91
+ - Already handled: records error via `builder.errors.push()` and continues
92
95
-- [ ] **3.5 Handle `with` statement via UnsupportedNode** (`src/HIR/BuildHIR.ts`, line ~1382)
96
- - Currently throws `UnsupportedSyntax`
97
- - Instead: record the error, emit the body statements as-is (or skip them), continue
93
+- [x] **3.5 Handle `with` statement via UnsupportedNode** (`src/HIR/BuildHIR.ts`, line ~1382)
94
+ - Already handled: records error and emits `UnsupportedNode`
95
99
-- [ ] **3.6 Handle inline `class` declarations** (`src/HIR/BuildHIR.ts`, line ~1402)
100
- - Currently throws `UnsupportedSyntax`
101
- - Already creates an `UnsupportedNode`; just record the error instead of throwing
96
+- [x] **3.6 Handle inline `class` declarations** (`src/HIR/BuildHIR.ts`, line ~1402)
97
+ - Already handled: records error and emits `UnsupportedNode`
98
103
-- [ ] **3.7 Handle remaining Todo errors in expression lowering** (`src/HIR/BuildHIR.ts`)
104
- - For each of the ~35 Todo error sites in `lowerExpression`, `lowerAssignment`, `lowerMemberExpression`, etc.:
105
- - Record the Todo error on the environment
106
- - Emit an `UnsupportedNode` instruction value with the original Babel AST node as fallback
107
- - Key sites include: pipe operator, tagged templates with interpolations, compound logical assignment (`&&=`, `||=`, `??=`), `for await...of`, object getters/setters, UpdateExpression on context variables, complex destructuring patterns
108
- - The `UnsupportedNode` variant already exists in HIR and passes through codegen unchanged, so no new HIR types are needed for most cases
99
+- [x] **3.7 Handle remaining Todo errors in expression lowering** (`src/HIR/BuildHIR.ts`)
100
+ - Already handled: all ~60 error sites use `builder.errors.push()` to accumulate errors. The try/catch around body lowering provides a safety net for any that still throw.
101
110
-- [ ] **3.8 Handle `throw` inside `try/catch`** (`src/HIR/BuildHIR.ts`, line ~284)
111
- - Currently throws Todo
112
- - Instead: record the error, and represent the `throw` as a terminal that ends the block (the existing `throw` terminal type may already handle this, or we can use `UnsupportedNode`)
102
+- [x] **3.8 Handle `throw` inside `try/catch`** (`src/HIR/BuildHIR.ts`, line ~284)
103
+ - Already handled: records error via `builder.errors.push()` and continues
104
114
-- [ ] **3.9 Handle `for` loops with missing test or expression init** (`src/HIR/BuildHIR.ts`, lines ~559, ~632)
115
- - Record the error and construct a best-effort loop HIR (e.g., for `for(;;)`, use `true` as the test expression)
105
+- [x] **3.9 Handle `for` loops with missing test or expression init** (`src/HIR/BuildHIR.ts`, lines ~559, ~632)
106
+ - For `for(;;)` (missing test): emit `true` as the test expression and add a branch terminal
107
+ - For empty init (`for (; ...)`): add a placeholder instruction to avoid invariant about empty blocks
108
+ - For expression init (`for (expr; ...)`): record error and lower the expression as best-effort
109
+ - Changed `'unsupported'` terminal to `'goto'` terminal for non-variable init to maintain valid CFG structure
110
117
-- [ ] **3.10 Handle nested function lowering failures** (`src/HIR/BuildHIR.ts`, `lowerFunction` at line ~3504)
118
- - Currently calls `lower()` recursively and merges errors if it fails (`builder.errors.merge(functionErrors)`)
119
- - With the new approach, the nested `lower()` always returns an HIR, but errors are recorded on the shared environment
120
- - Ensure the parent function continues lowering even if a nested function had errors
111
+- [x] **3.10 Handle nested function lowering failures** (`src/HIR/BuildHIR.ts`, `lowerFunction` at line ~3504)
112
+ - `lowerFunction()` now always returns `LoweredFunction` since `lower()` always returns `HIRFunction`
113
+ - Errors from nested functions are recorded on the shared environment
114
+ - Removed the `null` return case and the corresponding `UnsupportedNode` fallback in callers
115
+
116
+- [x] **3.11 Handle unreachable functions in `build()`** (`src/HIR/HIRBuilder.ts`, `build()`)
117
+ - Changed `CompilerError.throwTodo()` for unreachable code with hoisted declarations to `this.errors.push()` to allow HIR construction to complete
118
+
119
+- [x] **3.12 Handle duplicate fbt tags** (`src/HIR/BuildHIR.ts`, line ~2279)
120
+ - Changed `CompilerError.throwDiagnostic()` to `builder.errors.pushDiagnostic()` to record instead of throw
121
122
### Phase 4: Update Validation Passes
123
@@ -324,4 +324,7 @@ Walk through `runWithEnvironment` and wrap each pass call site. This is the inte
324
* **Lint-only passes (Pattern B: `env.logErrors()`) should not use `tryRecord()`/`recordError()`** because those errors are intentionally non-blocking. They are reported via the logger only and should not cause the pipeline to return `Err`. The `logErrors` pattern was kept for `validateNoDerivedComputationsInEffects_exp`, `validateNoSetStateInEffects`, `validateNoJSXInTryStatement`, and `validateStaticComponents`.
325
* **Inference passes that return `Result` with validation errors** (`inferMutationAliasingEffects`, `inferMutationAliasingRanges`) were changed to record errors via `env.recordErrors()` instead of throwing, allowing subsequent passes to proceed.
326
* **Value-producing passes** (`memoizeFbtAndMacroOperandsInSameScope`, `renameVariables`, `buildReactiveFunction`) need safe default values when wrapped in `tryRecord()` since the callback can't return values. We initialize with empty defaults (e.g., `new Set()`) before the `tryRecord()` call.
327
+* **Phase 3 (BuildHIR) revealed that most error sites already used `builder.errors.push()` for accumulation.** The existing lowering code was designed to accumulate errors rather than throw. The main changes were: (1) changing `lower()` return type from `Result` to `HIRFunction`, (2) recording builder errors on env, (3) adding a try/catch around body lowering to catch thrown CompilerErrors from sub-calls like `resolveBinding()`, (4) treating `var` as `let` instead of skipping declarations, and (5) fixing ForStatement init/test handling to produce valid CFG structure.
328
+* **Partial HIR can trigger downstream invariants.** When lowering skips or partially handles constructs (e.g., unreachable hoisted functions, `var` declarations before the fix), downstream passes like `InferMutationAliasingEffects` may encounter uninitialized identifiers and throw invariants. This is acceptable since the function still correctly bails out of compilation, but error messages may be less specific. The fix for `var` (treating as `let`) demonstrates how to avoid this: continue lowering with a best-effort representation rather than skipping entirely.
329
+* **Errors accumulated on `env` are lost when an invariant propagates out of the pipeline.** Since invariant CompilerErrors always re-throw through `tryRecord()`, they exit the pipeline as exceptions. The caller only sees the invariant error, not any errors previously recorded on `env`. This is a design limitation that could be addressed by aggregating env errors with caught exceptions in `tryCompileFunction()`.
330
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+1
-1
@@ -155,7 +155,7 @@ function runWithEnvironment(
155
const log = (value: CompilerPipelineValue): void => {
156
env.logger?.debugLogIRs?.(value);
157
};
158
- const hir = lower(func, env).unwrap();
158
+ const hir = lower(func, env);
159
log({kind: 'hir', name: 'HIR', value: hir});
160
161
pruneMaybeThrows(hir);
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+117
-65
@@ -14,7 +14,6 @@ import {
14
CompilerSuggestionOperation,
15
ErrorCategory,
16
} from '../CompilerError';
17
-import {Err, Ok, Result} from '../Utils/Result';
17
import {assertExhaustive, hasNode} from '../Utils/utils';
18
import {Environment} from './Environment';
19
import {
@@ -75,7 +74,7 @@ export function lower(
74
// Bindings captured from the outer function, in case lower() is called recursively (for lambdas)
75
bindings: Bindings | null = null,
76
capturedRefs: Map<t.Identifier, SourceLocation> = new Map(),
78
-): Result<HIRFunction, CompilerError> {
77
+): HIRFunction {
78
const builder = new HIRBuilder(env, {
79
bindings,
80
context: capturedRefs,
@@ -186,32 +185,51 @@ export function lower(
185
186
let directives: Array<string> = [];
187
const body = func.get('body');
189
- if (body.isExpression()) {
190
- const fallthrough = builder.reserve('block');
191
- const terminal: ReturnTerminal = {
192
- kind: 'return',
193
- returnVariant: 'Implicit',
194
- loc: GeneratedSource,
195
- value: lowerExpressionToTemporary(builder, body),
196
- id: makeInstructionId(0),
197
- effects: null,
198
- };
199
- builder.terminateWithContinuation(terminal, fallthrough);
200
- } else if (body.isBlockStatement()) {
201
- lowerStatement(builder, body);
202
- directives = body.get('directives').map(d => d.node.value.value);
203
- } else {
204
- builder.errors.pushDiagnostic(
205
- CompilerDiagnostic.create({
206
- category: ErrorCategory.Syntax,
207
- reason: `Unexpected function body kind`,
208
- description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,
209
- }).withDetails({
210
- kind: 'error',
211
- loc: body.node.loc ?? null,
212
- message: 'Expected a block statement or expression',
213
- }),
214
- );
188
+ try {
189
+ if (body.isExpression()) {
190
+ const fallthrough = builder.reserve('block');
191
+ const terminal: ReturnTerminal = {
192
+ kind: 'return',
193
+ returnVariant: 'Implicit',
194
+ loc: GeneratedSource,
195
+ value: lowerExpressionToTemporary(builder, body),
196
+ id: makeInstructionId(0),
197
+ effects: null,
198
+ };
199
+ builder.terminateWithContinuation(terminal, fallthrough);
200
+ } else if (body.isBlockStatement()) {
201
+ lowerStatement(builder, body);
202
+ directives = body.get('directives').map(d => d.node.value.value);
203
+ } else {
204
+ builder.errors.pushDiagnostic(
205
+ CompilerDiagnostic.create({
206
+ category: ErrorCategory.Syntax,
207
+ reason: `Unexpected function body kind`,
208
+ description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,
209
+ }).withDetails({
210
+ kind: 'error',
211
+ loc: body.node.loc ?? null,
212
+ message: 'Expected a block statement or expression',
213
+ }),
214
+ );
215
+ }
216
+ } catch (err) {
217
+ if (err instanceof CompilerError) {
218
+ // Re-throw invariant errors immediately
219
+ for (const detail of err.details) {
220
+ if (
221
+ (detail instanceof CompilerDiagnostic
222
+ ? detail.category
223
+ : detail.category) === ErrorCategory.Invariant
224
+ ) {
225
+ throw err;
226
+ }
227
+ }
228
+ // Record non-invariant errors and continue to produce partial HIR
229
+ builder.errors.merge(err);
230
+ } else {
231
+ throw err;
232
+ }
233
}
234
235
let validatedId: HIRFunction['id'] = null;
@@ -224,10 +242,6 @@ export function lower(
242
}
243
}
244
227
- if (builder.errors.hasAnyErrors()) {
228
- return Err(builder.errors);
229
- }
230
-
245
builder.terminate(
246
{
247
kind: 'return',
@@ -244,23 +258,29 @@ export function lower(
258
null,
259
);
260
247
- return Ok({
261
+ const hirBody = builder.build();
262
+
263
+ // Record all accumulated errors (including any from build()) on env
264
+ if (builder.errors.hasAnyErrors()) {
265
+ env.recordErrors(builder.errors);
266
+ }
267
+
268
+ return {
269
id: validatedId,
270
nameHint: null,
271
params,
272
fnType: bindings == null ? env.fnType : 'Other',
273
returnTypeAnnotation: null, // TODO: extract the actual return type node if present
274
returns: createTemporaryPlace(env, func.node.loc ?? GeneratedSource),
254
- body: builder.build(),
275
+ body: hirBody,
276
context,
277
generator: func.node.generator === true,
278
async: func.node.async === true,
279
loc: func.node.loc ?? GeneratedSource,
280
env,
260
- effects: null,
281
aliasingEffects: null,
282
directives,
263
- });
283
+ };
284
}
285
286
// Helper to lower a statement
@@ -555,6 +575,24 @@ function lowerStatement(
575
576
const initBlock = builder.enter('loop', _blockId => {
577
const init = stmt.get('init');
578
+ if (init.node == null) {
579
+ /*
580
+ * No init expression (e.g., `for (; ...)`), add a placeholder to avoid
581
+ * invariant about empty blocks
582
+ */
583
+ lowerValueToTemporary(builder, {
584
+ kind: 'Primitive',
585
+ value: undefined,
586
+ loc: stmt.node.loc ?? GeneratedSource,
587
+ });
588
+ return {
589
+ kind: 'goto',
590
+ block: testBlock.id,
591
+ variant: GotoVariant.Break,
592
+ id: makeInstructionId(0),
593
+ loc: stmt.node.loc ?? GeneratedSource,
594
+ };
595
+ }
596
if (!init.isVariableDeclaration()) {
597
builder.errors.push({
598
reason:
@@ -563,8 +601,14 @@ function lowerStatement(
601
loc: stmt.node.loc ?? null,
602
suggestions: null,
603
});
604
+ // Lower the init expression as best-effort and continue
605
+ if (init.isExpression()) {
606
+ lowerExpressionToTemporary(builder, init as NodePath<t.Expression>);
607
+ }
608
return {
567
- kind: 'unsupported',
609
+ kind: 'goto',
610
+ block: testBlock.id,
611
+ variant: GotoVariant.Break,
612
id: makeInstructionId(0),
613
loc: init.node?.loc ?? GeneratedSource,
614
};
@@ -635,6 +679,23 @@ function lowerStatement(
679
loc: stmt.node.loc ?? null,
680
suggestions: null,
681
});
682
+ // Treat `for(;;)` as `while(true)` to keep the builder state consistent
683
+ builder.terminateWithContinuation(
684
+ {
685
+ kind: 'branch',
686
+ test: lowerValueToTemporary(builder, {
687
+ kind: 'Primitive',
688
+ value: true,
689
+ loc: stmt.node.loc ?? GeneratedSource,
690
+ }),
691
+ consequent: bodyBlock,
692
+ alternate: continuationBlock.id,
693
+ fallthrough: continuationBlock.id,
694
+ id: makeInstructionId(0),
695
+ loc: stmt.node.loc ?? GeneratedSource,
696
+ },
697
+ continuationBlock,
698
+ );
699
} else {
700
builder.terminateWithContinuation(
701
{
@@ -858,10 +919,12 @@ function lowerStatement(
919
loc: stmt.node.loc ?? null,
920
suggestions: null,
921
});
861
- return;
922
+ // Treat `var` as `let` so references to the variable don't break
923
}
924
const kind =
864
- nodeKind === 'let' ? InstructionKind.Let : InstructionKind.Const;
925
+ nodeKind === 'let' || nodeKind === 'var'
926
+ ? InstructionKind.Let
927
+ : InstructionKind.Const;
928
for (const declaration of stmt.get('declarations')) {
929
const id = declaration.get('id');
930
const init = declaration.get('init');
@@ -1494,9 +1557,6 @@ function lowerObjectMethod(
1557
): InstructionValue {
1558
const loc = property.node.loc ?? GeneratedSource;
1559
const loweredFunc = lowerFunction(builder, property);
1497
- if (!loweredFunc) {
1498
- return {kind: 'UnsupportedNode', node: property.node, loc: loc};
1499
- }
1560
1561
return {
1562
kind: 'ObjectMethod',
@@ -2276,18 +2336,20 @@ function lowerExpression(
2336
});
2337
for (const [name, locations] of Object.entries(fbtLocations)) {
2338
if (locations.length > 1) {
2279
- CompilerError.throwDiagnostic({
2280
- category: ErrorCategory.Todo,
2281
- reason: 'Support duplicate fbt tags',
2282
- description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
2283
- details: locations.map(loc => {
2284
- return {
2285
- kind: 'error',
2286
- message: `Multiple \`<${tagName}:${name}>\` tags found`,
2287
- loc,
2288
- };
2339
+ builder.errors.pushDiagnostic(
2340
+ new CompilerDiagnostic({
2341
+ category: ErrorCategory.Todo,
2342
+ reason: 'Support duplicate fbt tags',
2343
+ description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
2344
+ details: locations.map(loc => {
2345
+ return {
2346
+ kind: 'error' as const,
2347
+ message: `Multiple \`<${tagName}:${name}>\` tags found`,
2348
+ loc,
2349
+ };
2350
+ }),
2351
}),
2290
- });
2352
+ );
2353
}
2354
}
2355
}
@@ -3468,9 +3530,6 @@ function lowerFunctionToValue(
3530
const exprNode = expr.node;
3531
const exprLoc = exprNode.loc ?? GeneratedSource;
3532
const loweredFunc = lowerFunction(builder, expr);
3471
- if (!loweredFunc) {
3472
- return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
3473
- }
3533
return {
3534
kind: 'FunctionExpression',
3535
name: loweredFunc.func.id,
@@ -3489,7 +3548,7 @@ function lowerFunction(
3548
| t.FunctionDeclaration
3549
| t.ObjectMethod
3550
>,
3492
-): LoweredFunction | null {
3551
+): LoweredFunction {
3552
const componentScope: Scope = builder.environment.parentFunction.scope;
3553
const capturedContext = gatherCapturedContext(expr, componentScope);
3554
@@ -3501,19 +3560,12 @@ function lowerFunction(
3560
* This isn't a problem in practice because use Babel's scope analysis to
3561
* identify the correct references.
3562
*/
3504
- const lowering = lower(
3563
+ const loweredFunc = lower(
3564
expr,
3565
builder.environment,
3566
builder.bindings,
3567
new Map([...builder.context, ...capturedContext]),
3568
);
3510
- let loweredFunc: HIRFunction;
3511
- if (lowering.isErr()) {
3512
- const functionErrors = lowering.unwrapErr();
3513
- builder.errors.merge(functionErrors);
3514
- return null;
3515
- }
3516
- loweredFunc = lowering.unwrap();
3569
return {
3570
func: loweredFunc,
3571
};
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+2
-1
@@ -381,11 +381,12 @@ export default class HIRBuilder {
381
instr => instr.value.kind === 'FunctionExpression',
382
)
383
) {
384
- CompilerError.throwTodo({
384
+ this.errors.push({
385
reason: `Support functions with unreachable code that may contain hoisted declarations`,
386
loc: block.instructions[0]?.loc ?? block.terminal.loc,
387
description: null,
388
suggestions: null,
389
+ category: ErrorCategory.Todo,
390
});
391
}
392
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md
+3
-12
@@ -24,18 +24,9 @@ function useThing(fn) {
24
```
25
Found 1 error:
26
27
-Compilation Skipped: `this` is not supported syntax
28
-
29
-React Compiler does not support compiling functions that use `this`.
30
-
31
-error.reserved-words.ts:8:28
32
- 6 |
33
- 7 | if (ref.current === null) {
34
-> 8 | ref.current = function (this: unknown, ...args) {
35
- | ^^^^^^^^^^^^^ `this` was used here
36
- 9 | return fnRef.current.call(this, ...args);
37
- 10 | };
38
- 11 | }
27
+Invariant: [HIRBuilder] Unexpected null block
28
+
29
+expected block 0 to exist.
30
```
31
32
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error._todo.computed-lval-in-destructure.expect.md
+8
-7
@@ -17,16 +17,17 @@ function Component(props) {
17
```
18
Found 1 error:
19
20
-Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern
20
+Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
21
22
-error._todo.computed-lval-in-destructure.ts:3:9
23
- 1 | function Component(props) {
24
- 2 | const computedKey = props.key;
25
-> 3 | const {[computedKey]: x} = props.val;
26
- | ^^^^^^^^^^^^^^^^ (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern
22
+<unknown> x$8.
23
+
24
+error._todo.computed-lval-in-destructure.ts:5:9
25
+ 3 | const {[computedKey]: x} = props.val;
26
4 |
28
- 5 | return x;
27
+> 5 | return x;
28
+ | ^ this is uninitialized
29
6 | }
30
+ 7 |
31
```
32
33
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-hoisted-function-in-unreachable-code.expect.md
+9
-6
@@ -18,15 +18,18 @@ function Component() {
18
```
19
Found 1 error:
20
21
-Todo: Support functions with unreachable code that may contain hoisted declarations
21
+Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
22
23
-error.todo-hoisted-function-in-unreachable-code.ts:6:2
23
+<unknown> Foo$0.
24
+
25
+error.todo-hoisted-function-in-unreachable-code.ts:3:10
26
+ 1 | // @compilationMode:"infer"
27
+ 2 | function Component() {
28
+> 3 | return <Foo />;
29
+ | ^^^ this is uninitialized
30
4 |
31
5 | // This is unreachable from a control-flow perspective, but it gets hoisted
26
-> 6 | function Foo() {}
27
- | ^^^^^^^^^^^^^^^^^ Support functions with unreachable code that may contain hoisted declarations
28
- 7 | }
29
- 8 |
32
+ 6 | function Foo() {}
33
```
34
35
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
+6
-131
@@ -79,43 +79,11 @@ let moduleLocal = false;
79
## Error
80
81
```
82
-Found 10 errors:
83
-
84
-Todo: (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration
85
-
86
-error.todo-kitchensink.ts:3:2
87
- 1 | function foo([a, b], {c, d, e = 'e'}, f = 'f', ...args) {
88
- 2 | let i = 0;
89
-> 3 | var x = [];
90
- | ^^^^^^^^^^^ (BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration
91
- 4 |
92
- 5 | class Bar {
93
- 6 | #secretSauce = 42;
94
-
95
-Compilation Skipped: Inline `class` declarations are not supported
96
-
97
-Move class declarations outside of components/hooks.
98
-
99
-error.todo-kitchensink.ts:5:2
100
- 3 | var x = [];
101
- 4 |
102
-> 5 | class Bar {
103
- | ^^^^^^^^^^^
104
-> 6 | #secretSauce = 42;
105
- | ^^^^^^^^^^^^^^^^^^^^^^
106
-> 7 | constructor() {
107
- | ^^^^^^^^^^^^^^^^^^^^^^
108
-> 8 | console.log(this.#secretSauce);
109
- | ^^^^^^^^^^^^^^^^^^^^^^
110
-> 9 | }
111
- | ^^^^^^^^^^^^^^^^^^^^^^
112
-> 10 | }
113
- | ^^^^ Inline `class` declarations are not supported
114
- 11 |
115
- 12 | const g = {b() {}, c: () => {}};
116
- 13 | const {z, aa = 'aa'} = useCustom();
117
-
118
-Todo: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
82
+Found 1 error:
83
+
84
+Invariant: Expected a variable declaration
85
+
86
+Got ExpressionStatement.
87
88
error.todo-kitchensink.ts:20:2
89
18 | const j = function bar([quz, qux], ...args) {};
@@ -125,103 +93,10 @@ error.todo-kitchensink.ts:20:2
93
> 21 | x.push(i);
94
| ^^^^^^^^^^^^^^
95
> 22 | }
128
- | ^^^^ (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
96
+ | ^^^^ Expected a variable declaration
97
23 | for (; i < 3; ) {
98
24 | break;
99
25 | }
132
-
133
-Todo: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
134
-
135
-error.todo-kitchensink.ts:23:2
136
- 21 | x.push(i);
137
- 22 | }
138
-> 23 | for (; i < 3; ) {
139
- | ^^^^^^^^^^^^^^^^^
140
-> 24 | break;
141
- | ^^^^^^^^^^
142
-> 25 | }
143
- | ^^^^ (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
144
- 26 | for (;;) {
145
- 27 | break;
146
- 28 | }
147
-
148
-Todo: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
149
-
150
-error.todo-kitchensink.ts:26:2
151
- 24 | break;
152
- 25 | }
153
-> 26 | for (;;) {
154
- | ^^^^^^^^^^
155
-> 27 | break;
156
- | ^^^^^^^^^^
157
-> 28 | }
158
- | ^^^^ (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
159
- 29 |
160
- 30 | graphql`
161
- 31 | ${g}
162
-
163
-Todo: (BuildHIR::lowerStatement) Handle empty test in ForStatement
164
-
165
-error.todo-kitchensink.ts:26:2
166
- 24 | break;
167
- 25 | }
168
-> 26 | for (;;) {
169
- | ^^^^^^^^^^
170
-> 27 | break;
171
- | ^^^^^^^^^^
172
-> 28 | }
173
- | ^^^^ (BuildHIR::lowerStatement) Handle empty test in ForStatement
174
- 29 |
175
- 30 | graphql`
176
- 31 | ${g}
177
-
178
-Todo: (BuildHIR::lowerExpression) Handle tagged template with interpolations
179
-
180
-error.todo-kitchensink.ts:30:2
181
- 28 | }
182
- 29 |
183
-> 30 | graphql`
184
- | ^^^^^^^^
185
-> 31 | ${g}
186
- | ^^^^^^^^
187
-> 32 | `;
188
- | ^^^^ (BuildHIR::lowerExpression) Handle tagged template with interpolations
189
- 33 |
190
- 34 | graphql`\\t\n`;
191
- 35 |
192
-
193
-Todo: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
194
-
195
-error.todo-kitchensink.ts:34:2
196
- 32 | `;
197
- 33 |
198
-> 34 | graphql`\\t\n`;
199
- | ^^^^^^^^^^^^^^ (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
200
- 35 |
201
- 36 | for (c of [1, 2]) {
202
- 37 | }
203
-
204
-Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `MemberExpression` cannot be safely reordered
205
-
206
-error.todo-kitchensink.ts:57:9
207
- 55 | case foo(): {
208
- 56 | }
209
-> 57 | case x.y: {
210
- | ^^^ (BuildHIR::node.lowerReorderableExpression) Expression type `MemberExpression` cannot be safely reordered
211
- 58 | }
212
- 59 | default: {
213
- 60 | }
214
-
215
-Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `BinaryExpression` cannot be safely reordered
216
-
217
-error.todo-kitchensink.ts:53:9
218
- 51 |
219
- 52 | switch (i) {
220
-> 53 | case 1 + 1: {
221
- | ^^^^^ (BuildHIR::node.lowerReorderableExpression) Expression type `BinaryExpression` cannot be safely reordered
222
- 54 | }
223
- 55 | case foo(): {
224
- 56 | }
100
```
101
102
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useMemo-callback-generator.expect.md
+18
-1
@@ -18,7 +18,7 @@ function component(a, b) {
18
## Error
19
20
```
21
-Found 1 error:
21
+Found 2 errors:
22
23
Todo: (BuildHIR::lowerExpression) Handle YieldExpression expressions
24
@@ -30,6 +30,23 @@ error.useMemo-callback-generator.ts:6:4
30
7 | }, []);
31
8 | return x;
32
9 | }
33
+
34
+Error: useMemo() callbacks may not be async or generator functions
35
+
36
+useMemo() callbacks are called once and must synchronously return a value.
37
+
38
+error.useMemo-callback-generator.ts:5:18
39
+ 3 | // useful for now, but adding this test in case we do
40
+ 4 | // add support for generators in the future.
41
+> 5 | let x = useMemo(function* () {
42
+ | ^^^^^^^^^^^^^^
43
+> 6 | yield a;
44
+ | ^^^^^^^^^^^^
45
+> 7 | }, []);
46
+ | ^^^^ Async and generator functions are not supported
47
+ 8 | return x;
48
+ 9 | }
49
+ 10 |
50
```
51
52
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo.error.object-pattern-computed-key.expect.md
+8
-6
@@ -23,16 +23,18 @@ export const FIXTURE_ENTRYPOINT = {
23
```
24
Found 1 error:
25
26
-Todo: (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern
26
+Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
27
28
-todo.error.object-pattern-computed-key.ts:5:9
29
- 3 | const SCALE = 2;
28
+<unknown> value$3.
29
+
30
+todo.error.object-pattern-computed-key.ts:6:9
31
4 | function Component(props) {
31
-> 5 | const {[props.name]: value} = props;
32
- | ^^^^^^^^^^^^^^^^^^^ (BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern
33
- 6 | return value;
32
+ 5 | const {[props.name]: value} = props;
33
+> 6 | return value;
34
+ | ^^^^^ this is uninitialized
35
7 | }
36
8 |
37
+ 9 | export const FIXTURE_ENTRYPOINT = {
38
```
39
40
\ No newline at end of file