facebook / react · Issue No. 37533
The Rust port of the React Compiler emits CompileError logger events whose detail
shape differs from the TypeScript reference for errors thrown as CompilerError.invariant()
during lowering. Concretely, the Rust event carries the error inside a nesteddetail.details[0] array, while the TS reference places the location directly ondetail.loc. This fails the e2e event comparison for the fixtureerror.bug-invariant-couldnt-find-binding-for-decl.js.
This is the only remaining Rust-vs-TS event-shape failure in the Babel e2e suite
(1807 / 1810 total; the other two failures are the already-documented, accepted
fixture differences in TODO.md Group A).
Full Babel e2e run at HEAD f1f7ed2ac:
Code: 1809/1810 passed Events: 1807/1810 passed Total: 1807/1810 passed
FAIL compiler/.../fixtures/compiler/error.bug-invariant-couldnt-find-binding-for-decl.js
Fixture input:
import {useEffect} from 'react';
export function Foo() {
useEffect(() => {
try {
// do something
} catch ({status}) {
// do something
}
}, []);
}
Raw detail payloads logged by each implementation for the same input:
| field | TS reference | Rust |
|---|---|---|
detail.category |
"Invariant" |
"Invariant" |
detail.reason |
"(BuildHIR::lowerAssignment) Could not find binding for declaration." |
same |
detail.severity |
"Error" |
"Error" |
detail.loc |
{start, end, identifierName: "status"} |
(absent) |
detail.details |
(absent) | [{kind: "error", loc: {start, end}, message: "..."}] |
The emitted code and fnLoc are identical; only the event detail shape diverges.
Both implementations already serialize each diagnostic faithfully — the Rustlog_error/compiler_error_to_info paths incompiler/crates/react_compiler/src/entrypoint/program.rs mirror the TSformatDetailForLogging (src/Entrypoint/Program.ts:177), which mapsCompilerDiagnostic → detail.details[] and CompilerErrorDetail → flat detail.loc.
The divergence originates earlier, in the error-type conversion:
compiler/crates/react_compiler_lowering/src/build_hir.rs:3942
records the catch-binding failure as an CompilerErrorDetail (category Invariant).builder.record_error(...) returns Err(CompilerError) for invariant errors
(compiler/crates/react_compiler_hir/src/environment.rs:331). The enclosing function
returns Result<T, CompilerDiagnostic>, so the ? operator triggers
impl From<CompilerError> for CompilerDiagnostic
(compiler/crates/react_compiler_diagnostics/src/lib.rs:385),
which wraps the ErrorDetail via CompilerDiagnostic::from_detail(...). ACompilerDiagnostic always serializes as the detail.details[] form, so the flat
location that TS preserves on the CompilerErrorDetail is moved into a sub-detail.
In TS, the equivalent CompilerError.invariant() produces a CompilerErrorDetail
(src/HIR/BuildHIR.ts:948), which survives to the logger as the flat detail.loc
form.
Keep the invariant error's ErrorDetail representation through the ? conversion so
the logged event stays flat, matching TS. The forward conversion currently always callsfrom_detail; the reverse conversion already special-cases flat output for the Todo
category (see the comment at compiler/crates/react_compiler_diagnostics/src/lib.rs:405).
Extending the same principle to the invariant path in the forward conversion would align
the emitted event shape without touching serialization call sites or fixture outputs.
Scope is intentionally limited to:
compiler/crates/react_compiler_diagnostics/src/lib.rs (the From conversion)No change to log_error, compiler_error_to_info, the Babel/NAPI bridge, or any.expect.md fixture is expected. Codegen output and fnLoc are unaffected (only the
event detail shape changes), so no other fixture should change.
compiler/crates/react_compiler_diagnostics (or react_compiler_lowering) assertingCompilerError to CompilerDiagnostic and thenlog_error's detail path yields detail.loc populated anddetail.details absent. The test fails on current main and passes after the fix.bash compiler/scripts/test-e2e.sh --variant babel must moveerror.bug-invariant-couldnt-find-binding-for-decl.js from FAIL to PASS while1809 fixtures passing.cargo test --workspace (currently 84 passed, 0 failed)f1f7ed2ac ([rust-compiler] Preserve ref access location across phi joins)1.97.1, edition 2024, rust-version = 1.85main.Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.