Closes the migration tracked in #23043.
The new builder API (methods on the AST types themselves) has been in place for a while, all Oxc crates are migrated, and every crate which uses `AstBuilder` already enables `disable_old_builder` in its tests. The module docs said "after a few weeks, we will remove `AstBuilder`'s own methods entirely" — this does that.
## What's removed
- `crates/oxc_ast/src/generated/ast_builder.rs` and `builder/methods.rs` — `AstBuilder`'s own node and primitive constructors.
- `AstBuilderGenerator`, which produced the former.
- The `disable_old_builder` Cargo feature, plus the dev-dependency hacks it required across 33 manifests. Eight crates depended on `oxc_ast` *purely* to turn the feature on; those deps and their `cargo-shear` ignore entries are gone too.
## One thing worth reviewing
`AstBuilderGenerator` owned the `#[builder(default)]` / `#[builder(skip)]` attr registration that `BuilderMethodsGenerator` only *reads* — its doc comment said as much. Deleting it naively would have silently dropped `skip`/`default` handling for the 12 AST types using those attrs, so `attrs()` and `parse_attr()` move to `BuilderMethodsGenerator`.
The evidence this was faithful: after regenerating, `builder_methods.rs` and every other generated file are **byte-identical**. The only generated change in the diff is the deletion.
## Breaking changes
- `AstBuilder`'s own methods are gone. Use the AST type methods: `Statement::new_expression_statement(span, expr, &builder)` instead of `builder.statement_expression(span, expr)`.
- `AstBuilder` is no longer `Copy`/`Clone`, and its `allocator` field is private — use the `allocator` method from `GetAllocator`.
- `AstBuilder` and `NONE` are no longer re-exported from the crate root; import them from `oxc_ast::builder`. (The `disable_old_builder` feature already gated these off, so nothing in-tree used the root path.)
- The `disable_old_builder` feature no longer exists — remove it from any manifest that sets it.
A migration note covering all of the above is in `crates/oxc_ast/src/builder/mod.rs`.
---
Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy.
## Summary
- allow `eval` and `arguments` as direct bindings in ambient declarations
- continue reporting strict-mode errors for destructured ambient bindings
- add focused coverage for declared functions, classes, object patterns, and array patterns
## Why
The semantic checker previously identified allowed bindings by walking to a `TSDeclareFunction`. That missed other ambient declarations, including declared class constructors and methods, and produced diagnostics for TypeScript cases that require no runtime code generation.
The check now uses the existing ambient-context state and rejects only destructuring binding shapes.
## Summary
Adds context notes to `Cannot assign to '{x0}' in strict mode` diagnostics, explaining whether strict mode comes from an explicit directive, class body, or module.
Fixes `import =` semantic reference resolution so internal module references are resolved as namespace lookups instead of ordinary value reads.
TypeScript treats the RHS of an internal `import =` specially. In the checker, `getTargetOfImportEqualsDeclaration` delegates internal module references to `getSymbolOfPartOfRightHandSideOfImportEquals`, which resolves bare RHS names and intermediate qualified names with `SymbolFlags.Namespace` rather than value meaning:
- `getTargetOfImportEqualsDeclaration`: https://github.com/microsoft/TypeScript/blob/f350b52331494b68c90ab02e2b6d0828d2a22a74/src/compiler/checker.ts#L3708-L3733
- `getSymbolOfPartOfRightHandSideOfImportEquals`: https://github.com/microsoft/TypeScript/blob/f350b52331494b68c90ab02e2b6d0828d2a22a74/src/compiler/checker.ts#L4480-L4499
- `getSymbolOfNameOrPropertyAccessExpression` also routes names on the RHS of import/export assignments through that same helper: https://github.com/microsoft/TypeScript/blob/f350b52331494b68c90ab02e2b6d0828d2a22a74/src/compiler/checker.ts#L50014-L50019
Before this change, Oxc could count plain values as references from `import =`:
```ts
const x = 1;
import foo = x;
```
Before:
```text
x references: 1
```
After:
```text
x references: 0
```
That matches TypeScript’s behavior because `x` is only a value symbol, not a namespace-capable symbol.
The same applies to qualified names:
```ts
const x = { y: 1 };
import foo = x.y;
```
Before:
```text
x references: 1
```
After:
```text
x references: 0
```
But namespace-capable declarations still resolve:
```ts
namespace x {}
import foo = x;
```
After:
```text
x references: 1
```
and:
```ts
namespace x {
export namespace y {}
}
import foo = x.y;
```
After:
```text
x references: 1
```
This updates the semantic snapshots to reflect the corrected reference/declaration matching.
## Summary
Reserved-keyword diagnostics now explain the language context that makes an identifier invalid, rather than only reporting that the keyword is reserved.
## Details
- Add context-aware notes for strict mode, classes, and modules.
- Add a dedicated note for `await` in module code.
- Update parser-conformance snapshots for the expanded diagnostics.
Before:
```text
× The keyword 'package' is reserved
```
After, for strict-mode code:
```text
× The keyword 'package' is reserved
note: This identifier is reserved in strict mode code
```
After, for class code:
```js
class Example {
method() {
const static = 1;
}
}
```
```text
× The keyword 'static' is reserved
note: Classes are always strict mode code
```
After, for module code:
```js
export const await = 1;
```
```text
× The keyword 'await' is reserved
note: The identifier `await` is reserved in module code
#24349 made unused-class removal conservatively keep every class whose heritage resolves to a class, lexical, or `var` binding. That preserves class-evaluation errors which the minifier assumptions explicitly allow optimizations to ignore, and it leaves `(class extends Base {});` residue in Rolldown chunks after tree-shaking ([rolldown#10274](https://github.com/rolldown/rolldown/issues/10274)).
The minifier already documents both `No TDZ Violation` and `No side effects from extending a class`, and `oxc_ecmascript` explicitly ignores invalid-superclass errors so removable derived classes are not broadly deoptimized. Treat resolved identifier heritage like other pure reads while retaining the pre-existing literal-arrow exception, including its paren/sequence unwrap. Effects from evaluating the heritage expression, decorators, and effectful class elements remain observable.
The runtime harness already has minifier-only exclusions for unsupported inputs. Six Test262 fixtures covered by these assumptions are skipped only before the minifier variant, with the reason documented beside the list; codegen and transformer still execute, and `runtime.snap` remains unchanged.
## Example
Input:
```js
export var Base = class {};
export var Keep = class extends Base {};
var REMOVE = class extends Base {};
```
Before:
```js
var Base = class {}, Keep = class extends Base {};
(class extends Base {});
export { Base, Keep };
```
After:
```js
var Base = class {}, Keep = class extends Base {};
export { Base, Keep };
```
Verified with `cargo test -p oxc_minifier`, `cargo coverage runtime`, Clippy with warnings denied for `oxc_minifier` and `oxc_coverage`, `just minsize`, `just allocs`, formatting, and `just ready`. Minsize and `runtime.snap` are unchanged; the allocation snapshot records fewer allocations for `kitchen-sink.tsx`.
Implemented with AI assistance (Claude Code and OpenAI Codex). The contributor remains responsible for reviewing, understanding, and submitting the changes under the repository AI usage policy.
## Summary
Make TS1221 underline the generator declaration consistently from the `*` token.
## Why
The previous diagnostic used the generic function span, but that span has different start points depending on how the function is parsed:
- A class method enters function parsing after its name, so `declare class C { *generator(): any; }` highlighted only `(): any;` (starting at column 15), omitting both the generator marker and its name.
- A function declaration starts its span at the `function` keyword, so `declare namespace M { function *generator(): any; }` highlighted the whole declaration (starting at column 5), including text that is not the invalid generator construct.
For example, the prior output treated these equivalent generator markers differently:
```ts
declare class C {
*generator(): any;
// ^^^^^^^^
}
declare namespace M {
function *generator(): any;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
```
The parser now carries the `*` token location as `Option<u32>` while parsing a function. TS1221 uses that location through the declaration end, so both cases consistently highlight the relevant generator declaration:
```ts
*generator(): any;
^^^^^^^^^^^^^^^^^^
```
The AST continues to store `generator` as a boolean; the location exists only while parsing diagnostics.
## Summary
- report TS1016 while parsing formal parameter lists
- preserve initialized-parameter and rest-parameter behavior
- remove the equivalent semantic check and diagnostic
- update parser and semantic coverage snapshots
Follows #24596, which has now merged.
## Summary
- report TS1242 for abstract constructors in the parser
- remove the equivalent semantic diagnostic
- update TypeScript parser coverage snapshots
## Why?
This check needs to be moved to the parser, as the data is not retained after we change the Constructor ast type.
## Summary
- Report operator-specific diagnostics when unary and `await` expressions are used as exponentiation left operands.
- Reject unparenthesized TypeScript type assertions in the same position while preserving explicitly parenthesized forms.
- Add regression fixtures and update the affected conformance snapshots.
Reject `new import(...).prop`, `new import.source(...).prop`, and `new import.defer(...).prop` by treating member access on an unparenthesized import expression as invalid after `new`.
Fixes#23458
Migrate the transform-pipeline `Visit` implementors to `VisitJs` (#24499): `oxc_transformer` (3), `oxc_minifier` (4), `oxc_react_compiler` (1), plus the parser regex example and the coverage driver. Part of #24317; linter half in #24530. Behavior is unchanged.
Together with #24530: `oxlint` −129.4 KiB (−0.94%) stripped, `oxc_transform` napi −16.2 KiB (−0.46%).
## Why each visitor can switch
A visitor is safe on `VisitJs` when its hooks cannot observe anything inside the pruned type grammar — either the hooked node kinds are value-space-only, or the AST it walks cannot contain type nodes at that pipeline stage.
| Visitor | File | Why equivalent |
|---|---|---|
| `PrivateInExpressionDetector` | `crates/oxc_transformer/src/decorator/legacy/mod.rs` | Hooks `visit_private_in_expression` (+`visit_decorators`); `#x in y` is runtime-only and cannot occur in type grammar, even inside decorator expressions carrying casts. |
| `BindingMover` | `crates/oxc_transformer/src/es2017/async_to_generator.rs` | Its formal-parameter overrides visit only pattern + initializer (`BindingPattern` carries no type field — annotations are sibling fields it never touches); runs in the exit phase after TS strip. |
| `UsedInJSXBindingsCollector` | `crates/oxc_transformer/src/jsx/refresh.rs` | Runs on un-stripped TSX, but its hooks (JSX-shaped calls, `visit_jsx_opening_element`, declarations/imports) cannot fire inside type grammar; its hand-written `visit_ts_type_annotation` skip-stub is deleted — `VisitJs` prunes that natively. |
| `Finder` | `crates/oxc_react_compiler/src/react_compiler/entrypoint/program.rs` | Matches functions by `scope_id`; real JS function scopes never live in pure type grammar, and `VisitJs` still walks the places one could hide (enum initializers, namespace bodies, cast inner expressions). |
| `KeepVar` | `crates/oxc_minifier/src/keep_var.rs` | Hooks `visit_statement`/`visit_variable_declaration`; statements and `var` declarations don't exist in type space, and namespace/enum bodies (which can contain them) are still walked by `VisitJs`. |
| `DirectEvalFlagCheck` | `crates/oxc_minifier/src/peephole/mod.rs` | Debug-only; hooks `visit_call_expression` only — call expressions cannot occur in valid type grammar. |
| `LiveDirectEvalCollector` | `crates/oxc_minifier/src/peephole/mod.rs` | Same single call-expression hook; type-space-immune regardless of input. |
| `TestVisitor` | `crates/oxc_minifier/tests/ecmascript/prop_name.rs` | Test helper parsing `SourceType::default()` (plain JS); single non-recursing `visit_object_expression` hook. |
| `RegularExpressionVisitor` | `crates/oxc_parser/examples/regular_expression.rs` | Finds `RegExpLiteral` / `new RegExp(...)`; neither node kind can occur in type grammar. |
| `CheckASTNodes` | `tasks/coverage/src/driver.rs` | Its only active override is `visit_reg_exp_literal` (regex print idempotency); `RegExpLiteral` cannot occur in type space. (The commented-out `visit_span` full-AST check would need `Visit` if ever revived.) |
Left on `Visit`: the minifier's incremental-scoping bookkeeping (`DropDiff`, `OverPruneCheck`, `LiveRefCollector`) — dropping `let x: Foo = …` must unmark the type-space `Foo` reference, which the `peephole::remove_unused_declaration` tests verify (they fail under `VisitJs`; confirmed and reverted during this work). `ChildScopeCollector` also stays: TS type nodes (mapped/conditional/function types) carry real `scope_id`s it must collect.
---
🤖 This PR was written with the assistance of Claude Code (Claude Fable 5).
#24463 moved the test262 pin from `de8e621c` to `d1d583db` but did not regenerate `tasks/coverage/snapshots/runtime.snap`, which still records the old sha. The runtime suite doesn't run in repo CI, so nothing caught it; monitor-oxc's Test262 job has been aborting on the snapshot sha guard ("Repository … is outdated for runtime.snap. Please run `just submodules`") on every run since 2026-07-13.
Two changes:
1. Regenerated the snapshot with `UPDATE_SNAPSHOT=1 cargo coverage runtime` against the pinned checkout: 17306/18277 (94.69%) passing, matching what monitor-oxc measures on main. The list changes are upstream test262 churn — two new `eval-code` global-function-init failures, and the async-arrow `forbidden-ext` block that no longer exists upstream.
2. Fixed the root cause of the staleness: the `update_submodules` workflow runs `just coverage`, whose default task skips the runtime suite, so `runtime.snap` was never regenerated on automated pin bumps. The workflow now runs `cargo coverage runtime` explicitly (it already sets up Node).
This PR was assisted by Claude.
## Summary
- Align auto-accessor definite assignment assertions with TypeScript.
- Report TS1263 when an accessor has both `!` and an initializer.
- Report TS1264 when an accessor has `!` but no type annotation.
- Report TS1255 for typed definite assertions in static, abstract, and ambient contexts.
- Highlight only the `!` token, matching TypeScript.
fixes#24503.
## TypeScript behavior
Given:
```ts
class A {
accessor missingType!;
// ^: TS1264: Declarations with definite assignment assertions must also have type annotations.
accessor initialized! = 1;
// ^: TS1263: Declarations with initializers cannot also have definite assignment assertions.
accessor typed!: number;
// ^: valid
static accessor staticTyped!: number;
// ^: TS1255: A definite assignment assertion '!' is not permitted in this context.
}
```
TypeScript treats auto-accessors as property declarations and applies the following grammar check:
https://github.com/microsoft/TypeScript/blob/637d5746b70257028fb95aad32ddec6b26ab0a14/src/compiler/checker.ts#L53443-L53452
This establishes the diagnostic precedence:
1. An initializer produces TS1263.
2. Otherwise, a missing type annotation produces TS1264.
3. Otherwise, an invalid context produces TS1255.
Evaluating a class declaration can throw before any of its members matter. `class C extends C {}` reads its own name while it is still in the TDZ and always throws a ReferenceError. A heritage that is a `var`, or a lexical binding declared later, can be undefined or uninitialized at evaluation time and throws a TypeError. The minifier only checked for a literal arrow function as heritage, so removing an unused class could silently delete one of these guaranteed throws.
The removal check now unwraps parens and sequence wrappers before looking at the heritage, and keeps any class whose heritage identifier resolves to a class, `let`/`const`, or `var` binding. Declaration order cannot be proven in the middle of minification (transforms copy and move spans), so the rule is conservative on purpose. Function declarations, imports, and globals are initialized before any class can evaluate, so classes extending them can still be removed.
## Example
```js
var f = () => {};
class C extends f {}
```
Running this throws `TypeError: Class extends value ... is not a constructor`.
Before, the unused class was removed and the whole program minified to nothing, so the TypeError was gone. After, the output is unchanged and the throw is preserved:
```js
var f = () => {};
class C extends f {}
```
This fixes six test262 runtime failures that exist on main today (17279 -> 17285 positive passes): `in-extends-expression-grouped`, `in-extends-expression-assigned`, `superclass-arrow-function`, `prototype-getter`, `prototype-setter`, and `constructable-but-no-prototype`. The runtime suite executes the minified tests in node; the results were verified through monitor-oxc. Minified sizes are unchanged on every fixture.
Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy.
## Summary
- track TypeScript ambient contexts using a nesting depth in `SemanticBuilder`
- enter ambient context for declaration files and `declare` declarations, including declarations without their own scope
- replace namespace-based and ancestry-based ambient detection
- use the ambient context for reserved keywords, global augmentations, class heritage, computed class members, and bodyless accessors
- extend conformance coverage for ambient variables, properties, functions, and classes
This approach was discussed with overlookmotel yesterday after some brainstorming!
Recognize identifiers nested within `declare` variable declarations and declared class properties as being in an ambient context.
This prevents valid TypeScript from incorrectly producing reserved-keyword diagnostics.
### Examples
Function types inside ambient variable declarations are now accepted:
```ts
declare let f: (package: number) => interface;
```
Type references inside declared class properties are also accepted:
```ts
class C {
declare field: package;
}
```
Previously, package and interface were incorrectly reported as reserved keywords.
Adds the x86_64 version of my Research `SIMD For Context Sensitive
Languages`.
(CPB = Cycles per byte)
- Adds new create crates/oxc_lexer; Which eventually lead into an
unfused parser design inside oxc_parser
- Fully test262 compliant JS standalone Lexer; SIMD used has
counterparts on every platform.
- Performs at 2.5CPB on checker.js on my ZEN 3 CPU; or around 1.5-1.75
gbs (3.8GHz).
- Across aarch64, + other platforms performance will hold which I've
seen in my POCs but those targets will come later.
- Supports JSX/TS options, Which don't have much impact on overall perf
results.
------
The expected results for seeing this work through [including on parser
side on oxc_parser]
1. P95 improvements of 80-100% over current `oxc_parser`; 12-18% of this
will come from the unfused contract where JS grammar specifically loves
being able to see 'ahead' into `kinds` in various places. The rest comes
from the perf improvements to the lexer itself and some wins that will
come inside the parser from work now done in the lexer.
## Summary
- mark computed method keys in ambient and abstract classes as type references
- preserve runtime read flags for concrete computed methods
- bound declare-class ancestry checks to the containing class
- add semantic snapshot coverage and update the TypeScript conformance snapshot
Follows the work done in 588d9971ac, 9b95632179
## Summary
Remove TypeScript enum-member bindings after transformation.
Enum members are represented as bindings during semantic analysis so the transformer can resolve member values, fold references, and handle merged enums. Once TypeScript transformation finishes, those bindings no longer correspond to emitted JavaScript declarations.
Include `EnumMember` in the existing final TypeScript binding cleanup instead of removing bindings during enum lowering, where later references may still need them.
This fixes `discriminatedUnionTypes4.ts` and removes the same stale binding mismatch from other enum cases.
Closes#24224 — the first commit is @camc314's fix from there.
Lowered namespaces kept their `ValueModule`/`NameSpaceModule` symbol flags, so the flags no longer matched the emitted JavaScript:
- Namespaces lowered to a declaration become `let` → `BlockScopedVariable`.
- Namespaces lowered to an assignment only lose their module bits.
This removes over 2,000 stale symbol-flag mismatches from the snapshots.
Unlike #24224 there is no pending state: the type-only check for merged namespaces now reads per-declaration flags from the redeclaration list instead of the symbol's live flags, so each declaration can update the flags right when it is lowered.
Closes#24226 — the first commit is @camc314's fix from there.
Lowered enums kept their `RegularEnum`/`ConstEnum` symbol flags, so the flags no longer matched the emitted JavaScript:
- Top-level enums become `var` → `FunctionScopedVariable`.
- Exported and nested enums become `let` → `BlockScopedVariable`.
- Merged enums lowered to an assignment only lose their enum bits.
This removes over 1,000 stale symbol-flag mismatches from the snapshots.
Unlike #24226 there is no pending state: enum member inlining now identifies enums through `EnumData` (#24268) instead of `SymbolFlags`, so the flags can be updated right when the enum is lowered.
Fixes#23110, also follows Prettier 3.9.
- Move same-line trailing comments after the statement semicolon
- Keep dangling comments after trailing array holes inside the brackets
- Guard suppression comments and ASI sources
## Summary
- resolve declared class heritage expressions through ValueAsType so their final references are Type
- allow type-only namespace imports in declared heritage while preserving Read for runtime class heritage
- add regression coverage for type-only namespace imports, value namespace imports, local bases, and a runtime control
- update semantic conformance snapshots, yielding two additional TypeScript passes
## Summary
- resolve computed declare class-field keys through ValueAsType so their final references are Type
- allow declared keys to resolve type-only import bindings while preserving Read for ordinary runtime class fields
- add a red/green semantic snapshot and update conformance snapshots, yielding one additional TypeScript semantic pass and one additional transform-conformance pass
## Summary
- resolve computed TypeScript method, getter, and setter keys through ValueAsType so their final references are Type
- allow those keys to resolve value bindings and type-only import bindings consistently with computed property signatures and type queries
- add regression coverage and update semantic conformance snapshots, yielding one additional Babel pass and six additional TypeScript passes
Handled for TSPropertySignature via https://github.com/oxc-project/oxc/commit/d8b9909bd8df193306de7ff6f1204f3ac0963076
## Summary
Fix semantic reference tracking when class-properties processes a named class rewritten by legacy decorators.
## Problem
Legacy decorators split a named class into two bindings:
- the outer variable binding;
- the inner class-expression binding.
Class-properties previously created `_Class = Class` using the rewritten `class.id`, which points to the inner binding. Since the assignment executes outside the class body, it must reference the outer binding captured when entering the class.
Originally anonymous classes are different: another transform may assign their final name later, so they must continue using the current `class.id`.
```mermaid
flowchart TD
A["Create read for `_Class = Class`"] --> B{"Captured class name is the temp binding?"}
B -- "No" --> C["Named class: read captured outer binding"]
B -- "Yes" --> D["Originally anonymous class: read current class ID"]
```
## Solution
Use the class binding captured on entry for named declarations, while retaining the current class ID for originally anonymous declarations.
This aligns transformed reference IDs with semantics rebuilt from the generated output without regressing anonymous decorated classes.
## Summary
Generate bindings for anonymous decorated classes in the class’s enclosing scope.
## Problem
When an anonymous class element has a decorator, the transformer creates a
temporary `_default` binding while traversing the class body.
Previously, the binding was registered in the current class scope even though
the transformed declaration is emitted outside that class.
```text
Semantic data before fix Transformed AST
Program Program
└─ Class scope └─ class/variable _default
└─ binding: _default ↑ emitted here
↑ recorded here
```
This caused the retained semantic data to disagree with semantics rebuilt from
the transformed AST:
```text
after transform: _default → class scope
rebuilt: _default → enclosing scope
```
## Solution
Derive the class’s enclosing scope from its semantic scope and generate the
temporary binding there.
```text
Class scope
│
└── parent scope ── generate `_default` binding here
│
└── matches emitted declaration
```
This corrects binding ownership for anonymous default-export classes with
decorated members.
## Summary
Preserve strict-mode metadata when lowering TypeScript namespaces into IIFE-style functions.
Previously, namespace transformation always replaced the original scope flags with `Function | StrictMode`, causing semantic mismatches for namespaces that were not strict. The transform now retains strictness only when present on the original namespace or enclosing scope.
Updated semantic and transform-conformance snapshots to remove the resolved scope-flag mismatches.
## Summary
TypeScript enum lowering reuses the enum body’s scope for the generated function expression. Update that scope with `ScopeFlags::Function` so transformed semantic data matches a freshly rebuilt AST.
## Summary
Keep transformer semantic data in sync when TypeScript-only syntax is removed.
This change:
- removes resolved and unresolved references belonging only to stripped TypeScript syntax
- removes bindings for erased type-only and unused imports
- preserves runtime bindings when a type import is merged with a value declaration
- promotes the surviving declaration’s symbol flags, span, and redeclaration metadata
- adds coverage for type imports merged with runtime declarations
- refreshes semantic and transformer conformance snapshots
## Motivation
We are preparing to change the AST shape, including adding and rearranging scopes.
Currently, transformer semantic snapshots contain thousands of lines of mismatches caused by stale bindings and references from TypeScript syntax that has already been removed from the AST. That creates a large amount of unrelated snapshot noise.
With that noise present, snapshot changes caused by the upcoming AST work would be difficult to review: a real scope or semantic regression could easily be hidden among mechanical churn.
This PR fixes the underlying semantic inconsistencies first, giving us a much cleaner baseline for reviewing future AST and scope changes. It is not intended to change emitted JavaScript.
## Summary
- propagate `DirectEval` when async-to-generator lowering inserts wrapper scopes
- update the Test262 semantic snapshot for the newly passing cases
## Known edge case
A direct eval nested in a function-valued default parameter of an async method can still leave a stale flag when that parameter scope is reparented. This should be addressed by the new function body scope logic
Fixes semantic scope mismatches when async function declarations are lowered inside sloppy block scopes.
Before this change, `async_to_generator` kept the original async function binding, and the generated helper binding, in the current block scope. After transform those declarations are no longer async:
```js
with (obj) {
async function ref() {}
}
```
becomes roughly:
```js
with (obj) {
function ref() {
return _ref.apply(this, arguments);
}
function _ref() {
_ref = babelHelpers.asyncToGenerator(function* () {});
return _ref.apply(this, arguments);
}
}
```
A fresh semantic rebuild treats those lowered plain function declarations with Annex B hoisting rules, so in sloppy block scopes their bindings can belong to the enclosing var scope instead of the block scope. The transformed semantic tree was still using the pre-lowered block placement, which caused rebuild mismatches in Test262 coverage.
This PR makes `async_to_generator` choose the same binding scope that a rebuilt semantic tree would use for the emitted plain function declarations, while preserving the existing behavior for TypeScript, strict mode, var scopes, and cases where the hoist scope already has a binding.
## Summary
Self-review follow-up to #24034 (stacked on #24052), hardening the runtime suite's server transport:
- **Ephemeral ports + stdout handshake**: the fixed 32055..N port range let an orphaned server from a killed run pass the readiness probe (silently testing against a stale binary) and made two concurrent runs in different worktrees cross-talk — one run's shutdown DELETE kills the other's servers. `runtime.js` now listens on an OS-assigned ephemeral port and reports it as the first stdout line, which the runner reads as both the port and the readiness signal (the polling loop and port math are gone); remaining server stdout is drained by a background thread so `DEBUG=1` output can't deadlock a full pipe.
- **Transport retry**: monitor-oxc's first live Test262 run flipped 13 for-of cases to `timeout: global` — ureq's 10s client timeout tripping under CI load while requests queued, not a semantic change. A localhost-specific agent (no proxy, one-minute timeout) plus a single retry fixes it; the server responds even for failing tests, so a transport `Err` never carries a verdict.
Result-neutral: 17279/18277, `runtime.snap` byte-identical across repeated runs.
## Summary
Self-review follow-up to #24034 (stacked on #24049): the runtime suite now fits the standard `run_tool` framework — `runtime::run` is a plain `fn(&[Test262File]) -> Vec<CoverageResult>` runner, deleting its hand-copied print/snapshot tail so reporting-convention changes can't silently skip it. `cargo coverage all` no longer reads the ~52k-file test262 tree twice (`run_all_with` shares one loaded `TestData`); the standalone `runtime` command still loads only test262, which monitor-oxc CI relies on. Also hoists the per-request `workspace_root()` walk to once per file.
Result-neutral: 17279/18277, `runtime.snap` byte-identical, verified via both `cargo coverage runtime` and `cargo coverage all`.
## Summary
Follow-up to #24034 from self-review: `available_parallelism()` spawned N Node servers even when `--debug` caps the rayon pool at 1 thread, leaving N−1 servers idle (only `current_thread_index() % num_servers` = server 0 is ever used). Size the server count from `rayon::current_num_threads()` instead. Full suite verified unchanged: 17279/18277, byte-identical `runtime.snap`.
## Summary
#18478 reduced the test262 runtime runner to a stub ("not yet implemented"), and since oxc's own CI never runs `cargo coverage runtime`, the transformer/minifier semantics coverage it provided has been dark — monitor-oxc's Test262 job (the only consumer) has been disabled since July 2025.
This restores execution on the simplified architecture: each eligible test262 case runs through codegen / transform / minify and executes in the Node runtime server, driven by rayon with blocking HTTP instead of the old tokio design. Skip lists and per-variant semantics are ported verbatim from the pre-#18478 runner.
- `main.rs` now loads `TestData` lazily, so `cargo coverage runtime` works with only the test262 fixtures checked out (which is how monitor-oxc CI invokes it).
- Execution is parallelized: one Node server per core with each rayon worker pinned to its own server, and the test262 harness is compiled once per distinct include list as a reusable `vm.Script` instead of re-parsed per request. Full suite: ~10s wall locally (~45s single-server).
- Results at test262 `de8e621c`: 17279/18277 positive tests pass (94.54%); `runtime.snap` is committed and byte-stable across repeated runs; clippy/fmt clean.
- monitor-oxc side: oxc-project/monitor-oxc#178 (must merge after this one).
Note: oxc CI still does not run the runtime suite, so `runtime.snap` can go stale and turn monitor-oxc's job red — the failure mode that led to the original "skip 262". Possible follow-ups: run it in conformance CI, or add a regression-only mode (fail on new failures, tolerate improvements).
## Summary
Ports typescript-go's symbol/type/error baseline harnesses
(`internal/testutil/tsbaseline/{type_symbol_baseline,error_baseline}.go`)
into `tasks/coverage` as new conformance suites, measuring oxc against
the `.symbols`, `.types`, and `.errors.txt` baselines already shipped in
the TypeScript submodule (~14k each). All report through the shared
`CoverageResult` machinery, so their summaries/snapshots look exactly
like the other suites.
Run with `cargo coverage symbols` / `cargo coverage types` / `cargo
coverage errors`.
## What's measured
- **symbols** — walk the AST (`oxc_ast_visit`), resolve lexical bindings
via `oxc_semantic`, emit `Symbol(name, Decl(file, line, col), ...)` with
declaration positions recovered from the parser token stream (to match
TypeScript's trivia-inclusive `node.pos`), then diff against the
baseline.
`AST Parsed 8364/8364 (100%)`, `Positive Passed 1765/8364 (21.10%)`.
- **errors** — match oxc parser diagnostic positions + TS codes (oxc's
parser attaches real `TSxxxx` codes via `ts_error`) against the
`.errors.txt` summary headers.
`AST Parsed 7699/7699 (100%)`, `Positive Passed 7251/7699 (94.18%)`.
- **types** — **scaffold**. Same harness parameterized by kind
(mirroring typescript-go's `isSymbolBaseline`), but oxc has no type
checker, so every type payload is `<<unresolved>>`. The full runner
(baseline discovery, walk, output format, comparison, snapshot) is wired
and ready for a checker to fill in the walker's `Types` arm.
`AST Parsed 8364/8364 (100%)`, `Positive Passed 78/8364 (0.93%)` — the
passes are files with empty type baselines.
## Notes
- oxc has no type checker, so `.types` and the type-checker surface of
`.symbols`/`.errors.txt` (member/`this` symbols, qualified names, lib
globals, `TS2xxx`/`TS18xxx` errors) are out of reach — that gap is the
measurable target for a future checker, which is exactly what the
`.types` scaffold sets up.
- The suites are opt-in subcommands (like `transpiler`), not part of
`cargo coverage all`.
---
🤖 Written with [Claude Code](https://claude.com/claude-code) (AI
assistance); reviewed by the author, per oxc's AI usage disclosure
policy.
Part of #23043.
We don't enable `oxc_ast`'s `disable_old_builder` Cargo feature in any of our published crates, or crates that are depended on by published crates.
Tasks are private to the repo, so enable this feature on them, to ensure no usage of legacy `AstBuilder` methods sneak in during the transition period before we delete these methods.
Previously, whether to serialize AST to ESTree JSON with/without TS fields was a compile-time option - via 2 different configs `CompactTSSerializer` and `CompactJSSerializer` (and their pretty-printing and with-fixes counterparts).
Turn this into a runtime option instead -
This make serialization a little slower (more branches in either mode, and dead code in JS-only mode) but reduces the size of `oxc-parser` binary as only one `ESTreeSerializer` is generated, instead of 2 (JS-only, and with-TS).
Add a friendly error for adjancent JSX element case:
```
× Adjacent JSX elements must be wrapped in an enclosing tag.
╭─[misc/fail/jsx-adjacent-elements.jsx:3:3]
2 │ <div />
3 │ <span />
· ─
4 │ );
╰────
help: Did you want a JSX fragment `<>...</>`?
```
This error message exists in babel.