Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/fault-tolerance-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Note that some errors may continue to cause an eager bailout:

Add error accumulation to the `Environment` class so that any pass can record errors during compilation without halting.

- [ ] **1.1 Add error accumulator to Environment** (`src/HIR/Environment.ts`)
- [x] **1.1 Add error accumulator to Environment** (`src/HIR/Environment.ts`)
- Add a `#errors: CompilerError` field, initialized in the constructor
- Add a `recordError(error: CompilerDiagnostic | CompilerErrorDetail)` method that:
- If an Invariant-category detail, immediately throw it
Expand All @@ -41,7 +41,7 @@ Add error accumulation to the `Environment` class so that any pass can record er
- Add a `aggregateErrors(): CompilerError` method that returns the accumulated error object
- Consider whether `recordError` should accept the same options as `CompilerError.push()` for convenience (reason, description, severity, loc, etc.)

- [ ] **1.2 Add a `tryRecord` helper on Environment** (`src/HIR/Environment.ts`)
- [x] **1.2 Add a `tryRecord` helper on Environment** (`src/HIR/Environment.ts`)
- Add a `tryRecord(fn: () => void): void` method that wraps a callback in try/catch:
- If `fn` throws a `CompilerError` that is NOT an invariant, record it via `recordError`
- If `fn` throws a non-CompilerError or a CompilerError invariant, re-throw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import * as t from '@babel/types';
import {ZodError, z} from 'zod/v4';
import {fromZodError} from 'zod-validation-error/v4';
import {CompilerError} from '../CompilerError';
import {
CompilerDiagnostic,
CompilerError,
CompilerErrorDetail,
ErrorCategory,
} from '../CompilerError';
import {CompilerOutputMode, Logger, ProgramContext} from '../Entrypoint';
import {Err, Ok, Result} from '../Utils/Result';
import {
Expand Down Expand Up @@ -545,6 +550,12 @@ export class Environment {

#flowTypeEnvironment: FlowTypeEnv | null;

/**
* Accumulated compilation errors. Passes record errors here instead of
* throwing, so the pipeline can continue and report all errors at once.
*/
#errors: CompilerError = new CompilerError();

constructor(
scope: BabelScope,
fnType: ReactFunctionType,
Expand Down Expand Up @@ -702,6 +713,75 @@ export class Environment {
}
}

/**
* Record a single diagnostic or error detail on this environment.
* If the error is an Invariant, it is immediately thrown since invariants
* represent internal bugs that cannot be recovered from.
* Otherwise, the error is accumulated and optionally logged.
*/
recordError(error: CompilerDiagnostic | CompilerErrorDetail): void {
if (error.category === ErrorCategory.Invariant) {
const compilerError = new CompilerError();
if (error instanceof CompilerDiagnostic) {
compilerError.pushDiagnostic(error);
} else {
compilerError.pushErrorDetail(error);
}
throw compilerError;
}
if (error instanceof CompilerDiagnostic) {
this.#errors.pushDiagnostic(error);
} else {
this.#errors.pushErrorDetail(error);
}
}

/**
* Record all diagnostics from a CompilerError onto this environment.
*/
recordErrors(error: CompilerError): void {
for (const detail of error.details) {
this.recordError(detail);
}
}

/**
* Returns true if any errors have been recorded during compilation.
*/
hasErrors(): boolean {
return this.#errors.hasAnyErrors();
}

/**
* Returns the accumulated CompilerError containing all recorded diagnostics.
*/
aggregateErrors(): CompilerError {
return this.#errors;
}

/**
* Wraps a callback in try/catch: if the callback throws a CompilerError
* that is NOT an invariant, the error is recorded and execution continues.
* Non-CompilerError exceptions and invariants are re-thrown.
*/
tryRecord(fn: () => void): void {
try {
fn();
} catch (err) {
if (err instanceof CompilerError) {
// Check if any detail is an invariant — if so, re-throw
for (const detail of err.details) {
if (detail.category === ErrorCategory.Invariant) {
throw err;
}
}
this.recordErrors(err);
} else {
throw err;
}
}
}

isContextIdentifier(node: t.Identifier): boolean {
return this.#contextIdentifiers.has(node);
}
Expand Down
Loading