Commit Graph

378 Commits

Author SHA1 Message Date
Kotaro Chikuba f6000ac141 fix(ecmascript): fold ** with Number::exponentiate, not IEEE pow (#25644)
`f64::powf` implements IEEE 754 `pow`, which returns `1` for a base of
`1` whatever the exponent.
[`Number::exponentiate`](https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate)
instead returns `NaN` when the exponent is `NaN` (step 1), and when the
base has magnitude `1` and the exponent is infinite (steps 9-10).

Constant folding called `powf` directly, so:

```js
1 ** Infinity      // spec: NaN    oxc folded to: 1
(-1) ** Infinity   // spec: NaN    oxc folded to: 1
1 ** -Infinity     // spec: NaN    oxc folded to: 1
1 ** NaN           // spec: NaN    oxc folded to: 1
```

`1` is truthy and `NaN` is not, so an `if` guarded by such an expression
takes the wrong branch.

The one case the two agree on — an exponent of `0` returning `1`
whatever the base, including `NaN ** 0` and `Infinity ** 0` — is
unchanged, since `powf` already gets it right and the spec checks the
exponent before the base.

`cargo minsize` shows no snapshot change.

Found by the minifier fuzzer (#25594).

---

Written with AI assistance (Claude Code). Reviewed by me, and every
expected value in the added test was checked against Node.js.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 12:21:54 +08:00
Kotaro Chikuba e82495ba54 fix(ecmascript): derive ToNumber of !x from ToBoolean (#25595)
Found by the semantic fuzzer added in #25594 (seed `15979`).

## What

`ToNumber(!x)` was computed from `ToNumber(x)` instead of
`ToBoolean(x)`:

```rust
Expression::UnaryExpression(unary) if unary.operator.is_not() => {
    let number = unary.argument.to_number(ctx)?;
    Some(if number == 0.0 { 1.0 } else { 0.0 })
}
```

`!x` is `ToBoolean(x)` negated, so the two predicates must agree — and
they don't. `NaN` is falsy but does not number to `0`, while `"0"`, `"
"`, `"0.0"` and `[]` all number to `0` yet are truthy.

## Why it was not caught

`minimize_conditional_expression` rewrites `a ? 1 : 0` into `+!!a`. That
rewrite is correct, but when a parent expression evaluated the freshly
built `+!!a` in the same pass, the broken branch flipped the result:

```js
(NaN ? 1 : 0) || 2   // 2, minified to 0
('0' ? 1 : 0) || 2   // 1, minified to 2
([]  ? 1 : 0) || 2   // 1, minified to 2
```

A `!!x` written in the source is folded correctly by `fold_unary_expr`
before any parent observes it, so only pass-generated nodes were
affected. That is why existing tests and the conformance suites pass.

refs https://github.com/oxc-project/oxc/pull/8267

## How

Derive the value from `to_boolean`. This is strictly more conservative:
when `to_boolean` returns `None` the fold is skipped rather than
answered wrongly.

## Testing

- New regression test `test_conditional_to_unary_plus_uses_to_boolean`
covering `NaN`, `"0"`, `" "`, `"0.0"` and `[]`.
- `cargo test -p oxc_minifier -p oxc_ecmascript`, `cargo clippy
--all-targets`, `cargo fmt --check` all pass.
- `just minsize`: no snapshot change, so no size regression.

## AI disclosure

This change was produced with AI assistance (Claude Code). I have
reviewed and understand every line; the root cause and each minimal
reproduction were verified by hand against Node.js.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 00:35:39 +00:00
camc314 88b34f2db2 fix(clippy): remove unneeded unsafe (#25551)
ah lint only runs on linux so this didn't fail previously in CI
2026-08-12 15:11:55 +00:00
CrazyboyQCD 48d5e9e47a refactor(ecmascript): use __jcvt() intrinsic on ARM64 for f64→i32 conversion (#24373)
- Simplify `cfg` by using `cfg_select` macro which is stabilized in 1.95
- Use `__jcvt()` intrinsic which is stabilized in 1.95
- Remove redundant `NAN` check since V8 has a set of test for it, which
claims that it works properly for `NAN` and `Infinity`:
https://github.com/v8/v8/blob/3b58a432df85d51bf1c439c30efcf0752de1076f/test/cctest/test-assembler-arm64.cc#L11416C1-L11468C48

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-12 09:15:55 +00: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
camc314 a33788e13e feat(ast)!: group class heritage into ClassHeritage (#25360)
## Summary

- Groups `Class::super_class` and `Class::super_type_arguments` into `Class::heritage`.
- Introduces `ClassHeritage` containing the superclass expression and its optional TypeScript type arguments.
- Makes superclass type arguments without a superclass expression unrepresentable.

## Breaking Change

The Rust AST changes from:

```rust
pub struct Class<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub r#type: ClassType,
    pub decorators: Vec<'a, Decorator<'a>>,
    pub id: Option<BindingIdentifier<'a>>,
    #[ts]
    pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
    pub super_class: Option<Expression<'a>>,
    #[ts]
    pub super_type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
    #[ts]
    pub implements: Vec<'a, TSClassImplements<'a>>,
    pub body: Box<'a, ClassBody<'a>>,
    #[ts]
    pub r#abstract: bool,
    #[ts]
    pub declare: bool,
    pub scope_id: Cell<Option<ScopeId>>,
}
```

to:

```rust
pub struct Class<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub r#type: ClassType,
    pub decorators: Vec<'a, Decorator<'a>>,
    pub id: Option<BindingIdentifier<'a>>,
    #[ts]
    pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
    pub heritage: Option<ClassHeritage<'a>>,
    #[ts]
    pub implements: Vec<'a, TSClassImplements<'a>>,
    pub body: Box<'a, ClassBody<'a>>,
    #[ts]
    pub r#abstract: bool,
    #[ts]
    pub declare: bool,
    pub scope_id: Cell<Option<ScopeId>>,
}

pub struct ClassHeritage<'a> {
    pub expression: Expression<'a>,
    #[ts]
    pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```

## Why?

ECMAScript defines the complete `extends` clause as `ClassHeritage`:

```text
ClassHeritage : extends LeftHandSideExpression
```

TypeScript additionally permits type arguments on the heritage expression:

```ts
class Derived extends Base<T> {}
```

Previously, the AST stored the expression and type arguments independently:

```text
Class {
    super_class: Some(Base),
    super_type_arguments: Some(<T>),
    ...
}
```

This allowed an invalid state to be constructed:

```text
Class {
    super_class: None,
    super_type_arguments: Some(<T>),
    ...
}
```

Superclass type arguments cannot exist without an `extends` expression.

The parser already treated these fields as a pair. It parsed a class heritage entry and then split its expression and type arguments into two independent fields. Builders and transforms therefore had to preserve their relationship manually.

Grouping them into `ClassHeritage` makes the syntactic dependency explicit and prevents consumers from observing or constructing contradictory class heritage.

## Class heritage forms

A class without an `extends` clause has no heritage:

```js
class Base {}
```

```text
Class {
    heritage: None,
    ...
}
```

A class extending an expression has heritage without type arguments:

```js
class Derived extends Base {}
```

```text
Class {
    heritage: Some(ClassHeritage {
        expression: Base,
        type_arguments: None,
    }),
    ...
}
```

TypeScript type arguments belong to the same heritage structure:

```ts
class Derived extends Base<T> {}
```

```text
Class {
    heritage: Some(ClassHeritage {
        expression: Base,
        type_arguments: Some(<T>),
    }),
    ...
}
```

The heritage expression may be any valid class heritage expression:

```js
class Derived extends mixin(Base) {}
class NullDerived extends null {}
```

## How to migrate

Code that previously accessed the superclass expression directly:

```rust
if let Some(super_class) = &class.super_class {
    // ...
}
```

can use the compatibility accessor:

```rust
if let Some(super_class) = class.super_class() {
    // ...
}
```

Code that needs both parts should access the grouped heritage:

```rust
if let Some(heritage) = &class.heritage {
    let expression = &heritage.expression;
    let type_arguments = heritage.type_arguments.as_deref();
}
```

Mutating consumers should update the nested fields:

```rust
if let Some(heritage) = &mut class.heritage {
    heritage.type_arguments = None;
}
```

Common migrations:

| Previous representation | New representation |
| --- | --- |
| `class.super_class` | `class.heritage.as_ref().map(\|h\| &h.expression)` or `class.super_class()` |
| `class.super_type_arguments` | `class.heritage.as_ref()?.type_arguments` or `class.super_type_arguments()` |
| `class.super_class.is_some()` | `class.heritage.is_some()` |
| Mutate `class.super_class` | Mutate `class.heritage.expression` |
| Clear `class.super_type_arguments` | Clear `class.heritage.type_arguments` |
| Pass superclass and type arguments separately to a `Class` builder | Construct and pass `ClassHeritage` |
| Clone both fields independently | Clone `class.heritage` |

```ts
superClass: Expression | null;
superTypeArguments?: TSTypeParameterInstantiation | null;
```

No `heritage` property or `ClassHeritage` node is added to the ESTree output.

fixes https://github.com/oxc-project/backlog/issues/216
2026-08-07 14:18:59 +00:00
camc314 44fd320324 feat(ast)!: split TS external modules & Namespace Declarations (#25284)
## Summary

- Splits `TSModuleDeclaration` into two dedicated declaration nodes:
  - `TSExternalModuleDeclaration` for string-literal modules and module augmentations.
  - `TSNamespaceDeclaration` for identifier-based `module` and `namespace` declarations.
- Removes `TSModuleDeclaration`, `TSModuleDeclarationName`, `TSModuleDeclarationBody`, and `TSModuleDeclarationKind`.
- Makes invalid combinations of names, bodies, and declaration kinds unrepresentable.
- Ensures only namespace declarations introduce a normal binding.
- Updates all AST consumers, generators, traversal, formatting, code generation, semantic analysis, lint rules, transforms, and serialization.
- Keeps normal and eager-raw ESTree output compatible: both declarations remain `TSModuleDeclaration` nodes.
- Allows experimental lazy raw deserialization to expose the two new node types directly.

## Breaking Change

The Rust AST changes from:

```rust
pub enum Declaration<'a> {
    VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
    FunctionDeclaration(Box<'a, Function<'a>>) = 33,
    ClassDeclaration(Box<'a, Class<'a>>) = 34,

    TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
    TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
    TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
    TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 38,
    TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 39,
    TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
}

pub struct TSModuleDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub id: TSModuleDeclarationName<'a>,
    pub body: Option<TSModuleDeclarationBody<'a>>,
    pub kind: TSModuleDeclarationKind,
    pub declare: bool,
    pub scope_id: Cell<Option<ScopeId>>,
}
```

to:

```rust
pub enum Declaration<'a> {
    VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
    FunctionDeclaration(Box<'a, Function<'a>>) = 33,
    ClassDeclaration(Box<'a, Class<'a>>) = 34,

    TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
    TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
    TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
    TSExternalModuleDeclaration(Box<'a, TSExternalModuleDeclaration<'a>>) = 38,
    TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 39,
    TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 40,
    TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 41,
}

pub struct TSExternalModuleDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub id: StringLiteral<'a>,
    pub body: Option<Box<'a, TSModuleBlock<'a>>>,
    pub declare: bool,
    pub scope_id: Cell<Option<ScopeId>>,
}

pub struct TSNamespaceDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub id: BindingIdentifier<'a>,
    pub body: TSNamespaceDeclarationBody<'a>,
    pub kind: TSNamespaceDeclarationKind,
    pub declare: bool,
    pub scope_id: Cell<Option<ScopeId>>,
}

pub enum TSNamespaceDeclarationBody<'a> {
    TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 0,
    TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1,
}

pub enum TSNamespaceDeclarationKind {
    Module = 0,
    Namespace = 1,
}
```

The corresponding inherited `Statement` variants, `AstKind` variants, builders, and visitor methods have also changed.

## Why?

`TSModuleDeclaration` previously represented two declaration families with different syntax and semantics:

```ts
declare module "foo" {}
declare module "*.css";

module Foo {}
namespace Foo {}
namespace Foo.Bar {}
```

The shared representation allowed invalid states such as:

- A namespace with a string-literal name.
- An external module with an identifier name.
- An identifier namespace without a body.
- A dotted external module.
- An external module containing another module declaration as its body.
- A declaration whose `kind` disagreed with its name.

Consumers also had to inspect `id` before determining whether the node introduced a binding or represented an external module.

The two declaration families have stronger independent invariants:

### External modules

- Always have a string-literal name.
- Never introduce a normal binding identifier.
- May omit their body.
- Cannot use dotted names.
- Use module-body import and export rules.

### Namespace declarations

- Always have a binding identifier.
- Always have a body.
- May contain nested namespace declarations for dotted names.
- Explicitly preserve the `module` or `namespace` spelling.
- Introduce a namespace/module symbol.
- Use namespace-body restrictions.

Dedicated nodes encode these differences directly.

## Declaration forms

External modules use `TSExternalModuleDeclaration`:

```ts
declare module "foo" {}
declare module "*.css";
module "foo" {}
```

Identifier-based declarations use `TSNamespaceDeclaration`:

```ts
module Foo {}
namespace Foo {}
```

Dotted namespaces are represented as nested namespace declarations:

```ts
namespace Foo.Bar {}
```

```rust
TSNamespaceDeclaration {
    id: Foo,
    body: TSNamespaceDeclarationBody::TSNamespaceDeclaration(
        TSNamespaceDeclaration {
            id: Bar,
            body: TSNamespaceDeclarationBody::TSModuleBlock(...),
            // ...
        }
    ),
    // ...
}
```

Only `TSExternalModuleDeclaration::body` is optional.

## ESTree compatibility

Normal serialization and eager raw deserialization continue to emit the existing ESTree shape:

```ts
interface TSModuleDeclaration {
  type: "TSModuleDeclaration";
  id: BindingIdentifier | StringLiteral | TSQualifiedName;
  body: TSModuleBlock | null;
  kind: "module" | "namespace";
  declare: boolean;
  global: false;
}
```

Dotted namespaces continue to serialize as a single `TSModuleDeclaration` with a `TSQualifiedName`.

Experimental lazy raw deserialization is not compatibility-preserving. It now exposes `TSExternalModuleDeclaration` and `TSNamespaceDeclaration` directly, with separate constructors, layouts, and visitor type IDs.

## How to migrate

Code that previously matched all forms through one variant:

```rust
if let Declaration::TSModuleDeclaration(declaration) = declaration {
    // ...
}
```

should now match the required declaration family:

```rust
match declaration {
    Declaration::TSExternalModuleDeclaration(declaration) => {
        // String-literal external module
    }
    Declaration::TSNamespaceDeclaration(declaration) => {
        // Identifier-based module or namespace
    }
    _ => {}
}
```

The same applies to `Statement` and `AstKind`:

```rust
Statement::TSExternalModuleDeclaration(declaration)
Statement::TSNamespaceDeclaration(declaration)

AstKind::TSExternalModuleDeclaration(declaration)
AstKind::TSNamespaceDeclaration(declaration)
```

Common field migrations:

| Previous representation | New representation |
| --- | --- |
| `TSModuleDeclarationName::StringLiteral(id)` | `TSExternalModuleDeclaration::id` |
| `TSModuleDeclarationName::Identifier(id)` | `TSNamespaceDeclaration::id` |
| `body: None` | Only valid on `TSExternalModuleDeclaration` |
| `TSModuleDeclarationBody::TSModuleBlock` | External `body` or `TSNamespaceDeclarationBody::TSModuleBlock` |
| `TSModuleDeclarationBody::TSModuleDeclaration` | `TSNamespaceDeclarationBody::TSNamespaceDeclaration` |
| `TSModuleDeclarationKind::Module` | External module, or `TSNamespaceDeclarationKind::Module` |
| `TSModuleDeclarationKind::Namespace` | `TSNamespaceDeclarationKind::Namespace` |
| Inspect `id` to determine binding behavior | Match `TSNamespaceDeclaration` |
| `declaration.id()` for string modules | Now returns `None` |
| `visit_ts_module_declaration` | `visit_ts_external_module_declaration` or `visit_ts_namespace_declaration` |

When constructing nodes, use the generated `TSExternalModuleDeclaration` or `TSNamespaceDeclaration` builders rather than recreating the previous shared representation.

fixes https://github.com/oxc-project/backlog/issues/218
2026-08-05 14:37:35 +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
camc314 1bdedd11ca feat(ast)!: introduce ExportDeclaration, ExportFromDeclaration (#25095)
## Summary

Split the overloaded Rust `ExportNamedDeclaration` into syntax-specific AST nodes:

- `ExportDeclaration` for exported declarations
- `ExportNamedDeclaration` for local exports
- `ExportFromDeclaration` for re-exports

## Breaking Change

The Rust AST changes from:

```rust
pub struct ExportNamedDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub declaration: Option<Declaration<'a>>,
    pub specifiers: Vec<'a, ExportSpecifier<'a>>,
    pub source: Option<StringLiteral<'a>>,
    pub export_kind: ImportOrExportKind,
    pub with_clause: Option<Box<'a, WithClause<'a>>>,
}

pub enum ModuleDeclaration<'a> {
    // ...
    ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
    // ...
}
```

to:

```rust
pub struct ExportDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub declaration: Declaration<'a>,
}

pub struct ExportNamedDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub specifiers: Vec<'a, ExportSpecifier<'a>>,
    pub export_kind: ImportOrExportKind,
}

pub struct ExportFromDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub specifiers: Vec<'a, ExportSpecifier<'a>>,
    pub source: StringLiteral<'a>,
    pub export_kind: ImportOrExportKind,
    pub with_clause: Option<Box<'a, WithClause<'a>>>,
}

pub enum ModuleDeclaration<'a> {
    // ...
    ExportDeclaration(Box<'a, ExportDeclaration<'a>>),
    ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
    ExportFromDeclaration(Box<'a, ExportFromDeclaration<'a>>),
    // ...
}
```

## Why?

The previous node represented three distinct syntax forms through optional and mutually exclusive fields:

```text
export const value = 1;       -> ExportDeclaration
export { value };             -> ExportNamedDeclaration
export { value } from "mod";  -> ExportFromDeclaration
```

This allowed invalid combinations such as declarations with specifiers or sources, and import attributes without a source. The new nodes make these states unrepresentable and remove repeated `declaration`/`source` branching from consumers.

## How to migrate

Match the syntax-specific `ModuleDeclaration`, `Statement`, or `AstKind` variant instead of inspecting optional fields. Generated builders, visitors, traversal ancestors, and formatter wrappers now expose corresponding APIs for all three nodes.

Common field migrations:

| Previous representation | New representation |
| --- | --- |
| `declaration: Some(declaration)` | `ExportDeclaration { declaration }` |
| `declaration.as_ref()` | `&export.declaration` |
| `source.is_none()` for local exports | Match `ExportNamedDeclaration` |
| `source: Some(source)` | `ExportFromDeclaration { source }` |
| `source.as_ref().unwrap()` | `&export.source` |
| `with_clause` | Only available on `ExportFromDeclaration` |
| Stored `export_kind` on exported declarations | `ExportDeclaration::export_kind()` |
| `ExportNamedDeclaration::boxed_plain_declaration(...)` | `ExportDeclaration::boxed(...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, None, ...)` | `ExportNamedDeclaration::boxed(..., specifiers, ...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, Some(source), ...)` | `ExportFromDeclaration::boxed(..., specifiers, source, ...)` |

closes https://github.com/oxc-project/backlog/issues/219
2026-07-31 10:25:47 +00:00
Dunqing 8a47ff3743 fix(ecmascript): do not treat lone-surrogate strings as constants (#25084)
The value of a StringLiteral and the cooked value of a template quasi encode lone surrogates with `\u{FFFD}` escapes and mark the node with a `lone_surrogates` flag. ToJsString and constant evaluation returned the encoded text, and consumers materialized it into new string literals without the flag, printing the escape encoding as literal text: `"\ud800" + "y"` folded to a string containing a literal `d800`.

Return `None` for such strings and template quasis so folds keep the original nodes.

```js
// source
console.log("\ud800" + "y");  // node prints: �y

// before (miscompiled)
console.log(`�d800y`);         // node prints: �d800y

// after
console.log(`\ud800`+`y`);     // node prints: �y
```
2026-07-30 10:52:55 +00:00
camc314 c917f204a7 feat(ast)!: introduce ArrowFunctionBody enum (#24987)
## Summary

- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.

## Breaking Change

The Rust AST changes from:

```rust
pub struct ArrowFunctionExpression<'a> {
    pub expression: bool,
    pub body: Box<'a, FunctionBody<'a>>,
}
```

to:

```rust
pub enum ArrowFunctionBody<'a> {
    FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
    INHERIT(Expression<'a>),
}

pub struct ArrowFunctionExpression<'a> {
    pub body: ArrowFunctionBody<'a>,
}
```

Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.

## Why?

The previous representation required consumers to maintain this implicit invariant:

```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```

Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.

Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.

## How to migrate

Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.

Common field migrations:

| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |

closes https://github.com/oxc-project/backlog/issues/211
2026-07-29 13:32:32 +00:00
camc314 3f36b16556 refactor(ecmascript): impl MayHaveSideEffects for SequenceExpression (#25074)
simple refactor to extract out `SequenceExpression` side effects such that it now implements `MayHaveSideEffects` to allow us to call `SequenceExpression::may_have_side_effects`
2026-07-29 10:30:44 +00:00
camc314 5acdae22cb feat(ecmascript): skip transparent TS expressions (#25072)
This will be used by the linter.
2026-07-29 10:06:13 +00:00
Dunqing fbe8d318ff fix(minifier): respect targets when folding RegExp source (#24790)
RegExp literal `.source` folding used a hard-coded approximation of ES2015 support. It rejected syntax that configured targets support, such as the `u` flag for ES2015, while allowing newer syntax without checking the configured engines.

The same target compatibility mapping already exists in `oxc_ecmascript` for RegExp constructor analysis. Expose one parsed-syntax predicate from that layer and reuse it for `.source` folding. This removes the duplicate policy and the minifier's direct `oxc_regular_expression` dependency. An unspecified target remains conservative, while an explicit supporting target unlocks the fold.

Covered by `test_fold_regex_source` with adjacent unsupported and supported targets for `y`, `u`, `s`, `d`, `v`, named groups, Unicode property escapes, lookbehind, duplicate named groups, and pattern modifiers.

Verified with `cargo test -p oxc_ecmascript --features side_effects`, `cargo test -p oxc_minifier` (631 passed, 42 ignored), `just minsize` (no minsize or allocation snapshot changes), affected-crate clippy with warnings denied, and `just fmt`.

Follow-up to #24712 and [its non-blocking review](https://github.com/oxc-project/oxc/pull/24712#discussion_r3619967432).

Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and submitting these changes under the repository AI usage policy.
2026-07-28 03:37:44 +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
Dunqing a086d91c83 perf(ecmascript): avoid number literal allocations (#24889)
The shared number-literal serializer can be called repeatedly by consumers that only need the emitted length. Its existing `Cow<str>` implementation allocates candidate strings for some hexadecimal and exponent forms, which would add avoidable allocation traffic to those callers.

Build candidates in an inline stack string while preserving the callback API and generated output. In allocation tracking, the unoptimized consumer added 56 system allocations for `antd.js` and 129 for `kitchen-sink.tsx`; this version removes those increases.

This PR depends on #24876. #24875 adds the minifier consumer.

Verified with `cargo test -p oxc_ecmascript -p oxc_codegen`, Clippy, formatting, and `just allocs` with no standalone snapshot changes.

AI assistance: Codex was used to implement and verify this change.
2026-07-26 12:11:48 +00:00
Dunqing dba374912a refactor(ecmascript): share number literal serialization (#24876)
Codegen is not the only consumer that needs the exact emitted form of a number, but the serializer currently lives in the codegen crate. Other consumers would otherwise need to duplicate its formatting policy or depend on codegen.

Move the callback helper extracted in #24888 from `oxc_codegen` to `oxc_ecmascript`, then reuse it from codegen. The serialization algorithm is unchanged; this PR only makes the helper public and relocates its dependencies.

This PR depends on #24888. #24889 handles allocation-free candidate construction, and #24875 adds the minifier consumer.

Verified independently with `cargo test -p oxc_ecmascript -p oxc_codegen`, Clippy, and formatting.

AI assistance: Codex was used to implement and verify this change.
2026-07-26 12:11:47 +00:00
sapphi-red 07e9acca08 fix(ecmascript): keep side effects in parent of toString calls (#24846)
Detect the side effect of`f()` in `(f(), 5).toString()`.
2026-07-24 01:16:58 +00:00
sapphi-red 16a40ad48c fix(ecmascript): detect side effects in unused arguments to charCodeAt (#24845)
Detect the side effect of`f()` in `'abcde'.charCodeAt(0, f())`.
2026-07-24 01:16:57 +00:00
sapphi-red 133bb35dee fix(ecmascript): detect side effects of foo().#bar correctly when PropertyReadSideEffects::None (#24769)
Side effects of`foo().#bar` was not detected correctly when PropertyReadSideEffects::None is used.

Repro: https://stackblitz.com/edit/oxc-project-website-l6tidw4j?file=src%2Fmain.js

refs https://github.com/oxc-project/oxc/pull/24707#discussion_r3614105531
2026-07-21 23:33:45 +00:00
Boshen baf022d74d refactor(ast)!: remove old AstBuilder methods (#24563)
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.
2026-07-21 15:22:46 +00: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
Armano 9a59e1961d refactor(minifier): reuse is_typed_array_constructor from ecma (#24765)
refactor/reuse of `is_typed_array_constructor` from `oxc_ecmascript`
instead of redefining it it `oxc_minifier` as `is_typed_array_name`
2026-07-21 22:41:28 +08:00
Dunqing 79e6d775fb fix(minifier): preserve RegExp feature detection (#24712)
> **PR stack (3 of 3)**
> 1. #24739
> 2. #24742
> 3. **#24712 (this PR)**
>
> Review #24739 and #24742 first. This PR is based on #24742, so GitHub shows only the RegExp behavior change.

Stacked on #24742, which exposes strict engine-target capabilities to side-effect analysis.

RegExp constructor calls used for engine feature detection were removed by DCE when their pattern was valid according to the current Oxc RegExp parser. A pattern can be valid at build time but still throw a `SyntaxError` on the configured runtime, so removing the call changes the result of surrounding `try`/`catch` checks.

RegExp purity now requires both syntactic validity and support from every configured target. Consumers without concrete targets remain conservative, while an explicit target allows the minifier to remove constructors known to be safe for those engines.

Target-independent flag validation and pattern compatibility checks now live in `oxc_regular_expression`; side-effect analysis only maps target support to unsupported flags and patterns. String patterns, modern flags, named groups, property escapes, lookbehind, modifiers, invalid flags, and lone-surrogate strings are covered. A RegExp literal with replacement flags is removable only when the target supports both those flags and the dedicated `ES2015RegExpConstructorCanAlterFlags` compatibility feature. Explicitly targeting `esnext` opts into current features.

## Example

Input:

```js
export function supportsUnicodeRegExp() {
  try {
    new RegExp("\\p{Ll}", "u");
    return true;
  } catch {
    return false;
  }
}
```

Before:

```js
export function supportsUnicodeRegExp() {
  try {
    return true;
  } catch {
    return false;
  }
}
```

After:

```js
export function supportsUnicodeRegExp() {
  try {
    new RegExp("\\p{Ll}", "u");
    return true;
  } catch {
    return false;
  }
}
```

Verified with all `oxc_regular_expression`, `oxc_compat`, `oxc_ecmascript`, and `oxc_minifier` tests; the no-feature/`side_effects`/`constant_evaluation` `oxc_ecmascript` matrix; affected-crate clippy; formatting; and diff checks.

Fixes rolldown/rolldown#10279.

Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and submitting these changes under the repository AI usage policy.
2026-07-21 14:15:24 +00:00
Dunqing 01c3611d9b refactor(ecmascript): expose engine targets to side effects (#24742)
> **PR stack (2 of 3)**
> 1. #24739
> 2. **#24742 (this PR)**
> 3. #24712
>
> Review #24739 first. This PR is based on its branch, so GitHub shows only the engine-target plumbing.

Stacked on #24739, which introduces the `side_effects` and `constant_evaluation` feature boundary for `oxc_ecmascript`.

Side-effect analysis sometimes needs to know whether runtime syntax is supported by every configured engine. `MayHaveSideEffectsContext` currently has no access to target information, while `EngineTargets::has_feature` is intentionally permissive about missing compatibility data because it answers whether a transformation is needed.

`MayHaveSideEffectsContext::engine_targets()` exposes the configured targets through a general-purpose accessor. Its default is `None`, so consumers without target information remain conservative, while the minifier supplies its existing `CompressOptions::target`.

`EngineTargets::supports_es_feature` provides the corresponding strict capability query. Every configured engine must have compatibility data and meet the required version; empty targets and missing rows are not treated as proof of support. The existing transformation-oriented `has_feature` behavior remains unchanged.

Because `oxc_linter` enables `side_effects`, this also adds `oxc_compat` to the oxlint and language-server build graphs. It does not change side-effect decisions by itself; #24712 consumes the new capability for target-aware RegExp validation.

Verified with all `oxc_compat` tests, the no-feature/`side_effects`/`constant_evaluation` `oxc_ecmascript` matrix, a minifier check, formatting, and Clippy.

Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and maintaining these changes.
2026-07-21 14:15:23 +00:00
Dunqing 9a48d2308f refactor(ecmascript)!: gate analysis APIs (#24739)
> **PR stack (1 of 3)**
> 1. **#24739 (this PR)**
> 2. #24742
> 3. #24712
>
> Review and merge in this order.

The `oxc_ecmascript` crate currently compiles constant evaluation and side-effect analysis for every consumer, even when a consumer only needs the small shared ECMAScript value primitives. This makes optional analysis code and its dependencies part of unrelated builds.

This introduces two explicit features:

- `side_effects` enables side-effect analysis and its RegExp support.
- `constant_evaluation` enables constant evaluation and implies `side_effects`, because side-effect analysis uses constant folding for several purity decisions.

`ConstantValue`, `ValueType`, and `DetermineValueType` move to feature-independent root modules so `GlobalContext` remains available without either analysis feature. Existing paths through `constant_evaluation` remain re-exported when that feature is enabled.

This is a prerequisite for #24742, which exposes engine targets to side-effect analysis without making compatibility data part of every `oxc_ecmascript` consumer.

This is a breaking Cargo feature change for direct consumers of `oxc_ecmascript`: code using side-effect analysis or constant evaluation must enable the corresponding feature.

Verified locally with the no-feature, `side_effects`, and `constant_evaluation` configurations, crate tests, dependent linter and minifier tests, rustdoc with warnings denied, and `just ready`. The release oxlint binary on this commit is 13,350,096 bytes.

Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and maintaining these changes.
2026-07-21 14:15:23 +00:00
sapphi-red e750a8243a fix(ecmascript): fix false negative for may_have_side_effects on dynamic property access (#24709)
``[][`length${foo()}`]`` was treated as side effect free. But `foo()` can have side effects.
2026-07-20 09:13:59 +00:00
camc314 54cc121250 feat(ast)!: split MetaProperty into ImportMeta and NewTarget (#24557)
## Summary
- replace the generic Rust MetaProperty AST node with dedicated ImportMeta and NewTarget nodes
- preserve the ESTree MetaProperty shape and generated public TypeScript types
- update parser, formatter, codegen, lint, transform, minifier, traversal, and React compiler consumers

## Summary

Remove `MetaProperty` AST node, in favour of two separate nodes (`ImportMeta` and `NewTarget`)

## Breaking Change

The Rust AST field changes from:

```rust
pub struct MetaProperty<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub meta: IdentifierName<'a>,
    pub property: IdentifierName<'a>,
}
```

to:

```rust
/// /// `import.meta` in `console.log(import.meta);`.
pub struct ImportMeta {
    pub node_id: Cell<NodeId>,
    pub span: Span,
}

/// `new.target` in `function F() { return new.target; }`.
pub struct NewTarget {
    pub node_id: Cell<NodeId>,
    pub span: Span,
}
```

## Why?

The current AST could represent shapes such as:

```
foo.bar
```

By constructing the node manually. However this doesn't make sense, and is invalid.

## How to migrate?

1. Replace any `Expression::MetaProperty` matches/checks with the dedicated `Expression::ImportMeta` and `Expression::NewTarget` varients
2. Replace any `new_meta_property` construction with the new `new_import_meta` or `new_new_target` APIs.
3. Inspecting the `meta`/`property` identifier names is no longer required.
2026-07-15 14:48:32 +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
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
Lawrence Lin bd49aff795 perf(ecmascript): avoid heap-allocating Math.min/max/imul operands (#23941)
## Summary

`try_fold_math_variadic` constant-folds `Math.min` / `Math.max` /
`Math.imul(...)` by
collecting the operand values into a `Vec<f64>`. That is a **system
allocation on every
foldable call**, on the minifier's constant-folding hot path.

The evaluation context already exposes the arena
(`ConstantEvaluationCtx: GetAllocator`,
and the module already imports `ArenaVec`), so collect into an
`ArenaVec` instead. The
list is transient and only iterated / indexed (`.iter()`, `.first()`,
`.get()`), so the
change is behaviour-identical — the system allocation is simply gone.

## Results — `cargo allocs` (minifier system allocations)

| fixture | before | after |
| --- | ---: | ---: |
| kitchen-sink.tsx | 2576 | **2312** (−264) |
| antd.js | 1040 | **870** (−170) |
| pdf.mjs | 2569 | **2476** (−93) |
| App.tsx | 78 | 72 |

## Tests

- `cargo test -p oxc_ecmascript --lib --tests` — pass
- `cargo test -p oxc_minifier --lib --tests` — 539 pass

## AI disclosure

Prepared with AI assistance (Claude). I located the hotspot with a
per-call-site allocation
profile across real files, confirmed the list is
transient/iterated-only, and verified the
reduction with `cargo allocs` plus the test suites.

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-07-05 14:21:15 +01:00
Dunqing e2744150c1 fix(minifier): don't drop global calls that throw despite pure arguments (#23917)
## What

`is_pure_global_method_call` (and the bare-global path) in `CallExpression::may_have_side_effects` treated a call as side-effect-free whenever its arguments were, ignoring that several of these functions **throw** at runtime. Unused calls that throw were therefore dropped as dead code. terser and esbuild both keep them.

Guard the cases oxc can prove:

| Call | Throws | Before | After |
| --- | --- | --- | --- |
| `String.raw()`, `Symbol.keyFor()`, `Symbol.keyFor(42)` | `TypeError` (missing/invalid required arg) | dropped | kept |
| `URL.canParse()` | `TypeError` (required first arg) | dropped | kept |
| `String.fromCodePoint(-1 / 1.5 / 0x110000)` | `RangeError` (code point outside `[0, 0x10FFFF]`) | dropped | kept |
| `Math.abs(10n)`, `Date.UTC(10n)`, `String.fromCharCode(10n)`, `isNaN(10n)`, … | `TypeError` (`ToNumber(BigInt)`) | dropped | kept |

Safe optimizations are preserved — `URL.canParse("x")`, `String.fromCodePoint(65)`, `String.fromCharCode(65)`, `Math.abs(1)` still drop. `String.raw` / `Symbol.keyFor` are removed from the pure-method list (never provably safe); the rest get argument guards.

### A Symbol argument is left droppable, on purpose

A Symbol argument also throws (`ToString` / `ToNumber` on a Symbol), but oxc has no `ValueType::Symbol` and `to_primitive` never resolves a pure expression to `Symbol`, so a Symbol throw is **never provable**. Guarding it would mean keeping every `Math.abs(x)` / `parseInt(x)` on an undetermined argument — a large minification regression. esbuild makes the same trade-off (it drops `Math.abs(Symbol.iterator)` too), so only the provable cases are guarded.

oxc already guards exactly this for the `Number` / `Symbol` / `BigInt` / `Error` constructors; this brings the method-call path in line. The assumption (including the non-provable-Symbol carve-out) is documented in `crates/oxc_minifier/docs/ASSUMPTIONS.md`.

Covered by `test_throwing_global_calls` in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; cross-checked against terser and esbuild; `minsize` unchanged.

---

Stacked on #23916.
2026-07-02 02:02:14 +00:00
Dunqing 60e716087d feat(minifier): drop side-effect-free IIFEs whose result is unused (#23967)
## Summary

- `CallExpression::may_have_side_effects` now looks through an IIFE — a call whose callee is a function/arrow literal: it is side-effect-free when its arguments, parameters, and every body statement are. Previously every such call was assumed effectful, so the minifier kept dead wrappers like `var unused = (function () { function test() {} return test })()`. Closes #23777.
- This is a plain improvement to side-effect detection in `oxc_ecmascript` — no DCE awareness there. The "preserve IIFE structure in DCE-only mode" policy (match Rollup / esbuild `--tree-shaking=true`, see `preserve_iife_in_dce_mode`) lives in the minifier, in one helper (`has_side_effects_or_preserved_iife`) shared by every unused-removal site that can face an IIFE call, so only full minification actually drops it.
- Conservative — skips `async`/generator callees (different invocation semantics) and any non-identifier parameter (defaults run user code, destructuring reads properties / drives the iterator protocol).
- Covered by `remove_unused_expression.rs::test_remove_side_effect_free_iife` (full-mode positive + negative) and `dead_code_elimination.rs::preserve_iife_in_dce_mode` (DCE-mode preservation: bare, unused-binding, sequence, and `for`-init positions). `minsize` unchanged, `allocs` slightly down.

## Example

```js
// input
var unused = (function () {
  function test() {}
  return test
})()
```

```js
// full minify, before — kept as a bare expression statement
(function () {
  function test() {}
  return test;
})();

// full minify, after
// (empty)
```

In dce-only / tree-shaking mode the IIFE structure is preserved (matches Rollup / esbuild `--tree-shaking=true`).
2026-07-01 03:36:07 +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
Dunqing dcc9a73b12 fix(minifier): treat Object.isExtensible/isFrozen/isSealed as pure (#23916)
## What

`Object.isExtensible`, `Object.isFrozen`, and `Object.isSealed` were grouped with the throwing introspection methods (`Object.keys`, `getOwnPropertyDescriptor`, …) in `CallExpression::may_have_side_effects`, so a call with a missing / `null` / `undefined` argument was conservatively kept.

Unlike those methods, the three `is*` methods never `ToObject` their target — per [spec](https://tc39.es/ecma262/#sec-object.isextensible) a non-object receiver returns a primitive (`false` / `true`) instead of throwing, so the call is side-effect-free and an unused one can be dropped.

```js
// in
Object.isExtensible();
Object.isFrozen(x);
Object.isSealed();
Object.keys();

// out
Object.isFrozen(x), Object.keys();
```

`isExtensible()` / `isSealed()` drop (pure); `isFrozen(x)` stays — an undetermined target could be a `Proxy`, whose `[[IsExtensible]]` / `[[OwnPropertyKeys]]` traps are observable — and `keys()` stays (it throws on the `undefined` receiver).

Covered by new cases in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; `minsize` unchanged.

Closes #23779
2026-06-29 13:51:39 +00:00
overlookmotel 3d61dea7e2 docs(all): correct capitalization in comments (#23887)
Just fix capitalization in comments.
2026-06-28 12:13:18 +00:00
overlookmotel f2091b3846 feat(ast): unify old and new AstBuilders (#23875)
Part of #23043.

Recent PRs have introduced a new `AstBuilder` type, and migrated all Oxc's crates over to it.

To allow downstream consumers to incrementally migrate from old `AstBuilder` to new one, unify the two implementations into a single `AstBuilder` type.

Remove the `builder_new` Cargo feature and instead add a new feature `disable_old_builder`. When enabled, this feature removes all the methods from `AstBuilder`, and makes `AstBuilder` non-`Copy`. Consumers should migrate, and then enable this feature to ensure later changes don't use the methods which are slated for removal.

Enable the `disable_old_builder` feature in all Oxc crates which use `AstBuilder` _in tests_. This ensures we don't go back to using the deprecated methods, while not forcing the feature on consumers (e.g. Rolldown) who use e.g. `oxc_transformer` but also use `AstBuilder` directly.
2026-06-27 20:37:38 +00:00
overlookmotel 8de5122230 feat(ecmascript)!: switch to new AstBuilder (#23834)
Part of #23043. Migrate `oxc_ecmascript` over to new `AstBuilder`.

This is a breaking change for users of `oxc_ecmascript` crate. `ConstantEvaluationCtx` no longer has an `ast` method to get an `AstBuilder`. Instead, any type that implements `ConstantEvaluationCtx` must also implement `oxc_allocator::GetAllocator` and `oxc_ast::builder::GetAstBuilder` - the latter proving a `builder` method which returns a `&AstBuilder`.

I'll aim to fix this to remove the breaking change in a follow-on PR (before next release).
2026-06-26 18:17:30 +00:00
overlookmotel f65d8e5291 refactor(all): shorten redundant std::vec::Vec (#23748)
Pure refactor.

#23748 made all usages of arena-allocated `Vec` be named `ArenaVec`. This makes plain `Vec` unambiguously refer to heap-allocated `Vec` from standard library.

Shorten `std::vec::Vec` to just `Vec` in various places.
2026-06-24 01:21:41 +00:00
overlookmotel d1f18cda21 refactor(all): import arena Box and Vec as ArenaBox / ArenaVec (#23747)
Pure refactor.

Use `ArenaBox` and `ArenaVec` everywhere for the arena-allocated types from `oxc_allocator`, instead of plain `Box` and `Vec`.

This disambiguates when `Vec` is `oxc_allocator::Vec` and when it's `std::vec::Vec`, making code clearer to follow.

Previously it was easy to confuse the two, and such confusion led to UB in `Traverse` (fixed in #23745).

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

The only exceptions are:

1. Inside `oxc_allocator` crate itself.
2. AST type definitions.
3. Generated code (migrated separately in #23749).
2026-06-24 01:21:41 +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
Dunqing 31bfd9bc75 fix(minifier): keep Object introspection calls on a possible Proxy (#23483)
## Summary

Closes #17157.

`Object.keys`, `getOwnPropertyDescriptor(s)`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `getPrototypeOf`, `hasOwn`, `isExtensible`, `isFrozen`, and `isSealed` introspect their target through internal methods a `Proxy` can trap (`[[OwnPropertyKeys]]`, `[[GetOwnProperty]]`, …), so they have observable side effects when the target is a Proxy. They were treated as unconditionally pure and dropped when unused — so `Object.getOwnPropertyDescriptor(proxy, "foo")` / `Object.keys(proxy)` were silently removed, skipping the trap.

They are now treated as side-effect-free only when the target is provably not a Proxy (a determined, non-null value type — i.e. a literal), unless the bundler opts out of property-read side effects. A `null`/`undefined`/missing target is also kept (it throws a `TypeError`).

- `Object.create(proto)` is pure only with an object/`null` prototype and no `properties` argument (read via `[[OwnPropertyKeys]]`/`[[Get]]`).
- `Object.values`/`entries` are no longer treated as pure at all — they invoke `[[Get]]` and run getters even on a plain object.
- `Object.is` stays unconditionally pure.

## Credit

Adapted from #21056 by @gthb. Their `proxy_sensitive_arg_index` approach is preserved; this version adds corrections for the getter-invoking methods (`values`/`entries`, and `create` with properties) and the throw-on-`null`/`undefined` cases that the original treated as droppable.

Verified by `cargo test -p oxc_minifier` / `-p oxc_ecmascript`; `minsize` unchanged.

cloese https://github.com/oxc-project/oxc/pull/21056
2026-06-17 08:07:58 +00:00
Dunqing 53509a85df feat(minifier): treeshake pure typed arrays and Set/Map array literals (#23469)
## Summary

Mark side-effect-free `new` constructions pure so they are dropped when unused (and annotated for downstream tree-shaking):

- **TypedArrays** with a non-negative numeric-literal length (`new Int8Array(8)`, `new Uint8Array(1024)`). The buffer allocation runs no user code; a valid length is pure and a too-large length only throws a maximum-length `RangeError`, which the minifier is allowed to drop (see `docs/ASSUMPTIONS.md`). `new Int8Array(-1)` (negative-length `RangeError`), `new Int8Array(0n)` (BigInt `TypeError`), object, and non-literal arguments are kept.
- **`new Set([...])` / `new Map([[..], ..])`** with array-literal arguments; element side effects are preserved when the construction is dropped. `WeakSet`/`WeakMap` are excluded (their keys must be objects, so `new WeakSet([1])` throws), and `Map` entries must be array literals (`new Map([1])` throws).

This matches esbuild's per-constructor handling — the only other minifier that keeps every throwing form; Rollup, Terser (`unsafe`), and SWC drop some of these incorrectly. Cross-verified against the ES spec, esbuild, Terser, Rollup, and SWC.

Minified-size impact: react.development.js −0.04 kB; size-neutral elsewhere (real code uses dynamic buffer lengths, not numeric literals).
2026-06-16 07:46:28 +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
oxc-guard[bot] e9b8fd595c release(crates): oxc v0.135.0 (#23117)
### 💥 BREAKING CHANGES

- ee4dc73 ast: [**BREAKING**] Add `#[non_exhaustive]` to AST nodes
(#23046) (overlookmotel)
- 4c35362 ast: [**BREAKING**] Add
`AstBuilder::template_element_escape_raw` and
`template_element_escape_raw_with_lone_surrogates` methods (#23047)
(overlookmotel)

### 🚀 Features

- b846ab2 react_compiler: Integrate the Rust port of the React Compiler
(#22942) (Boshen)
- 5b8dd68 parser: Report TS1255 for invalid class definite assertions
(#22917) (camc314)
- 85efabf semantic: Make building the class table optional, off by
default (#22862) (Boshen)

### 🐛 Bug Fixes

- 556acdc codegen: Parenthesize TS-cast assignment targets (#23112)
(Boshen)
- 37169ff codegen: Don't emit space between postfix `--` and `>` when
minifying (#23036) (Boshen)
- a4b1bf7 codegen: Drop redundant whitespace in minified TypeScript
output (#23038) (Boshen)
- cf53285 parser: Report reserved type-declaration names in the parser
(#23035) (Boshen)
- 4e44969 ast: Fix UB in `escape_template_element_raw` (#23052)
(overlookmotel)
- c543154 parser: Report comma operator in JSX expression in the parser
(#23030) (Boshen)
- 325c94f codegen: Tighten conditional-type and constructor-type
whitespace when minifying (#23033) (Boshen)
- 95dd3a2 parser: Report `import type` alias to a non-external reference
in the parser (#23032) (Boshen)
- 90180b8 codegen: Drop space after `:` in function return type when
minifying (#23028) (Boshen)
- 6da876e parser: Report `abstract` private class field in the parser
(#23029) (Boshen)
- 28467ce codegen: Don't emit space before a postfix update operand when
minifying (#23027) (Boshen)
- cb29926 codegen: Drop redundant space after `export default` when
minifying (#23024) (Boshen)
- 62965ae codegen: Drop redundant space after `else` when minifying
(#23025) (Boshen)
- 989230a parser: Report compound assignment to non-simple target in the
parser (#23022) (Boshen)
- 06f367c parser: Report `super.#field` private access in the parser
(#23014) (Boshen)
- 184edef codegen: Print space before `const`/`declare` enum modifier
(#23013) (Boshen)
- 4d722e0 parser: Report duplicate switch `default` clause in the parser
(#23012) (Boshen)
- 597ed85 codegen: Parenthesize `let`/`async` for-of head target
(#23008) (Boshen)
- 8b631bf codegen: Remove stray space before mapped type value colon
(#23010) (Boshen)
- c08407e codegen: Don't over-parenthesize `in` inside an arrow in a
for-init (#23009) (Boshen)
- 600cd6f codegen: Parenthesize lower-precedence
`TSInstantiationExpression` operand (#23007) (Boshen)
- 187e1a5 codegen: Don't leak space after comment-only JSX expression
container (#23006) (Boshen)
- 294c473 codegen: Don't over-parenthesize `TSTypeAssertion` operand
(#23004) (Boshen)
- 786d96f codegen: Give `TSTypeAssertion` unary precedence (#23002)
(Boshen)
- 1295882 parser: Report `new.target` and `import.meta` syntax errors in
the parser (#23003) (Boshen)
- d727b6b codegen: Parenthesize `await` expression as base of `**`
(#23001) (Boshen)
- 67dfa08 codegen: Keep parentheses around `new` callees containing a
call (#22997) (Boshen)
- 17e7cf3 parser: Disallow unerasable `as`/`satisfies` assertions
(#22986) (Boshen)
- beb46d3 parser: Commit to module goal on decorated exports (#22941)
(Boshen)
- 49e63f7 isolated-declarations: Require annotations for satisfies
initializers (#22898) (camc314)
- 8c93601 isolated-declarations: Allow unknown enum initializer in
non-const enum (#22900) (camc314)

### ⚡ Performance

- 7d89909 parser: Peek instead of lookahead for yield disambiguation
(#23071) (Boshen)
- bf872f0 parser: Skip arrow lookahead for a parenthesized literal
(#23070) (Boshen)
- d19fc54 parser: Guard type-argument speculation behind an angle-token
check (#23069) (Boshen)
- 8eb5507 parser: Skip redundant member-rest re-scan on call entry
(#23068) (Boshen)
- 883dfc1 parser: Skip parse_call_expression_rest when no call follows
(#23063) (Boshen)
- b171153 parser: Peek before the await-using lookahead (#23059)
(Boshen)
- 56f21bd parser: Use peek_token for the TS `asserts` type predicate
(#23058) (Boshen)
- 68805ac parser: Use peek_token instead of checkpoint/rewind for
single-token decisions (#23056) (Boshen)
- 1f9d8eb ast: `AstBuilder::template_element_escape_raw` avoid
allocation if no escape required (#23053) (overlookmotel)
- 502b04d semantic: Move cold function redeclaration handling into
`#[cold]` function (#22973) (overlookmotel)

### 📚 Documentation

- 275d318 napi/minifier: Point `target` to oxc docs (#23102) (camc314)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-06-08 22:17:46 +08:00
oxc-guard[bot] 6c30f81eca release(crates): oxc v0.134.0 (#22882)
### 🚀 Features

- 9c71f2e ast, codegen, formatter: Add `WithClauseKeyword::as_str`
helper and use it (#22791) (camc314)

### 🐛 Bug Fixes

- cf5769c parser: Reject TypeScript declarations in single-statement
context (#22827) (Boshen)
- c360fb6 parser: Reject generators in ambient contexts and overload
signatures (TS1221/TS1222) (#22848) (Boshen)
- cc60d8d parser: Reject invalid index signature parameter types
(TS1268/TS1337) (#22852) (Boshen)
- 3d13e29 parser: Reject `declare` in an already-ambient context
(TS1038) (#22850) (Boshen)
- 5152854 parser: Reject statements in ambient contexts (TS1036)
(#22849) (Boshen)
- 4f9afc5 parser: Reject `export as namespace` inside a namespace body
(TS1316) (#22846) (Boshen)
- 2eafea6 parser: Reject function implementations in ambient contexts
(TS1183) (#22845) (Boshen)
- c645615 parser: Reject incompatible class member modifiers (#22843)
(Boshen)
- 276b78b parser: Reject module-referencing imports/exports in a
namespace body (#22836) (Boshen)
- 842ed1c parser: Parse `class implements` with `implements` as the
class name (#22801) (Boshen)

### ⚡ Performance

- 7e3a567 parser: Reuse cached token kind in delimited-list loops
(#22841) (Boshen)
- 9e741c2 parser: Use peek_token instead of lookahead on the modifier
path (#22842) (Boshen)
- 9e496a7 semantic: Defer declare lookup for empty accessors (#22810)
(camc314)
2026-06-01 17:27:01 +01:00
oxc-guard[bot] 83328b65ce release(crates): oxc v0.133.0 (#22734)
### 🚀 Features

- e857b0c napi/minify: Expose legalComments option and result (#20370)
(Boshen)
- 661132d parser: More friendly error messages for rest assignment
target and rest binding element (#22719) (sapphi-red)
- ee659b6 transformer/legacy-decorator: Add `strictNullChecks` option
for nullable-union design:type (#22266) (Kyle Cannon)

### 🐛 Bug Fixes

- e1d064e transformer/class-properties: Reparent lifted private method
helpers (#22716) (Cameron)
- 4ac0fca minifier: Preserve `0 && (module.exports = { ... })`
cjs-module-lexer hint (#22729) (Dunqing)
- 40ff611 minifier: Mark peephole loop changed when dropping
dead-after-throw statement (#22722) (Dunqing)
- 2f7b210 codegen: Emit pife-arrow/function leading comments inside the
wrap (#22720) (Dunqing)
- e184f74 parser: Improve invalid `import` property access diagnostic
(#22693) (camc314)
- 7baed9c transformer/private-method: Clear inherited strict flags
(#22508) (camc314)
- a9ad27e parser: Keep annotation comments leading without preceding
newline (#22711) (Dunqing)
- 9ea4d64 minifier: Re-evaluate pure/no-side-effects flags after
peephole inlining (#22595) (Dunqing)
- 07afbb6 minifier: Drop empty-body IIFE wrapper when called with
arguments (#22589) (Dunqing)
- fa7c463 semantic: Correct TS enum member symbol spans (#22689)
(camc314)
- 26b9396 semantic: Resolve parameter decorators outside parameter scope
(#22623) (camc314)
- b284045 parser: Switch to module goal eagerly on `export` (#22684)
(Boshen)
- dfa931d semantic: Propagate unresolved auto-increment enum value
instead of defaulting to 0 (#22646) (Dunqing)
- 69a6ba6 transformer/legacy-decorator: Emit Array for ReadonlyArray<T>
in decorator metadata (#22265) (Kyle Cannon)
- e421ef0 transformer/legacy-decorator: Return runtime binding for
design:type (#22640) (Dunqing)
- d61e1d7 codegen: Preserve verbatim text of pure/no-side-effects
comments (#22525) (Dunqing)
- 702b14e minifier: Preserve IIFE structure in DCE-only mode (#22547)
(Dunqing)
- 917da24 parser: Apply PURE comment through member-access chains
(#22566) (Dunqing)
- a069b1c codegen: Preserve quotes for cjs-module-lexer equality strings
(#22551) (Dunqing)

### ⚡ Performance

- 2f623b0 semantic: Skip unresolved checks for re-exports (#22660)
(camc314)
- 0d9553d semantic: Early-exit `check_object_expression` for objects
with <2 properties (#22668) (Dunqing)
- d721ad9 semantic: Use direct grandparent lookup for TS type parameters
(#22658) (camc314)
- 0aff288 semantic: Reorder numeric literal strict mode checks (#22657)
(camc314)
- 4d5ddb1 semantic: Reorder binding identifier checks (#22656) (camc314)
- e32acd8 semantic: Reorder identifier ambient binding check (#22653)
(camc314)
- 09fe178 semantic: Reorder ident reference strict mode check (#22652)
(camc314)
- 4b6add2 semantic: Avoid duplicate ident clone for bindings (#22663)
(camc314)
- 82f9662 parser: Check identifier kind before context flag (#22662)
(camc314)
- d7cd951 parser: Fast path identifier parsing and inline operator
helpers (#22650) (Boshen)
- 7b84314 semantic: Use direct byte access for numeric leading-zero
check (#22642) (camc314)
- 0345a31 semantic: Pre-size class elements hash map (#22618) (camc314)
- 04d3065 minifier: Drop per-call buffers in try_fold_concat (#22596)
(Dunqing)
- 4f289f1 semantic: Resolve_references_for_current_scope without a temp
Vec (#22599) (Dunqing)
- e862c15 semantic: Avoid heap alloc for var hoist scope ids (#22603)
(Dunqing)
- 8ff8674 semantic: Early return if `excess` is `0` in
`Stats::increase_by` (#22616) (camc314)
- 7a4120e semantic: Pre-reserve unresolved_references using
Stats::references (#22580) (Dunqing)

Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
2026-05-26 14:28:43 +08:00
oxc-guard[bot] 63b129182d release(crates): oxc v0.132.0 (#22527)
### 🐛 Bug Fixes

- 0f26de6 ecmascript: Resolve identifier value type via tracked
constants (#22234) (Alexander Lichter)
- c27a8cf minifier: Normalize `{ x: x }` shorthand so adjacent-if merge
is idempotent (#22401) (Dunqing)
- e431a0e parser: Break extends clause loop on fatal error (#22517)
(Boshen)
- e9ec7c6 minifier: Fold optional chains by base nullishness (#22236)
(Alexander Lichter)
- e6090e7 transformer: Keep enum IIFE when a non-inlinable value
reference remains (#22501) (Dunqing)
- 931b7d6 transformer: Inline const enum members through type-cast
wrappers (#22500) (Dunqing)
- b9615b2 codegen: Preserve string quotes in require() calls during
minification (#22475) (zennnnnnn11)
- c73c159 transformer/async-to-generator: Reparent parameter initializer
scopes (#22507) (camc314)
- ecfd3ca transformer/async-to-generator: Move only parameter bindings
(#22503) (camc314)
- 3ce3431 transformer/explicit-resource-managment: Preserve shadowed
for-head block (#22451) (camc314)

### ⚡ Performance

- ce92c6c semantic: `#[inline]` `Scoping::get_binding` (#22414)
(Dunqing)
- 98be95c regular_expression: Track regex flags via bitflags (#22427)
(Boshen)
- dbbc059 jsdoc: Skip should_attach_jsdoc when no remaining comments
(#22409) (Boshen)
- 217d7d8 minifier: Index `SymbolValues` by `SymbolId` (#22441)
(Dunqing)
- d782b78 minifier: Use BitSet for LiveUsageCollector live references
(#22425) (Boshen)
2026-05-18 12:09:14 +01:00
Alexander Lichter 0f26de6dd2 fix(ecmascript): resolve identifier value type via tracked constants (#22234)
`value_type` for `Expression::Identifier` returned `Undetermined` for
any non-global identifier, **even when the binding had zero writes and a
known constant initializer**. This causes code like `slot?.()` on a `let
slot;` with all writers tree-shaken away not being folded to `void 0`,
leaving dead code in the output.

This PR changes that by checking
`GlobalContext::get_constant_value_for_reference_id` for non-global
identifier references and mapping the resulting `ConstantValue` to a
`ValueType` via a new private helper.

Once the identifier resolves to a tracked nullish value, the existing
nullish branch in `fold_chain_expr` collapses the chain to `void 0`. The
same lookup also works for the opposite case (#21923).

The lookup is gated by making sure there are no write references, so
anything that is being written anywhere keeps its `Undetermined` type.

Closes rolldown/rolldown#9281
Related (but does not resolve) #21923

---------

Co-authored-by: Dunqing <dengqing0821@gmail.com>
2026-05-18 16:59:34 +08:00
oxc-guard[bot] 564ebbf46f release(crates): oxc v0.131.0 (#22459)
### 🚀 Features

- bc91a17 codegen: Expose `Codegen::with_source_type` method (#22432)
(camc314)

### 🐛 Bug Fixes

- 5ac7e79 minifier: Drop unused-var-init pure IIFEs and preserve
annotation for downstream (#22349) (Dunqing)
- 4ab57eb allocator: Fixed-size allocators use `VirtualAlloc` on Windows
(#22124) (overlookmotel)
- 66d77eb allocator: Fix segfault on Linux MUSL with fixed-size
allocators (#22388) (overlookmotel)
- b8fbc1f transformer/object-rest-spread: Correct scope id when moving
bindings (#22419) (camc314)
- 18edc2c codegen: Keep `Object.defineProperty` property name as plain
string in minify (#22400) (Dunqing)
- dda33de transformer/explicit-resource-management: Align lexical
binding scopes (#22320) (camc314)
- 8e79de8 transformer: Preserve for-await statement bodies (#22361)
(camc314)
- 0cba210 transformer/class: Replace `new.target` in static blocks
(#22360) (camc314)
- 67ab1c9 transformer/es2018/for-await: Hoist for-await generated
bindings (#22355) (camc314)
- c3ceb4a transformer/object-rest-spread: Use hoisted scope for `for-of`
temp refs (#22347) (camc314)

### ⚡ Performance

- 73a9043 allocator/bitset: Avoid temp heap `String` allocation (#22403)
(camc314)
- 8b2f4f9 transformer/object-rest-spread: Collect `Vec<SymbolId` over
`Vec<BindingIdentifier>` (#22418) (camc314)
- 83679ea parser: Split TriviaBuilder::handle_token hot/cold paths
(#22415) (Boshen)
- 2c7d781 codegen: Inline identifier-name accessors (#22411) (Boshen)
- 618bc76 diagnostics: Inline `OxcDiagnosticInner` to avoid heap
allocation (#22406) (Boshen)
- 0b4e158 parser: Reserve cap `2` for sequence expressions vec (#22374)
(camc314)
- 5f3bdd0 codegen: Add `#[inline]` to `code`, `code_len` (#22373)
(camc314)

Co-authored-by: overlookmotel <557937+overlookmotel@users.noreply.github.com>
2026-05-15 15:38:24 +01:00