Conversation
Add detailed plan for making the React Compiler fault-tolerant by accumulating errors across all passes instead of stopping at the first error. This enables reporting multiple compilation errors at once.
Add error accumulation methods to the Environment class: - #errors field to accumulate CompilerErrors across passes - recordError() to record a single diagnostic (throws if Invariant) - recordErrors() to record all diagnostics from a CompilerError - hasErrors() to check if any errors have been recorded - aggregateErrors() to retrieve the accumulated CompilerError - tryRecord() to wrap callbacks and catch CompilerErrors
…erance - Change runWithEnvironment/run/compileFn to return Result<CodegenFunction, CompilerError> - Wrap all pipeline passes in env.tryRecord() to catch and record CompilerErrors - Record inference pass errors via env.recordErrors() instead of throwing - Handle codegen Result explicitly, returning Err on failure - Add final error check: return Err(env.aggregateErrors()) if any errors accumulated - Update tryCompileFunction and retryCompileFunction in Program.ts to handle Result - Keep lint-only passes using env.logErrors() (non-blocking) - Update 52 test fixture expectations that now report additional errors This is the core integration that enables fault tolerance: errors are caught, recorded, and the pipeline continues to discover more errors.
…rs on env Update 9 validation passes to record errors directly on fn.env instead of returning Result<void, CompilerError>: - validateHooksUsage - validateNoCapitalizedCalls (also changed throwInvalidReact to recordError) - validateUseMemo - dropManualMemoization - validateNoRefAccessInRender - validateNoSetStateInRender - validateNoImpureFunctionsInRender - validateNoFreezingKnownMutableFunctions - validateExhaustiveDependencies Each pass now calls fn.env.recordErrors() instead of returning errors.asResult(). Pipeline.ts call sites updated to remove tryRecord() wrappers and .unwrap().
… tolerance Update remaining validation passes to record errors on env: - validateMemoizedEffectDependencies - validatePreservedManualMemoization - validateSourceLocations (added env parameter) - validateContextVariableLValues (changed throwTodo to recordError) - validateLocalsNotReassignedAfterRender (changed throw to recordError) - validateNoDerivedComputationsInEffects (changed throw to recordError) Update inference passes: - inferMutationAliasingEffects: return void, errors on env - inferMutationAliasingRanges: return Array<AliasingEffect> directly, errors on env Update codegen: - codegenFunction: return CodegenFunction directly, errors on env - codegenReactiveFunction: same pattern Update Pipeline.ts to call all passes directly without tryRecord/unwrap. Also update AnalyseFunctions.ts which called inferMutationAliasingRanges.
Add test fixture demonstrating fault tolerance: the compiler now reports both a mutation error and a ref access error in the same function, where previously only one would be reported before bailing out. Update plan doc to mark all phases as complete.
Greptile SummaryThis PR adds fault tolerance to the React Compiler pipeline, enabling it to continue running all passes and report multiple errors at once rather than stopping at the first error. This is a mirror of react/react#35848.
Confidence Score: 4/5
Important Files Changed
Last reviewed commit: 6337950 |
| let reactiveFunction!: ReactiveFunction; | ||
| env.tryRecord(() => { | ||
| reactiveFunction = buildReactiveFunction(hir); | ||
| }); |
There was a problem hiding this comment.
Uninitialized variable after caught error
If buildReactiveFunction(hir) throws a non-invariant CompilerError (e.g. a Todo error — BuildReactiveFunction.ts has several CompilerError.throwTodo() calls), tryRecord will catch it and record the error. However, reactiveFunction will remain uninitialized at runtime because the assignment inside the callback never executed. The !: definite assignment assertion silences TypeScript but doesn't protect at runtime.
Subsequent code on line 471 (assertWellFormedBreakTargets(reactiveFunction)) and all later passes will operate on an undefined value, likely crashing with a confusing error that masks the original problem.
Consider initializing with a safe default before the tryRecord block (similar to how fbtOperands and uniqueIdentifiers are handled), or checking whether reactiveFunction was assigned before continuing with subsequent passes.
Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
Line: 461-464
Comment:
**Uninitialized variable after caught error**
If `buildReactiveFunction(hir)` throws a non-invariant `CompilerError` (e.g. a `Todo` error — `BuildReactiveFunction.ts` has several `CompilerError.throwTodo()` calls), `tryRecord` will catch it and record the error. However, `reactiveFunction` will remain uninitialized at runtime because the assignment inside the callback never executed. The `!:` definite assignment assertion silences TypeScript but doesn't protect at runtime.
Subsequent code on line 471 (`assertWellFormedBreakTargets(reactiveFunction)`) and all later passes will operate on an `undefined` value, likely crashing with a confusing error that masks the original problem.
Consider initializing with a safe default before the `tryRecord` block (similar to how `fbtOperands` and `uniqueIdentifiers` are handled), or checking whether `reactiveFunction` was assigned before continuing with subsequent passes.
How can I resolve this? If you propose a fix, please make it concise.| if ( | ||
| (detail instanceof CompilerDiagnostic | ||
| ? detail.category | ||
| : detail.category) === ErrorCategory.Invariant | ||
| ) { |
There was a problem hiding this comment.
Redundant ternary condition
Both branches of this ternary access detail.category, making the conditional unnecessary. This was likely intended to differentiate between CompilerDiagnostic and CompilerErrorDetail, but since both have a .category property, the ternary is a no-op.
| if ( | |
| (detail instanceof CompilerDiagnostic | |
| ? detail.category | |
| : detail.category) === ErrorCategory.Invariant | |
| ) { | |
| detail.category === ErrorCategory.Invariant |
Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
Line: 220-224
Comment:
**Redundant ternary condition**
Both branches of this ternary access `detail.category`, making the conditional unnecessary. This was likely intended to differentiate between `CompilerDiagnostic` and `CompilerErrorDetail`, but since both have a `.category` property, the ternary is a no-op.
```suggestion
detail.category === ErrorCategory.Invariant
```
How can I resolve this? If you propose a fix, please make it concise.|
Upstream PR was closed or merged. Code is synced via branch mirror. |
Mirror of facebook/react#35848
Original author: josephsavona
Stack created with Sapling. Best reviewed with ReviewStack.