Commit Graph

696 Commits

Author SHA1 Message Date
oxc-guard 0389c4010e release(crates): oxc v0.146.0 (#25912)
### 🚀 Features

- 83cb516 minifier: Improve negation of expressions in boolean context (#25755) (Armano)

### 🐛 Bug Fixes

- 134fb22 isolated-declarations: Preserve unresolved parameter types (#25909) (Dunqing)
- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)
- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)

### ⚡ Performance

- 5ef0c93 minifier: Unwrap all unary exprs without creating dummy expressions (#25891) (Armano)
- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)
- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)
- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)
- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)
- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)
- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)
- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)

### 📚 Documentation

- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)
2026-08-19 08:29:51 +00:00
oxc-guard[bot] 4a4a1fb2e7 release(crates): oxc v0.145.0 (#25865)
### 💥 BREAKING CHANGES

- 365274e packages/codegen: [**BREAKING**] `printSync` return an object
(#25720) (overlookmotel)

### 🚀 Features

- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename`
option (#25854) (overlookmotel)
- 300763f packages/codegen: Add full source map support (#25585)
(camc314)
- a169e4a napi/minify: Expose property name mangling options (#24741)
(Dunqing)
- 2f5cdb1 minifier: Add property name mangling (#24740) (Dunqing)
- 4922141 mangler: Deduplicate private accessor names (#25601) (camc314)
- 1c4f519 napi: Add Relay transform plugin (#25503) (Boshen)
- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)

### 🐛 Bug Fixes

- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)
- 345f981 react_compiler: Skip node_modules by default (#25859) (Boshen)
- ab81f3f minifier: Drop side-effect-free additions (#25639) (Dunqing)
- 241c559 napi/minify: Accept RegExp for property filters (#25827)
(Dunqing)
- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)
- 53f9295 isolated-declarations: Preserve undefined for defaulted any
(#25292) (camc314)
- 8ab883a codegen: Preserve property key annotations (#25766) (Dunqing)
- b13fd48 minifier: Model uninitialized module vars as undefined
(#25497) (Dunqing)
- f6000ac ecmascript: Fold `**` with `Number::exponentiate`, not IEEE
`pow` (#25644) (Kotaro Chikuba)
- b846abc isolated-declarations: Handle ambient expando properties
(#25655) (camc314)
- dec0a86 react-compiler: Standardize diagnostics (#25702) (Boshen)
- fca2e0c parser: Reject initialized lexical declarations in for-in
(#25700) (Boshen)
- ae9be8f parser: Forbid type parameters on quoted constructors (#25696)
(Boshen)
- c5f188a react-compiler: Use Babel v1 validation defaults (#25676)
(Boshen)
- db44651 napi: Disable reuseWorker in browser bindings (#25640)
(leaysgur)
- ce35d47 react_compiler: Preserve JSX import source pragmas (#25592)
(Boshen)
- 059784d semantic: Respect shadowed `Infinity` and `NaN` in enum
evaluation (#25604) (camc314)
- bb5a232 minifier: Keep variable declaration initilized with class when
keepNames is enabled (#25584) (sapphi-red)
- f49229d minifier: Keep side effects when rotating bitwise operands
(#25596) (Kotaro Chikuba)
- e82495b ecmascript: Derive `ToNumber` of `!x` from `ToBoolean`
(#25595) (Kotaro Chikuba)
- 509931b semantic: Classify global references per identifier (#25608)
(camc314)
- 80484ce mangler: Correct base54 safety comment (#25606) (camc314)
- c1369a7 codegen: Resolve private names in class heritage (#25588)
(camc314)
- c002f29 codegen: Escape sources for empty import specifiers (#25586)
(camc314)
- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582)
(camc314)
- cdf1846 semantic: Allow legacy escapes in JSX attributes (#25576)
(Boshen)
- e75e102 minifier: Preserve block statement in labeled iteration
statements (#25162) (Armano)
- 771d79a mangler: Exclude non-manglable symbols from slot assignment
(#25539) (sapphi-red)
- 76b19f5 minifier: Avoid duplicating large folded strings (#25532)
(Dunqing)
- 59e8895 codegen: Validate starting indent level (#25550) (camc314)
- 88b34f2 clippy: Remove unneeded `unsafe` (#25551) (camc314)
- 23a7ad0 parser: Stop delimited lists at end of file (#25542) (Boshen)
- 73acba9 parser: Preserve fatal errors during await reparse (#25541)
(Boshen)
- c3e99d1 minifier: Avoid invalid octal escapes in template folds
(#25495) (Dunqing)
- b4e6a9e codegen: Output newline after `export default interface`
(#25487) (overlookmotel)
- 5fcf683 minifier: Correct issue with try finally termination (#25185)
(Armano)
- 1645d93 react_compiler: Preserve source spans (#25462) (Boshen)
- 8d7f9cf react_compiler: Honor eslint suppressions (#25394) (Boshen)

### ⚡ Performance

- 568203e ecmascript: Use binary search for known globals (#25817)
(Boshen)
- c84ede3 estree_tokens: Share JS token update entry point (#25826)
(Boshen)
- 9a6e862 minifier: Move owned statements directly (#25835) (Dunqing)
- 61b2aef minifier: Move owned AST nodes directly (#25837) (Dunqing)
- 673b04b minifier: Replace expressions without take_in dummies (#25836)
(Dunqing)
- 63ff8ef linter: Outline diagnostic construction (#25762) (Boshen)
- 621808e diagnostics: Measure graphemes lazily (#25723) (Boshen)
- f62ed0e diagnostics: Optimize graphical number rendering (#25715)
(Boshen)
- 7152834 diagnostics: Reduce graphical formatting overhead (#25714)
(Boshen)
- 10fc4b7 diagnostics: Reduce graphical rendering allocations (#25711)
(Boshen)
- 9c8abab diagnostics: Batch graphical reports (#25710) (Boshen)
- 757f3d4 mangler: Share allocated names across reused slots (#25605)
(camc314)
- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)

### 📚 Documentation

- 58f7ab9 packages/codegen: Reformat docs and comments (#25848)
(overlookmotel)
- 627466e transform-react: Document all options (#25800) (Boshen)
- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722)
(overlookmotel)
- 386a699 packages/codegen: Correct JSDoc comment (#25716)
(overlookmotel)
- 31e571d mangler: Update code example (#25599) (camc314)
- fd62354 codegen: Revamp package readme (#25560) (camc314)
- ffa3153 codegen: Clarify raw transfer Node requirement (#25565)
(camc314)
- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)
- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-08-18 22:26:40 +08:00
oxc-guard b6f1458dec release(crates): oxc v0.144.0 (#25474)
### 💥 BREAKING CHANGES

- a33788e ast: [**BREAKING**] Group class heritage into `ClassHeritage` (#25360) (camc314)
- 5c5cdcd ast: [**BREAKING**] Narrow `TSInterfaceHeritage::expression` to TSTypeName (#24360) (camc314)
- 6be314f ast: [**BREAKING**] Remove duplicated `VariableDeclarator::kind` (#25319) (camc314)
- 44fd320 ast: [**BREAKING**] Split TS external modules & Namespace Declarations (#25284) (camc314)

### 🚀 Features

- 9a37c41 napi: Restructure React transform options (#25326) (Boshen)
- b3c1b30 allocator: Add `ArenaVec::push_mut` (#25338) (overlookmotel)

### 🐛 Bug Fixes

- bbd6ed3 codegen: Wrap string literal in parens after directives (#25435) (overlookmotel)
- d07a293 codegen: Preserve cast parentheses in conditional tests (#25115) (camc314)
- 8b7d441 codegen: Preserve satisfies parentheses before bitwise operators (#25113) (camc314)
- e0fb2d7 minifier: Avoid unsafe return removal in labeled blocks (#25406) (Armano)
- c49f4ce parser: Preserve terminal license comments (#25400) (camc314)
- 9d19ab3 transform_react: Downgrade nonfatal compiler diagnostics (#25418) (Boshen)
- 937825c react_compiler: Disable exhaustive memo validation by default (#25417) (Boshen)
- 12937b3 parser: Reject comma expressions in class fields (#25385) (Cameron)
- 7d8a9b5 react_compiler: Preserve hook spans for React Refresh (#25393) (Boshen)
- 9e93ba6 parser: Reset auto-accessor initializer context (#25384) (camc314)
- cc2e28d parser: Reject trailing tokens in `Parser::parse_expression` (#25371) (camc314)
- 697c629 minifier: Preserve Annex B labeled-function bindings in implicit-return rewrite (#25349) (Armano)
- 4d723ca parser: Report TS1035 for quoted module names (#25305) (camc314)
- 0c9b980 semantic: Report module declaration diagnostics (#25285) (camc314)
- 2a1058a napi: Omit parser header from WASI declarations (#25254) (camc314)

### ⚡ Performance

- dd1430b semantic: Avoid allocation in class overload check (#25440) (camc314)
- e420816 parser: Optimize trailing comma tracking (#25376) (camc314)
- 6529d1a minifier: Short-circuit scope check when alternate already determines result (#25318) (Armano)

### 📚 Documentation

- fe111eb allocator: Improve doc comments for `Arena` methods (#24922) (overlookmotel)
2026-08-10 09:57:10 +00:00
overlookmotel 22a433cbe4 test(allocator): add more tests for bump pointer calculation (#25365)
Add more tests for bump pointer calculation in `Arena::try_alloc_layout_fast_impl`.

These tests close a gap in test coverage - test that allocation is rejected when subtracting the requested allocation size from current cursor pointer produces 0 exactly (null pointer). All pass.
2026-08-09 13:33:05 +00:00
overlookmotel 24ecee3da1 refactor(allocator): Arena::alloc_with write through a &mut MaybeUninit (#24923)
Alter `Arena::alloc_with` and `Arena::try_alloc_with` to write through `&mut MaybeUninit<T>` instead of raw `NonNull<T>` pointers.

This is pure refactor at present. It reduces the unsafe code in these methods to a single line, and I feel makes it clearer.

Additional motivation is to give LLVM `noalias` information (`&mut MaybeUninit<T>` has the exclusivity guarantee of `&mut` which `NonNull` doesn't). At present compiler produces same assembly before/after this change, so it doesn't seem to capitalize on this `noalias` info, however it might in the future. The change I don't think has any possibility of making codegen worse, and it has at least _potential_ to make it better.

It may also be contributing to the gain in #25337.
2026-08-07 21:36:31 +00:00
overlookmotel fe111eb112 docs(allocator): improve doc comments for Arena methods (#24922)
Docs-only change. Add links in doc comments.
2026-08-07 21:25:01 +00:00
overlookmotel b3c1b308bc feat(allocator): add ArenaVec::push_mut (#25338)
Rust's stdlib recently added a new method `push_mut` to `Vec`.

https://doc.rust-lang.org/std/vec/struct.Vec.html#method.push_mut

Add the same method to `ArenaVec`, and also a `push_fast_mut` method, which is the counterpart to `push_fast`.

Before:

```rust
fn push_and_do<'a>(vec: &mut ArenaVec<'a, Expression<'a>>, expr: Expression<'a>) {
    vec.push(expr);
    let expr_mut_ref: &mut Expression<'a> = vec.last_mut().unwrap();
    do_something_with_expr(expr_mut_ref);
}
```

After:

```rust
fn push_and_do<'a>(vec: &mut ArenaVec<'a, Expression<'a>>, expr: Expression<'a>) {
    let expr_mut_ref: &mut Expression<'a> = vec.push_mut(expr);
    do_something_with_expr(expr_mut_ref);
}
```

`push_mut` does the same thing as `push` followed by `last`, but removes the `unwrap`, which compiler is unable to optimize out.
2026-08-06 12:05:48 +00:00
oxc-guard 45a17c25d1 release(crates): oxc v0.143.0 (#25250)
### 💥 BREAKING CHANGES

- 4120da0 semantic: [**BREAKING**] Return borrowed JSDoc so the parse cache works (#25186) (Connor Shea)
- f0a41c8 allocator: [**BREAKING**] Remove `ArenaBox::dangling` method (#25236) (overlookmotel)
- 067da8c ast: [**BREAKING**] Store single parameter in `TSIndexSignature::parameter` (#25154) (camc314)
- a0c7788 ast: [**BREAKING**] Change `TSIndexSignatureName::name` to `Ident` (#25150) (camc314)
- 1bdedd1 ast: [**BREAKING**] Introduce `ExportDeclaration`, `ExportFromDeclaration` (#25095) (camc314)
- c917f20 ast: [**BREAKING**] Introduce `ArrowFunctionBody` enum (#24987) (camc314)
- 7e1199c ast: [**BREAKING**] Remove conversion to `Box` from AST builder methods (#25038) (overlookmotel)

### 🚀 Features

- 3c8312e allocator: Make `Box` `Send` + `Sync` when the type it contains is (#25242) (overlookmotel)
- cb5bc08 ast: Derive `GetSpan` for `Comment` (#25147) (leaysgur)
- 8541e18 ast_tools: Generate minifier scope collector (#25088) (camc314)
- 5acdae2 ecmascript: Skip transparent TS expressions (#25072) (camc314)
- 85d0f4d napi: Add React Compiler transform binding (#24934) (Boshen)
- 70d6ece minifier: Fold safe integer exponentiation (#25009) (Dunqing)
- 73fc747 minifier: Enable termination statement optimization for do while (#24921) (Armano)
- 5eadfdb minifier: Fold safe integer division and remainder (#24981) (Dunqing)

### 🐛 Bug Fixes

- 6c9ee14 minifier: Preserve if-chain idempotency and correct side effects when alternate should not be moved (#25041) (Armano)
- 8a47ff3 ecmascript: Do not treat lone-surrogate strings as constants (#25084) (Dunqing)
- 468b970 isolated-declarations: Strip re-export attributes (#25090) (camc314)
- a70035d parser: Report export imports in namespaces (#25086) (camc314)
- 5d8ccf5 react_compiler: Preserve nonfatal diagnostics (#25066) (Boshen)
- d349e4a react_compiler: Match eslint suppression gating (#25068) (Boshen)
- 0dc16ba react_compiler: Track catch block declarations (#25067) (Boshen)
- e6734a5 minifier: Fold typeof comparisons with known object strings (#25017) (Dunqing)
- 3a9d5a5 minifier: Keep `typeof foo == ['object']` as-is (#24941) (sapphi-red)
- 10ec276 ast: Custom AST builder methods take `IntoIn<ArenaBox>` where child method does (#25033) (overlookmotel)
- fbe8d31 minifier: Respect targets when folding RegExp source (#24790) (Dunqing)

### ⚡ Performance

- fb402b0 minifier: Stop re-deriving printed numeric spellings (#25103) (Dunqing)
- 7251e52 react_compiler: Avoid large types on stack (#25037) (overlookmotel)
- e07718f isolated_declarations: Avoid large types on stack (#25036) (overlookmotel)
- 9fb1e3e transformer: Avoid large types on stack (#25035) (overlookmotel)
- 8b80f8b parser: Avoid large types on stack (#25034) (overlookmotel)

### 📚 Documentation

- 2ea0aa9 ast: Correct grammar in AST builder methods doc comments (#25173) (overlookmotel)
2026-08-03 11:07:37 +00:00
overlookmotel 3c8312e6dc feat(allocator): make Box Send + Sync when the type it contains is (#25242)
`ArenaBox` was previously `!Send` and `!Sync`, because it contains a `NonNull` pointer.

`ArenaBox<T>` has the same semantics as a `&mut T`, so can be `Send` / `Sync` if `T` is. The also matches stdlib's `Box`.

Note: `ArenaVec` is _not_ `Send` because it contains an `&Allocator` reference and `Allocator` is not `Sync`. But `ArenaBox` contains nothing but the pointer - it does not contain an `&Allocator` - so it doesn't share `ArenaVec`'s restriction.

This is a further step towards making the whole AST `Sync`, so ASTs can be shared across threads. After this PR, the only remaining blocker is the `Cell`s in AST for semantic IDs (e.g. `Cell<NodeId>`).
2026-08-03 10:06:49 +00:00
overlookmotel f0a41c879a feat(allocator)!: remove ArenaBox::dangling method (#25236)
Remove the `ArenaBox::dangling` method.

This was a hack only required for checking enum discriminants in code generated by `inherit_variants!` macro. That code was removed in #23960, so `ArenaBox::dangling` can now be removed too.

Any code which for some reason needs to create an invalid `ArenaBox` with a dangling pointer can do so with `ArenaBox::from_non_null(NonNull::dangling())`.
2026-08-03 09:46:33 +00:00
overlookmotel dd3b56130c refactor(allocator): reduce unsafe code in CloneIn impl for Bitset (#25235)
`Layout::for_value` here does exactly the same as the previous unsafe code.
2026-08-03 08:48:41 +00:00
overlookmotel ec7018ef4f refactor(allocator, str): use impl GetAllocator instead of generic params (#25177)
Pure refactor. Along the same lines as #25174 and other PRs in this stack.

Replace generic params with `impl` for `GetAllocator` in methods of various types and traits across `oxc_allocator` and `oxc_str`, to make the code easier to read.
2026-08-01 15:40:56 +00:00
oxc-guard[bot] fc702c1fa9 release(crates): oxc v0.142.0 (#24977)
### 💥 BREAKING CHANGES

- 1ac5ac7 ast: [**BREAKING**] Tighten bound on `AstBuild` trait (#24925)
(overlookmotel)

### 🚀 Features

- c7c5696 minifier: Fold parenthesized boolean ternary tests (#24968)
(Dunqing)
- 82c5806 minifier: Fold sequence branches in boolean ternaries (#24943)
(Dunqing)
- cc623d9 ast_visit: Add VisitJsMut visitor (#24828) (Boshen)
- b31713a minifier: Add new IsTerminated trait and use it in exit-point
optimization (#24950) (Armano)
- e9e0f24 minifier: Shorten integer zero comparisons (#24933) (Dunqing)
- 34f5559 minifier: Convert switch with one case to if statement
(#24611) (Armano)
- 9107e74 str: Add `static_str!` macro (#24887) (overlookmotel)
- 4d6f623 parser: Emit error for type args in JS files (#24896)
(camc314)
- ec08afc ast: Re-export `Ident` from `oxc_ast` crate (#24861)
(overlookmotel)
- 73cfdfa oxc: Re-export `oxc_str` types from `oxc` crate (#24860)
(overlookmotel)
- 11f5d1f ast_visit: Add `Utf8ToUtf16::convert_program_and_comments`
(#24859) (overlookmotel)

### 🐛 Bug Fixes

- cb5acfb minifier: Optimize termination statements in labeled
statements (#24813) (Armano)
- 4618300 minifier: Avoid longer numeric constant folds (#24875)
(Dunqing)
- f7971f1 minifier: Keep side effects in typeof comparisons (#24940)
(sapphi-red)
- 4597d6f minifier: Keep side effects when compressing `== true` to `==
1` (#24939) (sapphi-red)
- c3a48df minifier: Keep side effects values in template literals
(#24938) (sapphi-red)
- e19f183 minifier: Keep side effects when folding `Number` call
(#24937) (sapphi-red)
- 9d7745a react-compiler: Preserve warning bailouts (#24935) (Boshen)
- 0126aba codegen: Preserve orphaned file coverage comments (#24815)
(Dunqing)
- 9fbbaf8 parser: Clarify return in class static blocks (#24899)
(camc314)
- e7d5c78 str: Fix hygiene of `format_ident!` and `format_str!` macros
(#24886) (overlookmotel)
- 07e9acc ecmascript: Keep side effects in parent of `toString` calls
(#24846) (sapphi-red)
- 16a40ad ecmascript: Detect side effects in unused arguments to
`charCodeAt` (#24845) (sapphi-red)
- 5b4ae54 parser: Use specific for-await diagnostic (#24856) (camc314)
- ffcc33a parser: Report for-await in non-async functions (#24855)
(camc314)
- df230d0 minifier: Avoid reordering this in nested class keys (#24785)
(Dunqing)
- d24b76b minifier: Isolate derived constructor this state (#24784)
(Dunqing)
- 582805d parser: Parse unary conditional after satisfies (#24807)
(Boshen)
- 23b904f minifier: Preserve default parameter side effects (#24791)
(Dunqing)
- 3048594 parser: Use correct diagnostic for `await using` in bare
switch case (#24798) (camc314)
- e80574f estree: Handle empty spans serializing `ImportMeta` and
`NewTarget` (#24775) (overlookmotel)
- 4b12840 minifier: Do not fold a `-0` ternary branch to `+a` (loses the
sign) (#23637) (Jerry Zhao)
- 133bb35 ecmascript: Detect side effects of `foo().#bar` correctly when
PropertyReadSideEffects::None (#24769) (sapphi-red)

### ⚡ Performance

- a086d91 ecmascript: Avoid number literal allocations (#24889)
(Dunqing)
- 98f2ecb react_compiler: Compare value set pointers before probing in
state merge (#24805) (Marius Schulz)
- b287f29 estree: Avoid ident hash calculations when serializing
(#24774) (overlookmotel)

### 📚 Documentation

- 4d2e2bf ast: Fix incorrect return value in `common_js_require` docs
(#24945) (connorshea)
- b539e74 estree: Document custom serializers for `ImportMeta` and
`NewTarget` (#24777) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-07-27 23:07:40 +08:00
oxc-guard[bot] 14533a3dc1 release(crates): oxc v0.141.0 (#24714)
### 💥 BREAKING CHANGES

- 54cc121 ast: [**BREAKING**] Split `MetaProperty` into `ImportMeta` and
`NewTarget` (#24557) (camc314)

### 🚀 Features

- 4c71560 parser: More friendly error for spread element in dynamic
imports (#24705) (sapphi-red)
- 7b045cd minfier: Drop last break from last switch case (#24673)
(Armano)
- 7d3c178 minifier: Remove unreachable recursive functions (#24125)
(Dunqing)
- 94f99b3 ast: Allow `NONE` to be passed to AST builder methods where
`Option<ArenaVec>` is expected (#24629) (overlookmotel)
- 77230c5 ast: Accept arrays for `ArenaVec` params of AST builder
methods (#24621) (overlookmotel)
- f08b152 allocator: Implement `FromIn` for array to `Vec` conversion
(#24620) (overlookmotel)
- 2338c13 track-memory-allocations: Track heap deallocs, alloc bytes,
and peak growth (#24619) (Boshen)
- 7aa4739 syntax,transformer: Move JSX entity decoder to `oxc_syntax`
(#24617) (camc314)
- 2b097c4 str: Export `Str` as `ArenaStr` (#24604) (overlookmotel)
- 3acf4c1 minifier: Expand switch optimiation to remove empty cases
(#24520) (Armano)
- 129b759 parser: Improve diagnostics for unparenthesized LHS on
exponential expr (#24569) (camc314)
- 4d0c601 minifier: Fold arithmetic over undefined and null operands
(#24485) (Dunqing)
- 91541dd minifier: Drop empty switch statements  (#24527) (Armano)
- d05224d ast_visit: Generate VisitJs visitor that skips TypeScript
type-space (#24499) (Boshen)
- 3d22307 parser: Add `ParseOptions::enable_ident_hashes` (#24491)
(Boshen)

### 🐛 Bug Fixes

- 64c2241 minifier: Align class heritage removal with assumptions
(#24533) (Dunqing)
- 48b59f4 parser: Span ambient generator diagnostics (#24711) (camc314)
- e750a82 ecmascript: Fix false negative for may_have_side_effects on
dynamic property access (#24709) (sapphi-red)
- f145d73 minifier: Guard reordered identifier reads (#24698) (Dunqing)
- a2ef382 isolated-declarations: Reject `window.Symbol` as global symbol
reference (#24689) (camc314)
- b1bcf72 minifier: Invalidate facts for redeclared bindings (#24658)
(Dunqing)
- 921b834 minifier: Don't treat a conditionally-assigned var as
write-once (#24650) (Dunqing)
- 061af1f minifier: Avoid stale pure function summaries (#24636)
(Dunqing)
- 40c2f43 allocator: `Vec::from_array_in` do not allocate zero-length
array (#24628) (overlookmotel)
- 70994ae codegen: Preserve comments before expression operands (#24510)
(Dunqing)
- 7b4baff parser: Reject new import member access (#23459) (camc314)
- 128b385 minifier: Clippy warning with no-debug-assertions (#24547)
(camc314)
- 8421feb parser: Use first `as` span for imported name (#24537)
(leaysgur)
- c517aa0 parser: Reject invalid accessor assertions (#24504) (camc314)

### ⚡ Performance

- 884d9eb parser: Pre-size cover-grammar assignment target buffers
(#24683) (Boshen)
- d3f07a0 diagnostics: Box OxcDiagnosticInner to reduce binary size
(#24665) (Boshen)
- bcc9de0 parser: Defer diagnostic creation until parse exit (#24663)
(Boshen)
- c35d8ab allocator: Mark `ReplaceWith` panic path cold (#24515)
(camc314)
- ba65790 semantic, allocator: Branchless `clone_in` for semantic IDs
(#24564) (overlookmotel)
- 747feec parser: Build AST nodes with the AST builder instead of
cloning (#24540) (Boshen)
- ba35a0d react_compiler: Use IndexVec for dense id-keyed maps (#24549)
(Boshen)
- b685062 react_compiler: Keep small hot-path collections inline
(#24514) (Marius Schulz)
- a149e95 transformer: Outline rare expression exits (#24512) (camc314)
- 7808a6e react_compiler: Make aliasing effects cheap to intern and
clone (#24506) (Marius Schulz)
- 3a36f2a react_compiler: Store AbstractValue reasons as a u16 bitmask
(#24480) (Boshen)
- 1c96753 react_compiler: Use FxHashMap for the lookup-only aliasing
node map (#24490) (Boshen)

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-07-21 16:09:41 +01:00
overlookmotel 2e24c7e31c perf(allocator): speed up CloneIn for Vec and Box<[T]> (#24733)
Speed up `CloneIn` for `Vec<T>` and `Box<[T]>` by constructing source and destination slices and passing them to a separate function to clone between.

This formulation tells LLVM that source and destination cannot overlap, which causes it to generate more efficient code. When `T` is a `Copy` or trivially-clonable type with no padding bytes (e.g. `Vec<'a, NodeId>`), the entire clone operation is squashed down to a single `memcpy` call - which is much faster than iterating through the source item by item.
2026-07-20 18:42:54 +00:00
overlookmotel effd3c8cca feat(allocator): add Vec::from_raw_parts_in (#24732)
Add a public `Vec::from_raw_parts_in` method, which replicates the aleady existing API in `InnerVec`.
2026-07-20 18:42:53 +00:00
overlookmotel d8abaa1fdb refactor(allocator): RawVec::from_raw_parts_in take a NonNull pointer (#24731)
`RawVec::from_raw_parts_in` and `InnerVec::from_raw_parts_in`  take a `NonNull<T>` pointer, instead of a `*mut T` pointer.

`RawVec` ultimately uses `NonNull` internally, and both callers `Vec::from_value_in` and `Vec::from_array_in` have a `NonNull` pointer to start with. So this change avoid converting `NonNull<T>` to `*mut T` only to convert it back to `NonNull<T>` with an unsafe call.
2026-07-20 18:42:53 +00:00
overlookmotel 10fd38eab4 test(allocator, data_structures, str): use implements! macro in tests (#24722)
Use `implements!` macro (introduced in #24721) in various tests, including adding some new ones.

The most valuable change is replacing a `compile_fail` doctest which checks that `Arena` is not `Sync`. Using `assert!(implements!(Arena: !Sync));` is more robust.
2026-07-20 12:36:14 +00:00
overlookmotel 5293431203 feat(str)!: move IdentBuildHasher to oxc_str crate (#24717)
Move all `Ident`\-hashing code from `oxc_allocator` into `oxc_str`, to sit alongside `Ident`'s code.

This feels like the natural place for this code, and reduces the public API surface - we no longer need to expose `Ident`'s internals (`ident_hash`). Now only `IdentHasher` and `IdentBuildHasher` are exposed.

Only change to code is removing `pack_len_hash`, as it was only used in tests.
2026-07-20 10:48:48 +00:00
overlookmotel f08b152b48 feat(allocator): implement FromIn for array to Vec conversion (#24620)
Implement `FromIn` to convert from `[T; N]` array to an `ArenaVec<'a, T>`.

This is used in next PR #24621 to make AST builder methods take arrays.
2026-07-17 13:49:38 +00:00
overlookmotel 40c2f4307c fix(allocator): Vec::from_array_in do not allocate zero-length array (#24628)
`Vec::from_array_in` doesn't need to allocate when the array passed to it is zero-length.

This wasn't unsound as `Allocator` accepts zero-sized allocations, but it was pointless. Allocating a zero-sized type still aligns the bump cursor to the type's alignment, which is an allocation of sorts - it's not zero-cost.

Just delegate to `Vec::new_in` instead.
2026-07-17 13:49:38 +00:00
Boshen 2338c13f60 feat(track-memory-allocations): track heap deallocs, alloc bytes, and peak growth (#24619)
Adds three heap-side metrics to the allocation snapshots, using the metric machinery from #24587:

- `sys deallocs` (exact count) — system deallocations per stage; read against `sys allocs` it shows whether a stage churns or retains heap.
- `sys alloc bytes` — cumulative bytes requested from the system allocator per stage (allocation sizes plus reallocation growth).
- `sys peak growth` — high-water mark of live heap during a stage relative to the live bytes at stage start, via live-bytes accounting in the `GlobalAlloc` wrapper. Captures transient heap a stage needs even when it frees it before finishing.

Arena chunk operations are excluded from all `sys` metrics at the source: `oxc_allocator` marks each chunk allocation/deallocation immediately before calling the system allocator (task-only `track_allocations` feature, compiled out everywhere else), and the task's tracking allocator consumes the marker and skips the call. This replaces the previous global chunk counter + per-stage subtraction: a subtraction can correct counts but not a high-water mark, and chunk sizes are quantized and platform-dependent (whether an arena needs one more chunk depends on byte totals that vary across architectures with type layout — the #22621 mechanism). The first CI run demonstrated this concretely: kitchen-sink's minifier peak differed deterministically by 720 kB between aarch64 and x86_64 because the mangler's temporary arena crossed a chunk-growth boundary only on x86_64; and antd's formatter "peak" was 762 MB of main-arena chunk-doubling history rather than formatter behavior (now 13.8 MB of actual heap use).

Cross-platform tolerances: the cumulative byte metrics still drift with the number of heap hashbrown tables allocated (their control-group width is architecture-dependent), so they use a relative tolerance (1%, 4 kB floor) via the committed-value comparison from #24565; `arena size` keeps its tight 1024-byte tolerance.

Sample values for checker.ts (2.9 MB source): parser needs only 2.4 kB of transient heap (fully arena-based), semantic peaks at 3.9 MB, the minifier is the pipeline's largest heap consumer at 10.2 MB, and the formatter requests 5.9 MB total while peaking at 3 MB (heavy alloc/free churn).

🤖 Implemented with [Claude Code](https://claude.com/claude-code).
2026-07-17 12:51:14 +00:00
camc314 c35d8abb24 perf(allocator): mark ReplaceWith panic path cold (#24515)
## Summary

Mark ReplaceWith's write_dummy helper as cold.

The helper is only reached while unwinding from a panic in a replacer closure. Telling the optimizer that this path is cold helps keep its allocator and dummy-construction code out of hot code layout and improves inlining decisions in unwind builds.
2026-07-17 07:08:05 +00:00
overlookmotel ba65790029 perf(semantic, allocator): branchless clone_in for semantic IDs (#24564)
Follow-on after #24422.

Make cloning semantic IDs (`NodeId`, `ScopeId` etc) use branchless arithmetic.

* Cloning `NodeId` now just uses a single OR operation to take into account whether to preserve the existing ID (`clone_in_with_semantic_ids`) or substitute a dummy ID (`clone_in`).
* Cloning `Cell<Option<ScopeId>>` is 2 instructions (NOT + AND) on x86_64, or just 1 on aarch64 (NOT-AND).

https://godbolt.org/z/1KWdc5hnb

The mechanism is a type `CloneInSemanticIds` which is used as `with_semantic_ids` instead of a `bool`:

```rs
#[repr(u32)]
pub enum CloneInSemanticIds {
    With = 0,
    Without = u32::MAX,
}
```

This is 4 bytes, rather than `bool`'s 1 byte, but that makes no difference - either way it takes 1 register to pass between functions. The choice of discriminants is what enables the cheap arithmetic.

This is even smaller in terms of binary size than the previous solution of marking `Cell::clone_in_impl` as `#[inline(never)]` (OR instruction is smaller than function call), and it's more performant as it loses the function call overhead. It also keeps `Cell`'s blanket implementation of `CloneIn` generalized.

Between #24422 and this PR, merging `CloneIn` with/without semantic IDs into a single path has gained us a large binary size reduction, and lost us nothing - or may even have gained us a marginal perf improvement.

No visible change on our benchmarks, as we don't use `CloneIn` much, but it may well be a measurable improvement for Rolldown, which clones whole ASTs.
2026-07-16 04:14:55 +00:00
Boshen f297654a68 refactor(allocator, ast_tools): generate a single CloneIn traversal via a runtime flag (#24422)
## Summary

The `CloneIn` derive generated **two near-identical traversals over the whole AST** — `clone_in` (resets semantic ids to their default) and `clone_in_with_semantic_ids` (preserves them). Almost every node carries an id field, so the two bodies diverge nearly everywhere and the pair roughly doubled the clone codegen.

This threads *"preserve semantic ids?"* as a **runtime `bool`** through a **single** traversal:

- **`CloneIn::clone_in_impl(&self, with_semantic_ids: bool, allocator)` is the one required method.** `clone_in` / `clone_in_with_semantic_ids` are `#[inline(always)]` default wrappers that call it with `false` / `true`. Hand-written impls provide only `clone_in_impl`.
- The **derive emits a single `clone_in_impl` body per type** — no per-type `clone_in` / `clone_in_with_semantic_ids` wrappers.
- Semantic ids live in `Cell<NodeId>` / `Cell<Option<{Scope,Symbol,Reference}Id>>`. **`Cell<T: Copy + Default>::clone_in_impl` threads the flag**: preserve (`Cell::new(self.get())`) when `with_semantic_ids`, otherwise reset to the default (`Cell::new(T::default())` = `NodeId::DUMMY` / `None`). So the derive emits a plain `clone_in_impl(&self.node_id, …)` per id field — no per-field `if/else`.
- **`Cell::clone_in_impl` is `#[inline(never)]`** so the (for some consumers dead) preserve branch isn't inlined at every id-field site — see Results.

## Results

Measured `f736d87e77` → `db9f2ab41a` on this branch (release, stripped, fat-LTO):

| | before | after | Δ |
|---|--:|--:|--:|
| `oxc_ast` generated `CloneIn` (source lines) | 7,180 | 5,581 | **−1,599 (−22%)** |
| oxlint binary | 14,192,816 B | 14,126,656 B | **−66,160 B (−64.6 KiB)** |
| napi transform cdylib (`--features allocator`) | 3,633,312 B | 3,633,312 B | **0 (neutral)** |

The two binaries move differently because they clone differently:

- **oxlint / rolldown clone *with* semantic ids** (react compiler on `TSType`; whole-`Program` clones). They already carried chunks of both traversals; merging dedups them → **−64.6 KiB**.
- **napi transform / parse clone *without* ids** — `oxc_transformer` never calls `clone_in_with_semantic_ids`, so before this PR `--gc-sections` dropped that whole half. The merged traversal would force the preserve branch live at every id field (~+16 KiB); `#[inline(never)]` on the `Cell` leaf outlines it to one function per `Cell<T>`, keeping these binaries **flat**.

## Behavior & perf

Behavior is identical by construction — `clone_in` resets ids (`Cell::new(None)` / `Cell::new(DUMMY)`), `clone_in_with_semantic_ids` preserves them. The runtime flag is loop-invariant and well predicted; the `#[inline(never)]` leaf adds one outlined call per id-cell on the clone path (expected negligible — worth a benchmark spot-check). Generated files were produced by `just ast`.

---
🤖 AI-assisted (Claude Code), reviewed and measured by the author.
2026-07-14 15:49:54 +00:00
oxc-guard[bot] 8e0ed2ebb9 release(crates): oxc v0.140.0 (#24453)
### 🚀 Features

- 616bfa2 minifier: Remove unreachable code after terminating statements
(#24441) (Dunqing)
- ddab89a data_structures: Add `likely` and `unlikely` functions
(#24368) (overlookmotel)
- a3a39f9 react_compiler: Implement enableEmitHookGuards codegen
(#24329) (Boshen)
- b79eef7 minifier: Apply De Morgan's law to negated comparison chains
in jump guards and loop tests (#24279) (Dunqing)
- 34ff7b4 minifier: Drop write-only property assignments to unused local
bindings by default (#24112) (Dunqing)
- 1b829d8 semantic: Record const enums in EnumData (#24268) (Dunqing)
- ba0944c semantic: Add `Scoping::set_symbol_span` (#24221) (camc314)

### 🐛 Bug Fixes

- 7d33363 minifier: Preserve guaranteed throws from class heritage
evaluation (#24349) (Dunqing)
- 058a62f semantic: Track ambient contexts in `SemanticBuilder` (#24327)
(camc314)
- 721eb0b transformer/decorator: Scope accessor class binding (#24330)
(camc314)
- 1ebdce3 semantic: Allow reserved keywords in ambient declaration types
(#24325) (camc314)
- 460176a track-memory-allocations: Exclude arena chunks from Sys allocs
(#24292) (Dunqing)
- af4922b transformer: Clear lowered namespace redeclarations (#24300)
(camc314)
- ffd2765 semantic: Mark declared computed `MethodDefinition`s as type
references (#24296) (camc314)
- f17514b isolated-declarations: Emit const readonly fields as types
(#24288) (camc314)
- 40f769d minifier: Make `__proto__` write tracking execution-order
independent (#24280) (Dunqing)
- 6371fed transformer: Remove stale enum member bindings (#24272)
(camc314)
- f05dfab transformer: Correct symbol flags for lowered namespaces
(#24271) (Dunqing)
- 84eeb55 transformer: Correct symbol flags for lowered enums (#24269)
(Dunqing)
- c3057da transformer: Preserve generated class binding spans (#24220)
(camc314)
- 8260096 transformer: Correct span for lowered namespace symbol
(#24222) (camc314)
- 42d00d3 semantic: Mark declared class heritage as type references
(#24237) (camc314)
- 588d997 semantic: Mark TS `PropertyDefinition`s computed fields as
type references (#24233) (camc314)
- 9b95632 semantic: Mark computed method keys in `TSMethodSignature`s as
type references (#24232) (camc314)

### ⚡ Performance

- 5b26643 transformer_plugins: Dispatch global defines by trailing name
(#23666) (Boshen)
- dce0f29 react_compiler: Replace all compiled functions in a single AST
walk (#24403) (Boshen)
- f85f0d8 ast: Delegate inherited enum variants in clone_in and estree
derives (#23555) (Boshen)
- 3ff0234 allocator: Remove `unwrap` from `ReplaceWith` (#24365)
(overlookmotel)
- ab22e80 transformer: Fix Rust 1.97 performance regression (#24354)
(camc314)
- b47585c parser: Use `ReplaceWith` instead of `TakeIn` (#24018)
(overlookmotel)
- b227a06 minifier: Use `ReplaceWith` instead of `TakeIn` (#24017)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-07-14 15:02:22 +08:00
Boshen d678236105 refactor(allocator): remove Layout::repeat polyfill (#24378)
Rust 1.95 stabilized [`Layout::repeat`](https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.repeat) (MSRV bumped in #24359), which the `UnstableLayoutMethods` polyfill trait in `bumpalo_alloc.rs` was waiting on (per its TODO comment).

It turns out the whole trait is deletable rather than just replaceable:

- The only `.repeat()` call outside the trait itself (`vec2/raw_vec.rs`) is inside a commented-out block.
- The live `Layout::array::<T>(n)` calls already resolve to the inherent std method (stable since 1.44), not the trait method.
- `padding_needed_for` was only used by the trait's own `repeat` impl.

Also drops the two `#![allow(unstable_name_collisions)]` attributes, which only existed because the trait methods collided with the then-unstable std inherent methods.

Net: -59 lines, no behavior change. `cargo check/clippy/test -p oxc_allocator` all clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-12 01:06:16 +00:00
Boshen b88909312d refactor(allocator): use stabilized AtomicUsize::update (#24382)
Rust 1.95 stabilized [`Atomic*::update`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicUsize.html#method.update) (MSRV bumped in #24359), the infallible sibling of `fetch_update` for closures that always produce a new value.

Both `fetch_update` call sites (`oxc_allocator/src/tracking.rs` and `tasks/track_memory_allocations`) used an always-`Some` closure, so they gain nothing from the fallible API — switching to `update` drops the ignored `Result` (`let _ =`) and the "cannot be `Err`" comments. Same CAS-loop semantics; `saturating_add` behavior unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-11 17:40:08 +00:00
overlookmotel 3ff02349b6 perf(allocator): remove unwrap from ReplaceWith (#24365)
#24359 bumped our MSRV to 1.95.0. We can now use `Vec::push_mut`.

Use it in `ReplaceWith` to remove an infallible `unwrap` which compiler previously couldn't prove was infallible. Previously it emitted a panicking branch, now it doesn't.

This code is on a cold path, so perf impact is miniscule, but why not use the best API for the job?
2026-07-10 19:58:48 +00:00
camc314 66e1a25c0e chore(all): fix clippy::manual_assert_eq (#24351) 2026-07-10 09:57:05 +00:00
Dunqing 460176ad05 fix(track-memory-allocations): exclude arena chunks from Sys allocs (#24292)
## Issue

The `Allocations` CI job can fail on PRs with a 1-allocation `Sys allocs` difference that local `cargo allocs` cannot reproduce. #22621 fixed one instance of this in the transformer stage. #24112 and #24017 hit the same failure again in the minifier stage: `kitchen-sink.tsx` measures 2309 Sys allocs on macOS/arm64 but 2310 on Linux/x64, so whichever platform regenerates the snapshot last breaks CI for the other.

## Why

The tracker counts every call to the system allocator. That includes the chunks bump arenas request. Whether an arena needs one more chunk depends on the total number of bytes in the arena, and byte totals are not the same on every target. For example, hashbrown tables use 16-byte control groups on x86_64 but 8-byte groups on aarch64. When an arena's content size sits close to a chunk boundary, one target crosses it and the other does not.

In the observed failure, the minifier's mangler-phase semantic build creates a fresh `Scoping`. Its arena holds symbol names, resolved-reference vectors, and the per-scope `bindings` hash tables, which are slightly larger on x86_64. The traced Linux-only allocation happened under `Scoping::add_resolved_reference -> ArenaVec::push -> Arena::new_chunk`, when the push overflowed the current chunk and requested a new 720880-byte chunk. On aarch64 the same content fit in the existing chunk, so the count stayed one lower.

Every other allocation class in the measurement grows on element counts, which are identical across platforms. Logging every allocation size during the minifier stage on macOS/arm64, Linux/arm64, and Linux/x64 showed the three sequences match one-to-one except for arena chunk requests.

Any PR that shifts arena content near a chunk boundary can trip this again in any stage, so fixing one stage at a time does not close the class.

## Fix

Count chunk allocations in `oxc_allocator` under the tracker-only `track_allocations` feature, and subtract them from the reported Sys allocs in the tracker. The counter is global rather than per-arena because short-lived arenas (like the one backing `Scoping`) are created and dropped inside the measured operation.

Arena usage is still measured by the `Arena allocs` / `Arena reallocs` columns; only the platform-sensitive chunk-request count is excluded from `Sys allocs`.

Parser and transformer rows are unchanged (the main arena is pre-warmed, and #22621 already removed transformer scoping growth). Semantic, minifier, and formatter rows drop by their per-stage chunk counts.

## Validation

`cargo allocs` produces byte-identical snapshots on macOS/arm64, Linux/arm64 (Docker), and Linux/x64 (Docker), and is idempotent across reruns. `cargo test -p oxc_allocator` passes; clippy is clean with `track_allocations` both on and off, and the counter compiles out entirely without the feature.

AI usage: AI assistance was used to investigate, implement, and validate this change.
2026-07-08 14:22:57 +00:00
oxc-guard[bot] 69b2dfc181 release(crates): oxc v0.139.0 (#24218)
### 🚀 Features

- 260425f semantic/examples: Include unresolved references (#24214)
(camc314)
- 2d9b0b3 minifier: Fold boolean-literal ternary branches in value
contexts (#24110) (Dunqing)
- 61fbf10 ast: Implement `ReplaceWith` on all AST types (#24013)
(overlookmotel)
- 7db7a29 allocator: Add `ReplaceWith` trait (#24012) (overlookmotel)
- 4eb074e mangler: Add `reserved` option for names that must not be
mangled (#24041) (Dunqing)
- 2e62012 data_structures: Add `StringExt` trait (#24006)
(overlookmotel)
- 60e7160 minifier: Drop side-effect-free IIFEs whose result is unused
(#23967) (Dunqing)
- 26dd9e2 ast: Add method to widen inherited enum ref to parent ref
(#23961) (overlookmotel)

### 🐛 Bug Fixes

- e8b50ee transformer: Clean up semantics for stripped TypeScript syntax
(#24180) (camc314)
- d966d0b react_compiler: Remove clippy allows (#24168) (Boshen)
- 854ef8d react_compiler: Compile generic functions instead of
over-bailing on type-param hoisting (#24158) (Boshen)
- 093586c react_compiler: Align memoization cache-slot allocation with
Babel (#24157) (Boshen)
- 09c8f59 react_compiler: Normalize snapshot fixture paths (#24142)
(camc314)
- f13df97 react_compiler: Drop stray empty statement from catch bindings
(#24133) (Boshen)
- cb2a505 react_compiler: Codegen destructuring reassignment targets
(#24131) (Boshen)
- b82c394 react_compiler: Propagate codegen invariants instead of
emitting empty bodies (#24128) (Boshen)
- 5771982 react_compiler: Render unchanged programs as source in fixture
snapshots (#24129) (Boshen)
- 4b16e1a transformer/async-to-generator: Preserve direct eval scope
flags (#24136) (camc314)
- 4e9194f react_compiler: Lower `delete obj.prop` to
Property/ComputedDelete (#24123) (Boshen)
- 0b25582 ast: Type binding node `typeAnnotation` as `TSTypeAnnotation |
null` (#23113) (Boshen)
- 018c0e5 transformer: Hoist lowered async declarations (#22770)
(camc314)
- 652fbaf mangler: Keep names of destructured exported bindings (#24036)
(Dunqing)
- e274415 minifier: Don't drop global calls that throw despite pure
arguments (#23917) (Dunqing)
- 59abb30 minifier: Only merge string literals in `try_fold_add` when
the inner operator is `+` (#23622) (Jerry Zhao)

### ⚡ Performance

- c5ca77b transformer: Avoid cloning refresh options (#24191) (camc314)
- bf1a151 react_compiler: Compile out debug printers (#24184) (Boshen)
- abb44a0 transformer: Build fixed object-rest arguments (#24190)
(camc314)
- a4db731 isolated_declarations: Use `ReplaceWith` instead of `TakeIn`
(#24016) (overlookmotel)
- ff10855 transformer: Use `ReplaceWith` instead of `TakeIn` (#24015)
(overlookmotel)
- bd49aff ecmascript: Avoid heap-allocating Math.min/max/imul operands
(#23941) (Lawrence Lin)
- e4b708b react_compiler: Skip compiled files before prefilters (#24171)
(Boshen)
- c59f2fe rust: Return impl ExactSizeIterator from slice-backed
accessors (#24144) (Boshen)
- 5d6d04a codegen: SWAR-skip boring byte runs in sourcemap line/column
scan (#24023) (Boshen)
- a55e0be traverse: Reduce string operations in `get_var_name_from_node`
(#24007) (overlookmotel)
- e6d48e1 transformer/nullish_coalescing: Move cold path into separate
function (#23989) (overlookmotel)
- c4e35b5 transformer/object_rest_spread: Pre-allocate capacity in `Vec`
(#23988) (overlookmotel)
- 527b8e5 transformer/decorators: Narrow type earlier (#23987)
(overlookmotel)

### 📚 Documentation

- 30d17f5 allocator: Clarify docs for `TakeIn::take_in_box` (#24093)
(overlookmotel)
- 675e6a8 ast: Correct doc comment for `PrivateFieldExpression` (#24008)
(overlookmotel)
- e4c30e6 minifier: Explain what `dce` mode means (#23994) (Dunqing)
- 37cbf88 ast_macros: Document fields of `StructDetails` (#23959)
(overlookmotel)
- 4de3e54 ast: Correct doc comment (#23948) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-07-06 23:26:26 +08:00
overlookmotel 7db7a29aab feat(allocator): add ReplaceWith trait (#24012)
## The problem

A common pattern we have when mutating an AST (e.g. in transformer) is:

- Take a node from the AST.
- Wrap it in some other node, or extract a field from that node.
- Write the result back into the same place in AST the original came from.

Rust does not make this easy, due to aliasing rules. Currently we do something like this (example from arrow function transform):

```rs
let Statement::ExpressionStatement(expr_stmt) = match stmt else { unreachable!() };
let expr = expr_stmt.expression.take_in(ctx);
*stmt = Statement::new_return_statement(expr.span(), Some(expr), ctx);
```

The problem with this is that `take_in` writes a dummy `Expression` into the AST, in order to get the owned `Expression`. This is pointless, as the whole `Statement` that contains that dummy `Expression` is overwritten in the next line - the dummy is immediately disconnected from the AST. But that dummy node lives on forever in the arena, consuming memory.

## Solution in this PR

Introduce `ReplaceWith` trait which replaces the node in place, without writing a dummy:

```rs
stmt.replace_with(|stmt| {
    let Statement::ExpressionStatement(expr_stmt) = stmt else { unreachable!() };
    let expr = expr_stmt.unbox().expression;
    Statement::new_return_statement(expr.span(), Some(expr), ctx)
});
```

The closure receives an owned copy of the `Statement`, and can do whatever it needs to with it. The closure has to return a new `Statement` which is written to the "hole" the original came from.

This PR only adds the trait. Later PRs in this stack implement the trait on AST types, and then use it in various crates.

## Implementation

The difficulty is in making this sound even if the closure panics, and `std::panic::catch_unwind` is in use, allowing the old value to be observed.

A naive implementation would allow UB as follows:

```rs
use std::panic::{AssertUnwindSafe, catch_unwind};
use oxc_allocator::{Allocator, ArenaBox, ReplaceWith};

let allocator = Allocator::new();
let allocator = &allocator;

let mut boxed: ArenaBox<u32> = ArenaBox::new_in(1, &allocator);
let mut copy: Option<ArenaBox<u32>> = None;

let _ = catch_unwind(AssertUnwindSafe(|| {
    boxed.replace_with(|old| {
        // Move the copy out of the closure
        copy = Some(old);
        panic!("Unwind before write-back");
    });
}));

let mut copy: ArenaBox<u32> = copy.unwrap();
let boxed_mut: &mut u32 = boxed.as_mut();
let copy_mut: &mut u32 = copy.as_mut();
// `boxed_mut` and `copy_mut` both point to same `u32`.
// We have 2 `&mut u32`s pointing to same place. Aliasing violation!
*boxed_mut = 2;
*copy_mut = 3;
```

The solution is to make it possible to implement `ReplaceWith` only on types that also implement `Dummy`.

If the closure panics, a guard is triggered which writes a dummy into the original slot which the type was moved out of. This happens before any other code can observe the value in the slot.

To keep the API ergonomic, we don't want to have to pass in an `&Allocator` to `replace_with`. But the dummy has to be allocated *somewhere*. So we use a global store of `Allocator`s, stored in a `static`, which live for the entire life of the process. When a panic occurs, a new `Allocator` is created, and the dummy is allocated in it.

This is a memory leak - the `Allocator` containing the dummy will never be freed until the process exits.

However, in practice this is fine, because:

#### 1. It shouldn't happen

The guard only triggers, and these leaked `Allocator`s are only created, if the closure panics - which it never should. If the closure *does* panic, that's a bug, and we'll aim to fix it swiftly.

#### 2. Panic usually exits process

It's unusual to use `catch_unwind`. Generally a panic is not caught and it causes the process to exit. So an `Allocator` can be leaked, but only for the few milliseconds before the process exits, and it's freed again.

#### 3. `panic = "abort"`

Oxc's apps (Oxlint, Oxfmt) and NAPI packages (`oxc-parser` etc) are compiled with `panic = "abort"`.

With `panic = "abort"`, panics are converted to process abort. In that case, the compiler sees that the guard can never fire. It removes the guard and all the code which creates `Allocator`s and allocates dummies into them. So all these mechanics are completely zero cost, and no leak ever occurs.
2026-07-05 14:44:06 +00:00
overlookmotel 30d17f5864 docs(allocator): clarify docs for TakeIn::take_in_box (#24093)
Just clarify doc comment, explaining what the method does.
2026-07-02 20:31:37 +00:00
oxc-guard[bot] 8bf69a3076 release(crates): oxc v0.138.0 (#23934)
### 💥 BREAKING CHANGES

- 94fbacb ast: [**BREAKING**] Only export `AstBuilder` and `NONE` in
`builder` module (#23876) (overlookmotel)
- 8de5122 ecmascript: [**BREAKING**] Switch to new `AstBuilder` (#23834)
(overlookmotel)
- dc0ef38 transformer: [**BREAKING**] Switch to new `AstBuilder`
(#23831) (overlookmotel)
- 88f4455 str: [**BREAKING**] `Str` and `Ident` methods take
`&GetAllocator` (#23781) (overlookmotel)
- 36009dd allocator: [**BREAKING**] `GetAllocator::allocator` take
`&self` (#23676) (overlookmotel)
- bd74f9d allocator: [**BREAKING**] Rename `AllocatorAccessor` trait to
`GetAllocator` (#23675) (overlookmotel)

### 🚀 Features

- 326fe25 transformer_plugins: Support `typeof` `define` keys (#23605)
(Alexander Lichter)
- f2091b3 ast: Unify old and new `AstBuilder`s (#23875) (overlookmotel)
- cd1fd12 codegen: Expose `Codegen::print_string` API (#23785) (camc314)
- 785461b ast: Add custom builder methods to AST types (#23651)
(overlookmotel)
- 05d1357 ast: Add AST creation methods to AST types (#23650)
(overlookmotel)
- 2580eda str: Add `Str::from_str_in` and `Ident::from_str_in` methods
(#23767) (overlookmotel)
- 6883fcf minifier: Fold write-once falsy var to false in boolean
context (#23540) (Dunqing)
- fcbf993 allocator: Add `Vec::from_value_in` method (#23718)
(overlookmotel)
- 989ddb7 allocator: Add `Vec::from_box_in` method (#23717)
(overlookmotel)
- 9d1aa7f allocator: Improve `PartialEq` for `Vec` (#23716)
(overlookmotel)

### 🐛 Bug Fixes

- da0e5bf minifier: Don't reorder a closed-over TDZ read when inlining a
var (#23771) (Dunqing)
- 0b3021f allocator: Remove `Vec::from_box_in` (#23873) (overlookmotel)
- 0ab64ec ast: Silence deprecation warnings within files defining
deprecated `AstBuilder` methods (#23889) (overlookmotel)
- 8c07cad all: Enable `disable_old_builder` Cargo feature for `oxc_ast`
crate in tests (#23888) (overlookmotel)
- 3800f01 ast: Legacy `AstBuilder` methods take `self` not `&self`
(#23891) (overlookmotel)
- 869ac20 semantic/cfg: Connect for update exit to loop test (#23791)
(camc314)
- d3e92d5 semantic/cfg: Connect while branches from condition exit
(#23790) (camc314)
- 025045d ast: `ExportNamedDeclaration` plain builder methods return
boxed nodes (#23783) (overlookmotel)
- 7537c58 ast: Fix name of `AstBuilder` method for
`Expression::V8IntrinsicExpression` (#23766) (overlookmotel)
- 3f574f5 traverse: Fix unsoundness in `Traverse` walk functions
(#23745) (overlookmotel)
- 585760f parser: String in AST reference arena (#23721) (overlookmotel)
- 7231d55 allocator: Fix unsound lifetime extension in `Box::new_in`
(#23685) (overlookmotel)

### ⚡ Performance

- d5c916a semantic: Flatten hoisting_variables to avoid per-scope map
allocation (#23927) (Lawrence Lin)
- e71609d minifier: Bail member-expr folding before the side-effect walk
(#23924) (Lawrence Lin)
- e1f89ab minifier: Reduce string allocations folding addition (#23846)
(overlookmotel)
- 9f6ee3b isolated-declarations: Pool scope maps to avoid per-scope
alloc/rehash (#23761) (Boshen)
- 0b07c4c semantic: Avoid heap alloc for catch-clause binding ids
(#23911) (Lawrence Lin)
- c5eef8b regular_expression: Skip capturing-group pre-parse when
pattern has no `(` (#23908) (Lawrence Lin)
- b4f5b4b isolated_declarations: Remove redundant clone of formal
parameter pattern (#23912) (Lawrence Lin)
- 53d083f isolated_declarations: Use `TakeIn` not `CloneIn` (#23847)
(overlookmotel)
- 3ea9304 react_compiler: Use faster API to arena allocate strings
(#23849) (overlookmotel)
- a6d8e45 parser: Avoid span lookup for arrow expression body (#23788)
(camc314)
- e1886a0 transformer, minifier: Use `static_ident!` macro to create
static `Ident`s (#23727) (overlookmotel)
- 5527bef transformer/object-rest-spread: Reduce iteration (#23720)
(overlookmotel)
- 680ffbc transformer: Allocate AST nodes in arena directly (#23711)
(overlookmotel)
- 1c63c66 parser: Allocate AST nodes in arena directly (#23712)
(overlookmotel)
- 3855f0c minifier: Allocate AST nodes in arena directly (#23710)
(overlookmotel)
- d025887 isolated_declarations: Allocate AST nodes in arena directly
(#23709) (overlookmotel)
- 10b96c6 parser: Remove string search from parsing JSX element name
(#23713) (overlookmotel)

### 📚 Documentation

- 3d61dea all: Correct capitalization in comments (#23887)
(overlookmotel)
- aa1ad74 ast: Add `#[deprecated]` to legacy `AstBuilder` methods
(#23877) (overlookmotel)
- a4676db ast: Correct doc comment for `NONE` (#23765) (overlookmotel)
- 419ec80 syntax: Fix typo in doc comment (#23674) (overlookmotel)

### 🛡️ Security

- 3cdd18f deps: Update npm packages (#23690) (renovate[bot])

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-06-29 20:11:00 +01:00
overlookmotel 0b3021f884 fix(allocator): remove Vec::from_box_in (#23873)
Remove this method, which was added in #23717. The method is sound, and could be useful in some cases, but I realised it's not forward-compatible with planned future changes to allocator. So I think best not to introduce it.

This would be a breaking change, but it was only added a few days ago, so hadn't got into a release yet.
2026-06-28 14:00:01 +00:00
overlookmotel 163ed51285 refactor(allocator): Box::new_in take &GetAllocator (#23756)
Part of #23043.

Same as #23755, but for `Box`.

`Box::new_in` previously took an `&'a Allocator`. Instead make it take an `&A where A: GetAllocator<'a>`.

This makes it more flexible - you can pass a reference to any type which implements `GetAllocator` e.g. `ParserImpl`, `TraverseCtx` etc.

```rust
// Before (`ctx` here is `&mut TraverseCtx`)
let boxed = Box::new_in(value, ctx.ast.allocator);
// After
let boxed = Box::new_in(value, ctx);
```

The main motivation is to make it a workable replacement for `AstBuilder::alloc`, which will be removed in the new `AstBuilder`:

```rust
// Before (`ctx` here is `&mut TraverseCtx`)
let boxed = ctx.ast.alloc(value);
// After
let boxed = ArenaBox::new_in(value, ctx);
// Instead of
let boxed = ArenaBox::new_in(value, ctx.ast.allocator);
```

This has the same advantages and disadvantages as `Vec` methods - see #23755.
2026-06-24 12:13:16 +00:00
overlookmotel 5201522945 refactor(allocator): Vec construction methods take &GetAllocator (#23755)
Part of #23043.

`Vec::new_in`, `Vec::with_capacity_in`, `Vec::from_array_in` etc previously took an `&'a Allocator`. Instead make them take an `&A where A: GetAllocator<'a>`.

This makes them more flexible - you can pass a reference to any type which implements `GetAllocator` e.g. `ParserImpl`, `TraverseCtx` etc.

```rust
// Before (`ctx` here is `&mut TraverseCtx`)
let vec = ArenaVec::new_in(ctx.ast.allocator);
// After
let vec = ArenaVec::new_in(ctx);
```

The main motivation is to make them workable replacements for what are currently methods on `AstBuilder`, but will be removed in the new `AstBuilder`:

```rust
// Before (`ctx` here is `&mut TraverseCtx`)
let vec = ctx.ast.vec();
// After
let vec = ArenaVec::new_in(ctx);
// Instead of
let vec = ArenaVec::new_in(ctx.ast.allocator);
```

This is still longer code than the original, which is not ideal, but it's as short as can make them.

The main downside of this change is that when calling these methods with an `&Allocator`, you have to double reference:

```rust
let allocator: &'a Allocator = get_allocator_somehow();
// Passing `&&Allocator`
let vec = ArenaVec::new_in(&allocator);
```

This has no perf impact - these methods are inlined, and compiler collapses the double-reference like it wasn't there, but it's just ugly. Unfortunately, there's no way to implement `GetAllocator` so it'll accept both `&Allocator` and `&mut TraverseCtx`, and the latter is the much more common use case.
2026-06-24 12:13:15 +00:00
overlookmotel 8c6481bbf9 refactor(allocator): export ArenaBox and ArenaVec aliases (#23746)
Pure refactor.

It's a common pattern in repo to import `Vec` and `Box` etc from `oxc_allocator` and alias them to `ArenaBox` / `ArenaVec`.

```rust
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
```

Make this pattern less verbose by re-exporting `Box` and `Vec` as these aliases from `oxc_allocator`.

Ditto other arena types: `ArenaHashMap`, `ArenaHashSet`, `ArenaStringBuilder`.

Use those aliases in all other crates to shorten code.

```rust
use oxc_allocator::{ArenaBox, ArenaVec};
```
2026-06-24 01:21:40 +00:00
overlookmotel fcbf993df3 feat(allocator): add Vec::from_value_in method (#23718)
Add `Vec::from_value_in` which creates a `Vec` containing only a single value.

This performs the same role as `AstBuilder::vec1`.

```rust
// These 2 are equivalent
let v = self.ast.vec1(value);
let v = Vec::from_value_in(value, self.ast.allocator);
```

Internally uses `Vec::from_box_in`, introduced in #23717.
2026-06-22 15:01:59 +00:00
overlookmotel 989ddb7fc4 feat(allocator): add Vec::from_box_in method (#23717)
Add a method `Vec::from_box_in` which converts a `Box<T>` to a `Vec<T>` containing only the single value, without reallocation or copying.
2026-06-22 15:01:59 +00:00
overlookmotel 9d1aa7fcb6 feat(allocator): improve PartialEq for Vec (#23716)
Improve `PartialEq` implementations on `oxc_allocator::Vec`, bringing it up to parity with `std::vec::Vec`.

Previously code like this would fail to compile due to missing implementations for comparing `Vec`s to slices:

```rust
let v = Vec::from_array_in([1, 2, 3], &allocator);
assert_eq!(v, [1, 2, 3]);
```
2026-06-22 15:01:58 +00:00
overlookmotel f129b013b3 refactor(allocator): rename lifetime in Box impl (#23693)
Pure refactor. Rename lifetime in `Box` impl, for consistency with rest of the code.
2026-06-22 00:50:55 +00:00
overlookmotel 7231d5542f fix(allocator): fix unsound lifetime extension in Box::new_in (#23685)
Fix unsoundness in `Box::new_in`.

`Box::new_in` had this signature:

```rust
impl<T> Box<'_, T> {
    pub fn new_in(value: T, allocator: &Allocator) -> Self {
        Self(NonNull::from(allocator.alloc(value)), PhantomData)
    }
}
```

The returned `Box<'alloc, T>`'s lifetime `'alloc` and the `&Allocator` borrow were two independent, unconstrained lifetimes. Nothing tied them together, so the caller was free to choose any `'alloc` - including one that outlives the allocator. In effect you could mint a `Box<'static, T>` from a temporary `Allocator`:

```rust
let boxed = {
    let allocator = Allocator::default();
    Box::new_in(5, &allocator)   // returns Box<'_, i32> with a lifetime unrelated to `allocator`
};                               // `allocator` dropped here, freeing the backing memory
assert_eq!(*boxed, 5);           // use after free
```

Fix by tying the 2 lifetimes together.

```rust
impl<'alloc, T> Box<'alloc, T> {
    pub fn new_in(value: T, allocator: &'alloc Allocator) -> Self { ... }
}
```

This bug crept in in #2943 and has gone unnoticed since - largely because we rarely use `Box::new_in` directly, and `AstBuilder::alloc` was correctly providing a tighter lifetime bound.
2026-06-21 16:18:56 +00:00
overlookmotel 36009ddf68 feat(allocator)!: GetAllocator::allocator take &self (#23676)
Make `GetAllocator::allocator` method take `&self`, instead of `self`. Alter `TakeIn::take_in` accordingly - now takes `&A` where`A:GetAllocator` (previously took owned `A`).

This requires changing every `take_in` / `take_in_box` call site.

However, this change also means that `GetAllocator` can be implemented on all types which hold an `&Allocator` e.g. `TraverseCtx`, which shortens the code everywhere.

- Before: `expr.take_in(ctx.ast)`
- After: `expr.take_in(ctx)`

Change all those call sites to this shorter version.

This matches the pattern that new `AstBuilder` is going to introduce (#23043), and clears the way for that change.
2026-06-21 00:13:24 +00:00
overlookmotel bd74f9d35c feat(allocator)!: rename AllocatorAccessor trait to GetAllocator (#23675)
Pure refactor.

Rename `AllocatorAccessor` trait to `GetAllocator`.

Rust's naming convention is that trait names are verbs, describing what you can _do_ with the trait (e.g. `Clone`, not `Cloneable`). `GetAllocator` follows this convention, and also follows the naming convention we have for other traits e.g. `GetSpan`.
2026-06-21 00:13:24 +00:00
oxc-guard[bot] 6d56d57535 release(crates): oxc v0.137.0 (#23591)
### 💥 BREAKING CHANGES

- 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a
runtime option (#23574) (overlookmotel)
- e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts
(#23573) (overlookmotel)

### 🚀 Features

- 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571)
(overlookmotel)
- 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378)
(sapphi-red)
- 53509a8 minifier: Treeshake pure typed arrays and Set/Map array
literals (#23469) (Dunqing)
- 09762d9 minifier: Inline const value for read-only vars (#22593)
(Dunqing)

### 🐛 Bug Fixes

- 20375f9 react_compiler: Keep imports referenced only by a computed key
(#23586) (Boshen)
- 31bfd9b minifier: Keep Object introspection calls on a possible Proxy
(#23483) (Dunqing)
- 837a395 parser: Treat a line comment after ':' as leading, not
trailing (#23515) (Dunqing)
- e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string
argument (#23470) (Dunqing)
- ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457)
(camc314)

### ⚡ Performance

- cf24329 mangler: Compile slot sort once instead of per CAPACITY
(#23577) (Boshen)
- 4058a6a parser: Reduce code bloat from verify_modifiers
monomorphization (#23576) (Boshen)
- 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel)
- dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace
lowering (#23516) (Yunfei He)
- 970e09a minifier: Compute template-literal inline checks in a single
pass (#23467) (Yunfei He)
- 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count
and reuse stats in mangler builds (#23352) (Boshen)
- d1fa6e0 minifier: Evaluate ternary branches once in
minimize_conditional_expression (#23479) (Yunfei He)
- 3fa8051 transformer: Pre-size JSX props vec to attribute count
(#23466) (Yunfei He)
- 488b382 react_compiler: Borrow binding names in prefilter instead of
allocating (#23471) (Yunfei He)
- bcb3894 minifier: Incremental scoping refresh, delete
LiveUsageCollector (#23197) (Dunqing)

### 📚 Documentation

- f68641e data_structures: Improve docs on safety contract (#23575)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-06-18 17:31:57 +08:00
overlookmotel 7a76cd3dc3 feat(estree)!: make whether to include TS fields a runtime option (#23574)
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).
2026-06-17 23:22:37 +00:00
Dunqing bcb3894d76 perf(minifier): incremental scoping refresh, delete LiveUsageCollector (#23197)
> Developed with AI assistance (Claude Code); reviewed, tested, and benchmarked by the contributor.

## Summary

Deletes the per-pass `LiveUsageCollector` walk over the entire live program. The typed mutation helpers (previous PR in this stack) now walk only the dropped/replaced subtrees in place, accumulating dead `ReferenceId`s into a per-pass `PassDirty` arena `BitSet` that the `Compressor` driver consumes in one batch (after `Normalize` and after each peephole pass) via the new `Scoping::retain_resolved_references_excluding`. Direct-eval flag refresh runs only when an `eval(...)` call was actually dropped.

Cross-crate support riding in this PR: `Scoping::retain_resolved_references_excluding` (oxc_semantic) and `BitSet::capacity()` / `is_empty()` / `contains()` (oxc_allocator).

## Breaking

Removes `oxc_semantic::Scoping::retain_resolved_references` — its only consumer was `LiveUsageCollector`, deleted here. No other in-repo or known downstream callers.

## Benchmarks (CodSpeed, this PR vs stack base)

| Benchmark | Base → Head | Efficiency |
|---|---|---|
| `minifier[kitchen-sink.tsx]` | 63.6 ms → 52.5 ms | **+21.2%** |
| `pipeline[kitchen-sink.tsx]` | 122.3 ms → 110.7 ms | +10.5% |
| `minifier[App.tsx]` | 12.5 ms → 12.1 ms | +3.3% |
| everything else | — | unchanged |

> **Why earlier runs showed ~2× on kitchen-sink:** the initial cut of this branch had a reference-leak bug — dropped subtrees weren't always marked dead, leaked references made symbols look used, and the minifier silently skipped most of kitchen-sink's DCE, producing output **22% larger than main** (invisible to minsize, which doesn't include that file). Those runs measured a minifier doing less work, not a faster minifier. The leak is fixed (see below); the numbers above are measured at byte-identical output to main.

Memory: kitchen-sink.tsx arena allocs 100230 → 90553, sys allocs 2917 → 2639; all other tracked files at or slightly below base.

## Relation to #22736

This re-cuts the approach prototyped and discussed in #22736 (kept open during development as the design record), with three deviations from the POC:

1. **Mark-only `DropDiff`** — the POC's "Resurrect" mode existed for one call site (`substitute_is_object_and_not_null`) that reused `ReferenceId`s in replacement values. That site now mints fresh references (same symbol/resolution, new id), so the resurrect walk and its cross-call aliasing invariant are gone entirely.
2. **`mutated: bool` instead of a `mutations: u64` snapshot counter** — after this PR the signal's only reader is the fixed-point loop, so a private bool with read-and-reset `take_mutated()` suffices.
3. **Bounds-checked mark path** — references minted mid-pass and dropped in the same pass are skipped (treated live → conservative output) instead of the POC's debug-assert + potential release panic.

Safety net: `debug_assert_no_over_prune` walks the live program in debug builds after each dirty pass, so the whole test + conformance corpus doubles as an over-prune detector at zero release cost.

## Reference-leak fix + simplification follow-ups

The initial cut leaked references at three classes of drop sites. The leak direction is safe (stale extra references block optimizations, never corrupt output), but it cost real output size on `void ident`-heavy inputs — kitchen-sink.tsx minified 22% larger than main:

1. **`Normalize` raw drops** (`void x` → `void 0`, `drop_console`) never reached `PassDirty`. `Normalize` now records drops through the same typed helpers, and the driver flushes once before the fixed-point loop so pass 1 already observes pruned reference counts (no extra peephole pass).
2. **`remove_unused_template_literal`** silently drained elements that `remove_unused_expression` collapsed to nothing, without a drop walk.
3. **Dropped variable declarators** walked only `init`; references in the binding's TS type annotation (e.g. computed keys in a type literal) leaked. New `drop_variable_declarator` helper walks the whole declarator.

Each class has a leak-regression test; kitchen-sink.tsx output is verified **byte-identical to main**.

Two simplification passes followed: flush hoisted into the `Compressor` driver (no cross-traversal coupling), `DropDiff` reduced to a thin `Visit` over `&mut PassDirty` (no scoping lookup on the mark hot path; marks for unresolved refs are inert), `BitSet::clear()` reuse instead of a per-pass arena realloc, a shared `as_direct_eval_call` predicate for producer and consumer, plain `clone_in` (id-less) at the one site that cloned semantic ids, and removal of dead `drop_console` repair code.

## Verification

- `cargo test -p oxc_minifier -p oxc_mangler -p oxc_semantic -p oxc_allocator` — all pass, incl. the new leak-regression tests
- `just minsize` — **bit-identical output**; kitchen-sink.tsx (not in minsize) separately verified byte-identical to main
- `cargo coverage -- minifier` — test262 44825/44829, babel 1785/1785, snapshots unchanged
- clippy (all features/targets), fmt clean

## Test alignment in oxc_transformer_plugins

The `replace_global_defines` integration-test helper previously reused RGD's returned scoping directly for DCE — a path production does not take: `crates/oxc/src/compiler.rs` marks scoping dirty whenever RGD changed the AST (`scoping_dirty |= ret.changed`) and rebuilds before compress/DCE, and rolldown minifies freshly-parsed chunks. With the full-walk `LiveUsageCollector` gone, that test-only path surfaced stale ambient-`declare` references. The helper now mirrors the production pipeline (rebuild when `ret.changed`, reuse otherwise). No `ReplaceGlobalDefines` source changes. Two `declare`-define contract tests added.

## Known trade-off: defines-heavy DCE pays O(dropped) drop-walks

Profiling `pipeline[react.development.js]` (with `process.env.NODE_ENV` defines) shows the worst case of walk-what-you-drop: DCE drops one `if` subtree covering ~90% of the program's nodes, so the drop-walk approaches a full-program traversal where the old per-pass live walk only covered the small remainder (−3% CodSpeed / +7% local wall on that bench; attribution: ~72% drop-walks, 0% double-marking, ~0% bitset cost). A bounded-walk + live-walk-fallback mitigation was prototyped and validated (recovers to +1.4%, all other wins preserved), but we decided the added mechanism wasn't worth the maintenance cost for a one-bench regression — the same file's plain minification *improves* 12%, and kitchen-sink-class inputs (the load-bearing case) improve ~2×. Revisit if real-world defines-heavy workloads surface it.
2026-06-16 01:58:51 +00:00
oxc-guard[bot] 827248a7fb release(crates): oxc v0.136.0 (#23443)
### 💥 BREAKING CHANGES

- 7a24911 codegen: [**BREAKING**] Borrow sourcemaps from codegen
(#23422) (Boshen)
- bb0ed44 transformer: [**BREAKING**] Disable styled-components
transpileTemplateLiterals by default (#23171) (Boshen)

### 🚀 Features

- 1490a0a linter/react: Implement react-compiler rule (#23202) (Boshen)
- 6c0bdf0 transformer/react-refresh: Support `module.property.useHook()`
(#23190) (Dunqing)
- 47991bd semantic: Report TS1228 for invalid type predicates (#23174)
(camc314)
- 1d3af58 parser: Add TS2398 parameter property diagnostic (#23216)
(camc314)
- 44313da semantic: Add `scope_is_descendant_of` api (#22313) (camc314)
- e5050c0 parser: Improve diagnostic for rest initializer (#23205)
(camc314)
- ec266bb transformer: Run React Compiler as a feature-gated transform
pass (#23201) (Boshen)
- e7374fe parser: Report error for `const` modifier on interface type
parameter (#23173) (camc314)
- a7c1c9b parser: Report ambient definite variable assertions (#23165)
(camc314)
- d169fcd parser: Report invalid class definite assertions (#23164)
(camc314)
- 00244d8 parser: Report definite property initializer errors (#23160)
(camc314)

### 🐛 Bug Fixes

- 52d0c31 transformer: Replace ambient dot defines (#23231) (camc314)
- 2c28748 transformer/class: Parent generated constructors to class
scope (#23222) (camc314)
- 8edd234 parser: Report accessor definite assertion on token (#23203)
(camc314)
- de38a3f react_compiler: Keep imports referenced only by a local
re-export (#23176) (Boshen)
- f5721c2 codegen: Preserve parentheses around `intrinsic` type
reference (#23156) (Boshen)
- e89f81d parser: Don't emit TS1477 for parenthesized instantiation
expression (#23147) (Boshen)
- 8a04149 parser: Reject module-referencing imports/exports in a
namespace body (#22829) (Boshen)

### ⚡ Performance

- 2783295 parser: Table-driven operator precedence lookup (#23346)
(Boshen)
- 231d5de parser: Single-match member expression dispatch (#23347)
(Boshen)
- e89729b codegen: Accept one-shot wrap closures (#23265) (camc314)
- a6c11fa parser: Force-inline read_non_decimal to fold per-digit number
dispatch (#23157) (Boshen)
- d74964c parser: Store class definite assertion offset (#23170)
(camc314)
- f0fda4d parser: Shrink-wrap cold diagnostic tails out of hot frames
(#23159) (Boshen)
- a082180 parser: Store definite assertion offset (#23167) (camc314)
- 534f9c6 oxc: Conditionally rebuild semantic in compiler pipeline
(#23153) (Boshen)
- b435c6a parser: Skip checkpoint for `infer T extends U` constraint in
disallow context (#23128) (Boshen)
- 7464dce parser: Peek instead of checkpoint/rewind for `export default`
modifier (#23124) (Boshen)
- 80a9a32 parser: Fast-path single-keyword TS declarations (#23083)
(Boshen)
- da1a6c6 diagnostics: Migrate to allocation-optimized oxc-miette
(#23094) (Boshen)
- b7b08ce parser: Peek once for the static modifier disambiguation
(#23079) (Boshen)
- e7e07a3 parser: Fold unary dispatch into a single match (#23076)
(Boshen)

### 📚 Documentation

- d241add semantic: Add `AGENTS.md` test guidance for agents (#23441)
(camc314)
- 026f1ae parser: Add `AGENTS.md` test guidance for agents (#23440)
(camc314)
- 09755ac transformer: Add `AGENTS.md` test guidance for agents (#23439)
(camc314)
- e6bdfd4 lexer: Correct reference link for `byte_handlers!` (#23313)
(Dunqing)
- 65b6d7a allocator: Fix memory leaks in `Arena` examples (#23257)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-06-15 20:05:34 +08:00