mirror of
https://github.com/facebook/react.git
synced 2026-02-21 19:31:52 +00:00
[compiler] Phase 1: Add error accumulation infrastructure to Environment
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
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
@@ -777,6 +782,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,
|
||||
@@ -955,6 +966,82 @@ 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);
|
||||
}
|
||||
if (this.logger != null) {
|
||||
this.logger.logEvent(this.filename, {
|
||||
kind: 'CompileError',
|
||||
detail: error,
|
||||
fnLoc: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user