Skip to content

[compiler] fix: use null instead of GeneratedSource Symbol for synthesized Babel AST node loc#36330

Open
sleitor wants to merge 1 commit intofacebook:mainfrom
sleitor:fix-36327
Open

[compiler] fix: use null instead of GeneratedSource Symbol for synthesized Babel AST node loc#36330
sleitor wants to merge 1 commit intofacebook:mainfrom
sleitor:fix-36327

Conversation

@sleitor
Copy link
Copy Markdown
Contributor

@sleitor sleitor commented Apr 23, 2026

Summary

Fixes #36327

babel-plugin-react-compiler uses an internal sentinel const GeneratedSource = Symbol() to represent "no source location" in its HIR. However, one code path in codegenPlace() was directly assigning this Symbol to the .loc field of a synthesized Babel Identifier node:

const identifier = convertIdentifier(place.identifier);
identifier.loc = place.loc as any;  // ← place.loc might be Symbol()

Babel's Node.loc contract requires SourceLocation | null — a Symbol value violates the contract. When the compiled AST is later passed across worker boundaries (jest-worker, Node IPC via process.send, or any v8.serialize path), Node throws:

TypeError: Symbol() could not be cloned.

All other sites in CodegenReactiveFunction.ts that write to Babel node .loc already guard against GeneratedSource (via the withLoc() helper or explicit loc != GeneratedSource checks). This one site was the only unguarded escape point.

Fix: replace the bare assignment with a guarded form:

identifier.loc =
  place.loc !== GeneratedSource ? (place.loc as t.SourceLocation) : null;

How did you test this change?

  • Added a compiler fixture (bug-generated-source-symbol-in-babel-loc) that exercises the triggering pattern: useMemo + early return null + destructuring whose bindings are hoisted across memo scopes, producing synthesized temporaries (_t) with GeneratedSource locs. The fixture compiles successfully and the generated identifier nodes now have loc: null instead of loc: Symbol().
  • Ran yarn workspace babel-plugin-react-compiler lint — no errors.
  • Ran the full snapshot suite (node packages/snap/dist/runner.js) — 1720 Tests, 1720 Passed, 0 Failed.
  • Ran yarn workspace babel-plugin-react-compiler jest15 test suites, 43 tests, all passed.

…sized Babel AST node loc

When codegenPlace() produced an identifier for a Place whose loc was
GeneratedSource (the internal Symbol sentinel for "no source location"),
it directly assigned that Symbol to identifier.loc via:

    identifier.loc = place.loc as any;

Babel's Node.loc contract requires SourceLocation | null. A Symbol value
violates the contract and causes v8.serialize / jest-worker IPC failures
with "Symbol() could not be cloned" when the compiled AST is passed
across worker boundaries.

All other assignment sites in CodegenReactiveFunction.ts already guard
against GeneratedSource (via the withLoc() helper or explicit checks),
but this one site was unguarded.

Fix: replace the raw assignment with a guarded form that uses null for
synthesized nodes:

    identifier.loc = place.loc !== GeneratedSource
      ? (place.loc as t.SourceLocation)
      : null;

Adds a compiler fixture that exercises the triggering pattern (useMemo +
early return + destructuring hoisted across memo scopes).

Fixes facebook#36327
@meta-cla meta-cla Bot added the CLA Signed label Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant