mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
f693a473ce
## Root cause
The `new-mutability/` fixtures (and several others across the corpus)
that set
`// @enablePreserveExistingMemoizationGuarantees:false` bailed with a
spurious
```
[ReactCompiler] PreserveManualMemo: Existing memoization could not be preserved
```
diagnostic and emitted `No changes.`, whereas the fork compiles them.
This is **not** a compiler bug — it is a test-harness config mismatch.
The
upstream `snap` runner (`packages/snap/src/compiler.ts`,
`makePluginOptions`)
runs the entire fixture corpus with
`validatePreserveExistingMemoizationGuarantees` **off**, enabling it
only when a
fixture's first line contains
`@validatePreserveExistingMemoizationGuarantees`
(it keys off substring presence, ignoring any `:value`):
```ts
let validatePreserveExistingMemoizationGuarantees = false;
if (firstLine.includes('@validatePreserveExistingMemoizationGuarantees')) {
validatePreserveExistingMemoizationGuarantees = true;
}
```
oxc's `snapshot.rs` instead used the `EnvironmentConfig` schema default
(`true`). The `ValidatePreservedManualMemoization` pass is gated on
`enable_preserve || validate_preserve`, so with `enable_preserve:false`
but
`validate_preserve` left at its default `true`, the pass ran and fired
the error
where the fork skips validation entirely.
I confirmed with a `debugLogIRs` dump of the fork under the *real* snap
config
that oxc's `InferMutationAliasingEffects` /
`InferMutationAliasingRanges` /
reactive-scope output is byte-identical to the fork; the only divergence
was
whether the validation pass runs. (The fork also errors on these
fixtures when
`validatePreserveExistingMemoizationGuarantees` is forced on.)
## Fix
Replicate the snap runner exactly in `parse_pragma` — after the
directive loop,
unconditionally set `validate_preserve_existing_memoization_guarantees`
to
whether the first line contains the
`@validatePreserveExistingMemoizationGuarantees`
substring. This is the same class of harness alignment as #24118.
## Fixtures changed
26 snapshots, **all** moving from a spurious `PreserveManualMemo` bail
(or, for
`useMemo-named-function`, an over-eager diagnostic) to compiled code
that
matches the fork:
- 11 in `new-mutability/` (the batch): `mutate-through-identity`,
`mutate-through-identity-function-expression`,
`todo-transitivity-createfrom-capture-lambda`,
`transitivity-capture-createfrom`,
`transitivity-capture-createfrom-lambda`,
`transitivity-add-captured-array-to-itself`,
`transitivity-phi-assign-or-capture`,
`todo-control-flow-sensitive-mutation`,
`useCallback-reordering-deplist-controlflow`,
`useCallback-reordering-depslist-assignment`,
`useMemo-reordering-depslist-assignment`
- 15 elsewhere (`preserve-memo-validation/*`,
`useMemo-*`/`useCallback-*-dont-preserve-*`,
`repro-*`, `allow-modify-global-in-callback-jsx`,
`context-variable-as-jsx-element-tag`,
`existing-variables-with-c-name`).
Every changed snapshot was verified to have the **same `_c(N)` cache
size and
memoization structure as the fork** (differences reduce to codegen
whitespace:
redundant parens around assignments, JSX/array line-wrapping, trailing
commas).
No snapshot regressed (none went code→error/bail).
### new-mutability batch outcome (54 fixtures)
- **35** exact structural matches with the fork.
- **6** codegen-formatting-only diffs (identical cache size /
memoization):
`array-filter`, `array-map-named-callback-cross-context`,
`todo-control-flow-sensitive-mutation`,
`useCallback-reordering-deplist-controlflow`,
`useCallback-reordering-depslist-assignment`,
`useMemo-reordering-depslist-assignment`.
- **9** `error.*` fixtures already emit diagnostics matching the fork's
error intent
(unaffected by this change).
- **0** fixtures where oxc errors but the fork compiles (was 11).
## Remaining work (out of scope, distinct root causes)
- `transitivity-add-captured-array-to-itself` (`_c(18)` vs `_c(19)`) and
`transitivity-phi-assign-or-capture` (`_c(9)` vs `_c(11)`) now compile
but with
coarser memoization. Root cause: the harness does not supply the
shared-runtime
`typedCapture` / `typedCreateFrom` / `typedMutate` **aliasing**
signatures that
`makeSharedRuntimeTypeProvider` gives the fork, so oxc falls back to
conservative
call effects. Fixing this needs a broader `moduleTypeProvider` addition
and its own
corpus-wide verification.
- `ssa-renaming-ternary-destruction` emits an empty `function useFoo()
{}` — a
pre-existing SSA/phi/alias-group bug (the fixture's own comment flags
the case as
unresolved), unrelated to memoization validation.
- `error.todo-repro-named-function-with-shadowed-local-same-name` raises
an internal
`Invariant` instead of a clean error — a separate pre-existing issue.
25 lines
611 B
Plaintext
25 lines
611 B
Plaintext
---
|
|
source: crates/oxc_react_compiler/tests/snapshot.rs
|
|
input_file: crates/oxc_react_compiler/fixtures/useMemo-named-function.ts
|
|
---
|
|
import { c as _c } from "react/compiler-runtime";
|
|
// @validateNoSetStateInRender:false @enablePreserveExistingMemoizationGuarantees:false
|
|
import { useMemo } from "react";
|
|
import { makeArray } from "shared-runtime";
|
|
function Component() {
|
|
const $ = _c(1);
|
|
let t0;
|
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
t0 = makeArray();
|
|
$[0] = t0;
|
|
} else {
|
|
t0 = $[0];
|
|
}
|
|
const x = t0;
|
|
return x;
|
|
}
|
|
export const FIXTURE_ENTRYPOINT = {
|
|
fn: Component,
|
|
params: [{}]
|
|
};
|