Commit Graph

756 Commits

Author SHA1 Message Date
camc314 1d9b9d372b feat(ast): add GetNodeId trait (#26145)
## Summary

- add `GetNodeId` alongside `NodeId` in `oxc_syntax`
- add a dedicated `DeriveGetNodeId` following the existing `GetSpan` derive architecture
- generate implementations into `derive_get_node_id.rs`, delegating uniformly through `self.node_id()`
2026-09-02 15:35:26 +00:00
leaysgur 9f81002229 fix(formatter): re-add suppressed statement terminators per options.semi (#26220)
```js
// input (semi: true)
// oxfmt-ignore
const a = x

// before
// oxfmt-ignore
const a = x

// after
// oxfmt-ignore
const a = x;
```

```js
// input (semi: false)
let a = 1;
// oxfmt-ignore
[b].sort();

// before
let a = 1
// oxfmt-ignore
[b].sort(); // <- ASI failure

// after
let a = 1
// oxfmt-ignore
;[b].sort() // <- OK
```
2026-09-01 07:25:43 +00:00
leaysgur 769ffd590d fix(formatter): print suppressed typecast parens (#26218)
```js
// input
foo(
  // oxfmt-ignore
  /** @type {A} */ (x),
  /** @type {B} */ (y),
);

// before
foo(
  // oxfmt-ignore
  /** @type {A} */ x, // <-
  /** @type {B} */ (y),
);

// after
foo(
  // oxfmt-ignore
  /** @type {A} */ (x), // <-
  /** @type {B} */ (y),
);
```
2026-09-01 07:25:42 +00:00
leaysgur 662556c174 fix(formatter): print the idempotent placement for comments in dropped parens (#26041)
Fixes non-idempotent formatting with comment and dropped parens.
2026-08-25 09:28:11 +00:00
leaysgur 0fe2313d7a refactor(formatter): replace fmt_with_options with a context handoff (#26037)
Remove `fmt_with_options` impl to justify what to be generated and not.

In the first place, for `Function` one was not used.
2026-08-24 06:31:20 +00:00
leaysgur 6419ba452b refactor(formatter): make write_supressed as trait impl default (#26036)
Pure refactoring.
2026-08-24 06:31:19 +00:00
leaysgur bf37dd5c43 fix(formatter): preserve class decorators before export when the statement is suppressed (#26034)
Fixes #25911

---

As syntax, these two forms are equivalent:

```ts
@deco export class X {}
export @deco class X {}
```

However, in the former, the decorators lie outside the span of both the `ExportDeclaration` and the `Class`, they are inside no node's span.

The formatter's suppression path prints the `ExportDeclaration`'s span verbatim, so the decorators were silently dropped.
The latter form is unaffected because the decorators fall inside the export node's span.
2026-08-24 06:31:19 +00:00
Boshen 2cde1f6671 fix(rust): address nightly deprecations (#25998)
Addresses deprecations reported by the latest Rust nightly while preserving Rust 1.98 compatibility.

- Rename `unit-bindings` to `unit_bindings`: hyphenated lint names are deprecated in favor of their canonical underscore spelling.
- Replace `isize::max_value()` and `usize::max_value()` with `isize::MAX` and `usize::MAX`: the legacy numeric methods are deprecated in favor of associated constants.
- Set explicit `MAX_INDEX` values on `define_index_type!`: `oxc_index` otherwise generates `<raw>::max_value() as usize`; the explicit modern constants avoid that deprecated expansion without changing the index limits.
- Remove obsolete `clippy::legacy_numeric_constants` expectations: the corrected code no longer triggers that lint, so the expectations would be unfulfilled.
- Allow the AST scanner to consume optional `define_index_type!` configuration: it previously parsed only the struct declaration and rejected the added `MAX_INDEX` option.

AI-assisted.
2026-08-22 11:34:02 +00:00
camc314 c0f71564c3 chore(deps): bump MSRV to 1.96.0 (#25946)
## Summary

- bump the workspace MSRV from 1.95.0 to 1.96.0 under the N-2 policy after #25945
- retain an MSRV-compatible layout calculation while suppressing the Rust 1.98 Clippy suggestion
2026-08-20 18:23:40 +00:00
renovate[bot] d5d575c1de chore(deps): update rust to v1.98.0 (#25945)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [rust](https://rust-lang.org/)
([source](https://redirect.github.com/rust-lang/rust),
[changelog](https://redirect.github.com/rust-lang/rust/blob/main/RELEASES.md))
| toolchain | minor | `1.97.1` → `1.98.0` |

---

### Configuration

📅 **Schedule**: (in timezone Asia/Shanghai)

- Branch creation
  - "before 10am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/oxc-project/oxc).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zNS40IiwidXBkYXRlZEluVmVyIjoiNDQuMzUuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-20 18:04:07 +00:00
Dunqing 2f5cdb1231 feat(minifier): add property name mangling (#24740)
Oxc can shorten variable names and private class names, but it cannot shorten property names such as `obj._field`. This change adds optional property mangling. It replaces #23772 and gives bundlers a way to use the same property names across several files.

### Feature

- `include` selects names to mangle. `exclude` and `reserved` keep names unchanged.
- The cache can give a property a fixed output name or keep it unchanged.
- The mangler supports member access, object and class keys, destructuring, optional chains, the left side of `in`, and JSX property names.
- `mangle_quoted` also handles strings and template literals used directly as property keys. `/* @__KEY__ */` and `/* #__KEY__ */` can mark one other string or template literal as a property key.
- Short names are assigned in a stable order. More common properties get shorter names. Existing and reserved names are avoided.
- Bundlers can collect names from several programs, create one shared map, and apply it to every program.
- Numeric keys, `__proto__`, `constructor`, `prototype`, TypeScript-only keys, and private names are never changed by this pass.

### Design

Property names are part of JavaScript syntax, not local scope. For this reason, the code lives in `oxc_minifier`. It only reuses the short-name generator from `oxc_mangler`.

For one program, the order is:

```text
collect names → assign names → rewrite once → compress → mangle variables and private names
```

Property names are rewritten before compression because compression can remove information about quotes and comments.

For several programs, a bundler collects names from each program, merges the results, assigns one shared map, and rewrites each program once. Collection can run in parallel. All programs must use the same options.

The shared map keeps property names consistent inside one build. The cache has a different job: it keeps fixed names and reservations across separate minify calls.

### Example

With `include: "^_"` and `mangle_quoted: true`:

```js
obj[`_field`] = 1;
obj._field = 2;
```

becomes:

```js
obj[`e`] = 1;
obj.e = 2;
```

### Limitation

- Property mangling is unsafe when other code still uses the old property name. Public APIs and names shared with external code, host APIs, imported namespace objects, or separate builds must be excluded or reserved.
- Quote handling is per use. Without `mangle_quoted`, `obj._field` may change while `obj["_field"]` does not.
- The mangler cannot find names built at runtime or stored in arbitrary strings. Code that uses `eval`, `Function`, or `with` must reserve any affected names.
- Compression may create a matching property after this pass has finished. For example, `'f' + 'oo_'` may later become `foo_` and will not be mangled.
- TypeScript enum reverse mappings are not supported because the transform creates quoted strings.
- Multi-program support is a caller API. The caller must collect every program that shares mangled properties before assigning names.

The public `oxc-minify` API is stacked in #24741. The downstream multi-chunk integration is shown in rolldown/rolldown#10374.

Supersedes #23772.

AI assistance: OpenAI Codex and Claude were used for research, implementation, review, and testing. The contributor is responsible for reviewing and understanding the changes before submission.
2026-08-17 07:50:51 +00:00
leaysgur 44231fb5f7 fix(formatter): fix comment and indent issues around unions (#25665)
Fixes comment-placement bugs, idempotency and also inconsistency around unions/intersections, type aliases, conditional types, and assignment-likes.

- Fix invariant violations where comments crossed `=`, the value, or `;`
  - removed the relocation logic emulating old Prettier 3.8 behavior
- Normalize a union's leading comments to one canonical form behind the synthesized leading `|`, independent of the source shape
- Comments around a formatter-added `(` keep their source side of the `(`
- Hug conditional-type branch comments behind `?`/`:` with the content one level under the operator, matching conditional expressions
- Extend `= // c` position preservation from type aliases to all of JS
2026-08-16 15:04:28 +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
overlookmotel ef8f33e5fc refactor(ast_tools): avoid #[doc] for plain doc comments (#25237)
Pure refactor. `ast_tools` generators use plain doc comments instead of the the more verbose `#[doc = "..."]` where the comment can be written statically.
2026-08-03 09:34:21 +00:00
overlookmotel 183d8b94f7 refactor(ast): use impl GetAstBuilder instead of generic params in AST builder methods (#25175)
Pure refactor. Continuation of #25174.

Simplify AST builder methods by switching from generic param `B: GetAstBuilder<'a>` to `impl &GetAstBuilder<'a>`. This makes the code shorter, and easier to read.

Before:

```rust
pub fn new_big_int_literal<B: GetAstBuilder<'a>>(
    // ... etc ...
    builder: &B,
) -> Self
```

After:

```rust
pub fn new_big_int_literal(
    // ... etc ...
    builder: &impl GetAstBuilder<'a>,
) -> Self
```
2026-08-01 15:40:55 +00:00
overlookmotel 5d129463c6 refactor(ast): simplify AST builder method generics (#25174)
Pure refactor.

Simplify AST builder methods by switching from generic params to `impl`s. This makes the code shorter, and easier to read.

Before:

```rust
pub fn new_big_int_literal<B: GetAstBuilder<'a>, S1>(
    value: S1,
    // ... etc ...
) -> Self
where
    S1: Into<Str<'a>>
```

After:

```rust
pub fn new_big_int_literal<B: GetAstBuilder<'a>>(
    value: impl Into<Str<'a>>,
    // ... etc ...
) -> Self
```
2026-08-01 15:40:55 +00:00
overlookmotel 2ea0aa9537 docs(ast): correct grammar in AST builder methods doc comments (#25173)
Docs-only change.

Correct the function in `ast_tools` that decides whether a word needs "a" or "an" before it. This corrects the grammar of doc comments for AST builder methods related to `UnaryExpression`.
2026-08-01 15:40:54 +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
leaysgur 6bc1fe1f03 refactor(formatter): drop oxc_formatter_core re-export (#25140)
Leftovers from when we extract the `oxc_formatter_core`.
2026-07-31 08:11:23 +00:00
camc314 8541e18e91 feat(ast_tools): generate minifier scope collector (#25088)
## Summary

- generate `ChildScopeCollector` for both `oxc_traverse` and `oxc_minifier` from the same AST tools generator
- synchronize the minifier collector with the current AST schema
- prevent the two generated collector implementations from drifting
2026-07-29 16:25:26 +00:00
overlookmotel 07cff4e79b refactor(ast): rename Vec type params on AST builder methods (#25039)
Pure refactor, cosmetic change.

After #25038, all the generic params on AST builder methods are for `Vec`s. Reflect that in the naming of the generic params on these methods - name them `V1`, `V2` etc, instead of `T1`, `T2` ("V" for "Vec").
2026-07-28 22:40:04 +00:00
overlookmotel 7e1199cbbb feat(ast)!: remove conversion to Box from AST builder methods (#25038)
AST builder methods accepted an `IntoIn<ArenaBox<T>>` for all params where an `ArenaBox<T>` is required. You could pass a plain `T`, and the builder method would automatically allocate space for it, and copy it into the arena.

This was originally intended as a convenience, but in fact it's a footgun - it encourages allocating late, holding types on the stack during other work, which results in register pressure, and churn of copying to/from the stack.

Remove the automatic conversion. All places where we were using auto-allocation are already fixed in previous PRs #25034, #25035, #25036, #25037.

Additional benefits of this change:

1. AST builder methods become less polymorphic which should help with compile times.
2. We can get rid of the `NONE` type, which was a hacky workaround, necessary for passing as params where an `IntoIn<Option<ArenaBox<T>>>` is required. Now these all become plain `None`.

Most of the diff in this PR is just replacing `NONE` with `None` all over the place.

Note: AST builder methods still define params which are `ArenaVec`s as `IntoIn<ArenaVec<T>>`, as this does add genuine value - it allows passing `[]` instead of the much more verbose `ArenaVec::new_in(self)`.
2026-07-28 22:40:04 +00:00
Boshen cc623d93db feat(ast_visit): add VisitJsMut visitor (#24828)
## Summary

Generate `VisitJsMut` and `walk_js_mut` using the same JavaScript-only
traversal rules as `VisitJs`.

This avoids monomorphizing pure TypeScript type-space traversal for
mutable runtime visitors.

AI assistance was used to implement this change. I reviewed and take
responsibility for the result.
2026-07-27 16:20:25 +08:00
overlookmotel fbab4c31d2 refactor(ast): monomorphise ArenaBox::new calls in AST builder methods (#24926)
Continuation of #24925. `ArenaBox::new_in` takes any `&A where A: GetAllocator`. To reduce polymorphism, in AST builder methods convert the generic builder type to a concrete `&Allocator` before passing to `ArenaBox::new_in`.

Same as #24925, this does not impact binary size, as `ArenaBox::new_in` is inlined anyway, but it may reduce compile times due to reduce polymorphism.
2026-07-25 20:13:28 +00:00
overlookmotel 1ac5ac76f2 feat(ast)!: tighten bound on AstBuild trait (#24925)
Bound any type which implements `AstBuild` to also implement `GetAstBuilder` (referring to itself).

This means the type returned by `.builder()` on any `GetAstBuilder` type can be passed to any other method which takes `&B where B: GetAstBuilder` (e.g. any AST builder method).

Use this in AST builder methods to make their calls to other AST builder methods monomorphic over the actual `AstBuilder` type. This should reduce compile times, as less polymorphic types for the compiler to deal with. Note: It will _not_ reduce binary size, as all these methods are trivial and inlined anyway.

This also opens the door to reducing binary size in Rolldown. Some of Rolldown's custom AST builder methods are _not_ trivial and are currently polymorphic over multiple `GetAstBuilder` types (of which Rolldown has many). This change will allow splitting those methods into 2 - a generic wrapper method, and an inner implementation which is always called with the same `AstBuilder` type.
2026-07-25 20:13:28 +00:00
overlookmotel c5503a9e82 refactor(ast): rename file containing AST builder methods (#24762)
Pure refactor. #24563 removed the old `ast_builder` module, so now the new one can take over its rightful name. We may introduce _other_ kinds of builders in future - this name is unambiguous.
2026-07-22 08:28:58 +00:00
overlookmotel c231146ce6 refactor(ast_tools): rename BuilderMethodsGenerator to AstBuilderGenerator (#24761)
Pure refactor. #24563 removed the old `AstBuilderGenerator`, so now the new one can take over its rightful name. We may introduce _other_ kinds of builders in future - this name is unambiguous.
2026-07-22 08:19:04 +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
renovate[bot] 75ce81de95 chore(deps): update rust crate syn to v3 (#24684)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [syn](https://redirect.github.com/dtolnay/syn) |
workspace.dependencies | major | `2` → `3` |

---

### Release Notes

<details>
<summary>dtolnay/syn (syn)</summary>

###
[`v3.0.0`](https://redirect.github.com/dtolnay/syn/releases/tag/3.0.0)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.119...3.0.0)

This release contains adjustments to the syntax tree to account for
ongoing Rust language development from the 3 years since syn 2.0.0 and
to anticipate some in-flight Rust language RFCs.

These include: default values in fields, pinned type sugar, raw
lifetimes, generator blocks and functions, unnamed enum variants,
attributes in tuple types and tuple patterns, named arguments in
parenthesized generic argument lists, lightweight clones, const traits,
const function pointers, mutability restricted fields, supertrait auto
implementation, final associated functions, trait implementability
restrictions, const blocks in path arguments, item-level const blocks,
return type notation, never patterns, function delegation, mutable
by-reference bindings, in-place initialization, field projections,
explicitly dyn-compatible traits, view types, file-level frontmatter,
generic const arguments, guard patterns, lazy type aliases, explicitly
safe foreign items, super let, unsafe fields, pattern types,
heterogeneous try-blocks, function contracts, async function trait
bounds, static closure coroutine syntax, unsafe binder types, move
expressions, for-await loops, and postfix keywords.

<br>

<p align="center"><a href="https://docs.rs/syn/3/syn">[API documentation
for 3.0]</a></p>

### Breaking changes
#### Modifiers

To reserve more room for language evolution, there are 10 new
non-exhaustive structs in the syntax tree having the following
commonality:

- Name ending in `Modifiers`. {`BlockModifiers`, `ClosureModifiers`,
`ConstModifiers`, `FieldModifiers`, `FnModifiers`, `ImplModifiers`,
`LocalModifiers`, `TraitBoundModifiers`, `TraitModifiers`,
`TypeModifiers`}

- Each implements `Default`. The default value is guaranteed to comprise
no tokens.

- Non-exhaustive. Can only be instantiated by Syn's parser or by
creating and then mutating `▁▁Modifiers::default()`.

- Does not implement `Parse`. When parsing, they are parsed by the
enclosing syntax tree node.

- Does not implement `ToTokens`. In some cases the syntax that these
nodes might hold in the future is not necessarily contiguous tokens.

- Provides `.require_empty() -> Result<()>` which returns a meaningfully
spanned error if the modifiers are different from the empty default.
This enables a caller to reject syntax it does not recognize without
knowing what that syntax may be.

#### Types

- `Type::BareFn` has been renamed to `Type::FnPtr` to mirror the
compiler's terminology. Together with this, `BareVariadic` is renamed to
`FnPtrVariadic`.

- The mutually exclusive `const_token` and `mutability` fields of
`Type::Ptr` have been unified into an enum of type `PointerMutability`,
which was already previously used by `Expr::RawAddr`.

- Every `Type` variant now holds attributes, which can represent the
attributes of element types inside a tuple type, or attributes for a
function return type.

- `BareFnArg` is renamed to `NamedArg` and is used in
`ParenthesizedGenericArguments`, in addition to the existing use in
`Type::FnPtr`.

#### Expressions

- In `Expr::Closure`, the fields `or1_token` and `or2_token` have been
renamed to `inputs_begin` and `inputs_end` to indicate the beginning and
ending `|` token of the closure inputs.

#### Statements

- Attributes are now preserved on all expression kinds in statement
position. Previously, parsing would silently lose attributes on some
expression statements without a dedicated Expr variant, such as
tail-call expressions.

#### Patterns

- The `guard` field of `Arm` is replaced by a new `Pat::Guard` variant
held in the arm's `pat`.

#### Items

- The `unsafety` field of `Signature`, which represented the presence or
absence of `unsafe`, is replaced by a 3-way `Safety` enum which may be
safe, unsafe, or default. `ForeignItem::Static` also gets a `Safety`.

- Some of the fields of `Receiver` have been split to a non-exhaustive
`ReceiverKind` enum to create room for proposed new kinds of method
receivers, such as pinned.

- Type aliases now hold a `WhereClausePlacement` to distinguish between
early placement (which is the default for item-level type aliases and is
*deprecated* in associated type aliases) and late placement (which is
the default for associated type aliases and is *unstable* in item-level
type aliases). Parsing and printing a syntax tree will now preserve the
where-clause placement rather than rewriting it to the default placement
for the item kind.

#### Generics

- `WherePredicate::Lifetime` and `WherePredicate::Type` have a new field
holding the attributes on the where-predicates.

- `GenericParam::Type` and `GenericParam::Const` now match the rest of
the syntax tree in holding their optional default using a single Option
of tuple, rather than a pair of Option.

#### Literals

- The `Parse` implementation of `Lifetime` no longer permits keyword
lifetimes, matching a change in Rust 1.81 to deny such lifetimes
pre-expansion. `Lifetime::parse_any` may be used to parse lifetimes
permissive of keywords.

- `LitInt` and `LitFloat` no longer implement
`From<proc_macro2::Literal>`. Use `Lit::new(literal)` and then match
`Lit::Int` or `Lit::Float`.

- The `StrStyle` enum was unused and has been deleted.

#### More

- `File` contains a new `Option<Frontmatter>`, but does not yet parse
frontmatter in `syn::parse_file` until that language feature progresses
closer to stabilization.

- Some enums no longer provide `From` impls. Construct the variant by
name instead, such as `Expr::Array(e)` in place of `e.into()`.

- The `pop` method of `Punctuated<T, P>` now returns `Option<T>`
discarding the trailing punctuation if any, rather than `Option<Pair<T,
P>>`. A new method `pop_pair` provides the old behavior.

- Syntax tree traversal (`visit`, `visit_mut`, `fold`) no longer
provides a method for processing `Span`. This was already incompletely
supported and never walked most of the spans in a syntax tree.

- The `Speculative` and `AnyDelimiter` traits have been sealed and
cannot be implemented for types outside of Syn's `ParseStream`.

</details>

---

### Configuration

📅 **Schedule**: (in timezone Asia/Shanghai)

- Branch creation
  - "before 10am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/oxc-project/oxc).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI2NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
2026-07-19 20:57:12 +01:00
renovate[bot] da927afaa5 chore(deps): update rust crates (#24681)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [anyhow](https://redirect.github.com/dtolnay/anyhow) |
workspace.dependencies | patch | `1.0.103` → `1.0.104` |
| [bitflags](https://redirect.github.com/bitflags/bitflags) |
workspace.dependencies | patch | `2.13.0` → `2.13.1` |
| [compact_str](https://redirect.github.com/ParkMyCar/compact_str) |
workspace.dependencies | minor | `0.9.1` → `0.10.0` |
| [futures](https://rust-lang.github.io/futures-rs)
([source](https://redirect.github.com/rust-lang/futures-rs)) |
workspace.dependencies | patch | `0.3.32` → `0.3.33` |
|
[ignore](https://redirect.github.com/BurntSushi/ripgrep/tree/master/crates/ignore)
([source](https://redirect.github.com/BurntSushi/ripgrep/tree/HEAD/crates/ignore))
| workspace.dependencies | patch | `0.4.28` → `0.4.30` |
| [prettyplease](https://redirect.github.com/dtolnay/prettyplease) |
workspace.dependencies | minor | `0.2.37` → `0.3.0` |
| [proc-macro2](https://redirect.github.com/dtolnay/proc-macro2) |
workspace.dependencies | patch | `1.0.106` → `1.0.107` |
| [quote](https://redirect.github.com/dtolnay/quote) |
workspace.dependencies | patch | `1.0.46` → `1.0.47` |
| [self_cell](https://redirect.github.com/Voultapher/self_cell) |
workspace.dependencies | minor | `1.2.2` → `1.3.0` |
| [serde](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.228` → `1.0.229` |
| [syn](https://redirect.github.com/dtolnay/syn) |
workspace.dependencies | patch | `2.0.118` → `2.0.119` |
| [tokio](https://tokio.rs)
([source](https://redirect.github.com/tokio-rs/tokio)) |
workspace.dependencies | minor | `1.52.3` → `1.53.0` |
| [toml](https://redirect.github.com/toml-rs/toml) |
workspace.dependencies | patch | `1.1.2+spec-1.1.0` → `1.1.3` |

---

### Release Notes

<details>
<summary>dtolnay/anyhow (anyhow)</summary>

###
[`v1.0.104`](https://redirect.github.com/dtolnay/anyhow/releases/tag/1.0.104)

[Compare
Source](https://redirect.github.com/dtolnay/anyhow/compare/1.0.103...1.0.104)

- Update `syn` dev-dependency to version 3

</details>

<details>
<summary>bitflags/bitflags (bitflags)</summary>

###
[`v2.13.1`](https://redirect.github.com/bitflags/bitflags/blob/HEAD/CHANGELOG.md#2131)

[Compare
Source](https://redirect.github.com/bitflags/bitflags/compare/2.13.0...2.13.1)

#### What's Changed

- Lower the LLVM IR output of the generated output by
[@&#8203;bolshoytoster](https://redirect.github.com/bolshoytoster) in
[#&#8203;492](https://redirect.github.com/bitflags/bitflags/pull/492)

#### New Contributors

- [@&#8203;bolshoytoster](https://redirect.github.com/bolshoytoster)
made their first contribution in
[#&#8203;492](https://redirect.github.com/bitflags/bitflags/pull/492)

**Full Changelog**:
<https://github.com/bitflags/bitflags/compare/2.13.0...2.13.1>

</details>

<details>
<summary>ParkMyCar/compact_str (compact_str)</summary>

###
[`v0.10.0`](https://redirect.github.com/ParkMyCar/compact_str/blob/HEAD/CHANGELOG.md#0100)

[Compare
Source](https://redirect.github.com/ParkMyCar/compact_str/compare/v0.9.1...v0.10.0)

##### July 12, 2026

#### Changes

- Minimum supported Rust version (MSRV) was bumped to `v1.71`.
- The `Error` trait impls once again require the `std` feature. This
reverts the (unreleased)
no-std `core::error::Error` support from
[#&#8203;443](https://redirect.github.com/ParkMyCar/compact_str/pull/443),
which required Rust `v1.81`, it can return once the MSRV reaches
`v1.81`.
- Added support for the [`garde`](https://crates.io/crates/garde)
validation crate under an
optional feature, implementing its string rule traits for
`CompactString`.
- Implemented in [`feat: add garde validation
support`](https://redirect.github.com/ParkMyCar/compact_str/pull/471).
- Added support for the [`schemars`](https://crates.io/crates/schemars)
crate under an optional
feature, implementing `JsonSchema` for `CompactString`. Every method
delegates to `String`'s
  impl, so the generated schema is identical to a `String`'s.
- Implemented in [`feat: support
schemars`](https://redirect.github.com/ParkMyCar/compact_str/pull/454).
- Added support for the [`valuable`](https://crates.io/crates/valuable)
crate under an optional
feature, implementing the `Valuable` trait for `CompactString` so it
presents as a `Value::String`.
- Implemented in [`Add impl
Valuable`](https://redirect.github.com/ParkMyCar/compact_str/pull/451).
- Added a `pyo3` feature which implements
[`FromPyObject`](https://docs.rs/pyo3/0.29/pyo3/conversion/trait.FromPyObject.html)
and
[`IntoPyObject`](https://docs.rs/pyo3/0.29/pyo3/conversion/trait.IntoPyObject.html)
for `CompactString`, so it can
  be used seamlessly with [`pyo3`](https://crates.io/crates/pyo3) v0.29.
- Implemented in [`feat: add pyo3
feature`](https://redirect.github.com/ParkMyCar/compact_str/pull/446).
- Added support for the [`defmt`](https://crates.io/crates/defmt) crate
under an optional feature,
  implementing `defmt::Format` for `CompactString`.
- Implemented in [`feat: Add support for
defmt`](https://redirect.github.com/ParkMyCar/compact_str/pull/438).
- Added a `bevy-reflect` feature which implements
[`bevy_reflect`](https://crates.io/crates/bevy_reflect)'s
  reflection traits for `CompactString` (as an opaque type).
- Implemented in [`feat: Add bevy-reflect
feature`](https://redirect.github.com/ParkMyCar/compact_str/pull/439).
- Added a `utoipa` feature which implements
[`utoipa`](https://crates.io/crates/utoipa)'s
  `PartialSchema` and `ToSchema` traits for `CompactString`.
- Implemented in [`fix sqlx type impl, add utoipa
feature`](https://redirect.github.com/ParkMyCar/compact_str/pull/442)
and [`feat: fixup the utoipa
feature`](https://redirect.github.com/ParkMyCar/compact_str/pull/445).
- Removed `#[must_use]` from the `Drain` iterator.
- Implemented in [`fix: remove #[must_use] from
Drain`](https://redirect.github.com/ParkMyCar/compact_str/pull/486).
- Added [Kani](https://redirect.github.com/model-checking/kani) proof
harnesses to formally verify the crate's
  unsafe code.
- Implemented in [`feat: add kani proof harnesses to verify unsafe
code`](https://redirect.github.com/ParkMyCar/compact_str/pull/480).
- Bump the [`sqlx`](https://crates.io/crates/sqlx) dependency to `v0.9`.
Enabling the `sqlx`
  feature now requires Rust `v1.94`.
- Implemented in [`deps: bump sqlx to
0.9`](https://redirect.github.com/ParkMyCar/compact_str/pull/474).
- Bump the [`bevy_reflect`](https://crates.io/crates/bevy_reflect)
dependency to `v0.19`.
- Implemented in [`deps: update optional deps to
latest`](https://redirect.github.com/ParkMyCar/compact_str/pull/490).
- Bump the [`markup`](https://crates.io/crates/markup) dependency to
`v0.16`.
- Implemented in [`deps: update optional deps to
latest`](https://redirect.github.com/ParkMyCar/compact_str/pull/490).

#### Bug Fixes

- Fixed possible undefined behavior when reallocating a heap-allocated
`CompactString`.
- Implemented in [`fix: possible undefined behavior with realloc in a
HeapString`](https://redirect.github.com/ParkMyCar/compact_str/pull/459).
- Fixed `CompactString::retain` to avoid briefly creating references to
invalid UTF-8.
- Implemented in [`Avoid invalid UTF-8 references in
retain`](https://redirect.github.com/ParkMyCar/compact_str/pull/469).
- Fixed `CompactString::as_mut_bytes` exposing uninitialized spare
capacity through a `&mut [u8]`;
  heap spare capacity is now zero-initialized before it's returned.
- Implemented in [`fix: avoid exposing uninitialized spare
capacity`](https://redirect.github.com/ParkMyCar/compact_str/pull/468).

#### Performance

- Reuse an owned `String`'s existing allocation in `CompactString::new`.
- Implemented in [`perf: reuse owned String in
CompactString::new`](https://redirect.github.com/ParkMyCar/compact_str/pull/460).
- Specialize `ToCompactString` for `str`.
- Implemented in [`perf: specialize ToCompactString for
str`](https://redirect.github.com/ParkMyCar/compact_str/pull/461).
- Pass heap fields by value when dropping a `CompactString` instead of
`&mut Repr`.
- Implemented in [`perf: Drop passes heap fields by-value instead of
&mut Repr`](https://redirect.github.com/ParkMyCar/compact_str/pull/462).
- Skip the `Result` on the hot inline construction path with
`Repr::new_panic`.
- Implemented in [`perf: add Repr::new_panic to skip Result on the hot
inline
path`](https://redirect.github.com/ParkMyCar/compact_str/pull/463).
- Assert internal `Repr` invariants so the compiler can fold dead
error-handling branches on construction.
- Implemented in [`perf: assert Repr invariants to fold dead Err arms on
construction`](https://redirect.github.com/ParkMyCar/compact_str/pull/467).
- Speed up append, integer formatting, and inline creation.
- Implemented in [`perf: speed up append, integer formatting, and inline
creation`](https://redirect.github.com/ParkMyCar/compact_str/pull/479).
- Bulk-copy valid runs in `from_utf8_lossy` instead of copying
byte-by-byte.
- Implemented in [`perf: bulk-copy valid runs in
from_utf8_lossy`](https://redirect.github.com/ParkMyCar/compact_str/pull/484).
- Specialize memory copies for small/medium strings to avoid calling
`memcpy`. Keep heap construction out-of-line,
and move the OOM panic off the hot construction path to keep generated
assembly lean.
- Implemented in [`perf: several small
improvements`](https://redirect.github.com/ParkMyCar/compact_str/pull/488).
- Replaced the `ryu` dependency with
[`zmij`](https://crates.io/crates/zmij) for float formatting.
- Implemented in [`deps: replace ryu with zmij for float
formatting`](https://redirect.github.com/ParkMyCar/compact_str/pull/485).

</details>

<details>
<summary>rust-lang/futures-rs (futures)</summary>

###
[`v0.3.33`](https://redirect.github.com/rust-lang/futures-rs/blob/HEAD/CHANGELOG.md#0333---2026-07-18)

[Compare
Source](https://redirect.github.com/rust-lang/futures-rs/compare/0.3.32...0.3.33)

- Fix `ReadLine`'s soundness issue regarding to exception safety.
([#&#8203;3020](https://redirect.github.com/rust-lang/futures-rs/issues/3020))
- Fix unsound `Send` impl for `IterPinRef` and `Iter`.
([#&#8203;3003](https://redirect.github.com/rust-lang/futures-rs/issues/3003))
- Fix stacked borrows violation in `compat01as03` implementation.
([#&#8203;3012](https://redirect.github.com/rust-lang/futures-rs/issues/3012))
- Fix memory leak in `FuturesUnordered::IntoIter`.
([#&#8203;3005](https://redirect.github.com/rust-lang/futures-rs/issues/3005))
- Add `portable-atomic-alloc` feature and use it in `FuturesUnordered`.
([#&#8203;3007](https://redirect.github.com/rust-lang/futures-rs/issues/3007))
- Re-export `alloc::task::Wake`.
([#&#8203;3010](https://redirect.github.com/rust-lang/futures-rs/issues/3010))
- Update `spin` to 0.12.
([#&#8203;3014](https://redirect.github.com/rust-lang/futures-rs/issues/3014))

</details>

<details>
<summary>BurntSushi/ripgrep (ignore)</summary>

###
[`v0.4.30`](https://redirect.github.com/BurntSushi/ripgrep/compare/ignore-0.4.29...ignore-0.4.30)

[Compare
Source](https://redirect.github.com/BurntSushi/ripgrep/compare/ignore-0.4.29...ignore-0.4.30)

###
[`v0.4.29`](https://redirect.github.com/BurntSushi/ripgrep/compare/ignore-0.4.28...ignore-0.4.29)

[Compare
Source](https://redirect.github.com/BurntSushi/ripgrep/compare/ignore-0.4.28...ignore-0.4.29)

</details>

<details>
<summary>dtolnay/prettyplease (prettyplease)</summary>

###
[`v0.3.0`](https://redirect.github.com/dtolnay/prettyplease/releases/tag/0.3.0)

[Compare
Source](https://redirect.github.com/dtolnay/prettyplease/compare/0.2.37...0.3.0)

- Update to syn 3 (release notes:
<https://github.com/dtolnay/syn/releases/tag/3.0.0>)

</details>

<details>
<summary>dtolnay/proc-macro2 (proc-macro2)</summary>

###
[`v1.0.107`](https://redirect.github.com/dtolnay/proc-macro2/releases/tag/1.0.107)

[Compare
Source](https://redirect.github.com/dtolnay/proc-macro2/compare/1.0.106...1.0.107)

- Documentation improvements

</details>

<details>
<summary>dtolnay/quote (quote)</summary>

###
[`v1.0.47`](https://redirect.github.com/dtolnay/quote/releases/tag/1.0.47)

[Compare
Source](https://redirect.github.com/dtolnay/quote/compare/1.0.46...1.0.47)

- Documentation improvements

</details>

<details>
<summary>Voultapher/self_cell (self_cell)</summary>

###
[`v1.3.0`](https://redirect.github.com/Voultapher/self_cell/releases/tag/v1.3.0):
Version 1.3.0

[Compare
Source](https://redirect.github.com/Voultapher/self_cell/compare/v1.2.2...v1.3.0)

- Async `self_cell` construction is now `Send`, this makes using it with
tokio easier. This comes at no additional performance cost. See
[#&#8203;74](https://redirect.github.com/Voultapher/self_cell/issues/74)
for more details.
- Fixed some typos in the docs.

</details>

<details>
<summary>serde-rs/serde (serde)</summary>

###
[`v1.0.229`](https://redirect.github.com/serde-rs/serde/releases/tag/v1.0.229)

[Compare
Source](https://redirect.github.com/serde-rs/serde/compare/v1.0.228...v1.0.229)

- Update to syn 3

</details>

<details>
<summary>dtolnay/syn (syn)</summary>

###
[`v2.0.119`](https://redirect.github.com/dtolnay/syn/releases/tag/2.0.119)

[Compare
Source](https://redirect.github.com/dtolnay/syn/compare/2.0.118...2.0.119)

- Preserve attributes on tail-call expressions in statement position
([#&#8203;1994](https://redirect.github.com/dtolnay/syn/issues/1994))
- Parse field-representing types builtin in type position
([#&#8203;1996](https://redirect.github.com/dtolnay/syn/issues/1996))

</details>

<details>
<summary>tokio-rs/tokio (tokio)</summary>

###
[`v1.53.0`](https://redirect.github.com/tokio-rs/tokio/releases/tag/tokio-1.53.0):
Tokio v1.53.0

[Compare
Source](https://redirect.github.com/tokio-rs/tokio/compare/tokio-1.52.4...tokio-1.53.0)

### 1.53.0 (July 17th, 2026)

##### Added

- fs: implement `From<OwnedFd>` and `From<OwnedHandle>` for `File`
([#&#8203;8266])
- metrics: add task schedule latency metric ([#&#8203;7986])
- net: add `SocketAddr` methods to Unix sockets ([#&#8203;8144])

##### Changed

- io: add `#[inline]` to IO trait impls for in-memory types
([#&#8203;8242])
- net: implement UCred::pid on FreeBSD ([#&#8203;8086])
- net: support Nuttx target os ([#&#8203;8259])
- signal: refactor global variables on Windows ([#&#8203;8231])
- sync: `mpsc::{Receiver,UnboundedReceiver}` now drops waker on drop,
even if there are still senders ([#&#8203;8095])
- taskdump: support taskdumps on s390x ([#&#8203;8192])
- time: add `#[track_caller]` to `timeout_at()` ([#&#8203;8077])
- time: consolidate mutex locks on spurious poll ([#&#8203;8124])
- time: defer waker clone on spurious poll ([#&#8203;8107])
- time: move lazy-registration state into `Sleep` ([#&#8203;8132])
- tracing: remove unnecessary span clone ([#&#8203;8126])

##### Fixed

- io: do not treat zero-length reads as EOF in `Chain` ([#&#8203;8251])
- net: use getpeereid for QNX peer credentials ([#&#8203;8270])
- runtime: avoid illegal state in `FastRand` ([#&#8203;8078])
- sync: wake mpsc receiver when a queued `reserve[_many]` returns
permits ([#&#8203;8260])
- taskdump: skip double wake on `Trace::capture`/`Trace::trace_with`
([#&#8203;8043])
- time: avoid stack overflow in runtime constructor ([#&#8203;8093])
- time (alt timer): ensure timers stay in the same runtime after
`.reset()` ([#&#8203;8169])

##### IO uring (unstable)

- fs: use io-uring for `fs::try_exists` ([#&#8203;8080])
- fs: use io-uring for renaming files ([#&#8203;7800])
- rt: flush io-uring CQE in case of CQE overflow ([#&#8203;8277])

##### Documented

- docs: clarify cancel safety wording ([#&#8203;8181])
- fs: clarify `create_dir_all` succeeds if path exists ([#&#8203;8149])
- io: add warning about stdout reordering with multiple handles
([#&#8203;8276])
- net: document pipe `try_read*`/`try_write*` readiness behavior
([#&#8203;8032])
- runtime: document interaction with fork() ([#&#8203;8202])
- sync: clarify broadcast lagging semantics ([#&#8203;8239])
- sync: document memory ordering guarantees for Semaphore
([#&#8203;8119])
- task: explain why `yield_now` defers its waker ([#&#8203;8254])
- time: add panic docs to `timeout_at()` ([#&#8203;8077])
- time: fix reversed poll order in timeout doc ([#&#8203;8214])

[#&#8203;7800]: https://redirect.github.com/tokio-rs/tokio/pull/7800

[#&#8203;7986]: https://redirect.github.com/tokio-rs/tokio/pull/7986

[#&#8203;8032]: https://redirect.github.com/tokio-rs/tokio/pull/8032

[#&#8203;8043]: https://redirect.github.com/tokio-rs/tokio/pull/8043

[#&#8203;8077]: https://redirect.github.com/tokio-rs/tokio/pull/8077

[#&#8203;8078]: https://redirect.github.com/tokio-rs/tokio/pull/8078

[#&#8203;8080]: https://redirect.github.com/tokio-rs/tokio/pull/8080

[#&#8203;8086]: https://redirect.github.com/tokio-rs/tokio/pull/8086

[#&#8203;8093]: https://redirect.github.com/tokio-rs/tokio/pull/8093

[#&#8203;8095]: https://redirect.github.com/tokio-rs/tokio/pull/8095

[#&#8203;8107]: https://redirect.github.com/tokio-rs/tokio/pull/8107

[#&#8203;8119]: https://redirect.github.com/tokio-rs/tokio/pull/8119

[#&#8203;8124]: https://redirect.github.com/tokio-rs/tokio/pull/8124

[#&#8203;8126]: https://redirect.github.com/tokio-rs/tokio/pull/8126

[#&#8203;8132]: https://redirect.github.com/tokio-rs/tokio/pull/8132

[#&#8203;8144]: https://redirect.github.com/tokio-rs/tokio/pull/8144

[#&#8203;8149]: https://redirect.github.com/tokio-rs/tokio/pull/8149

[#&#8203;8169]: https://redirect.github.com/tokio-rs/tokio/pull/8169

[#&#8203;8181]: https://redirect.github.com/tokio-rs/tokio/pull/8181

[#&#8203;8192]: https://redirect.github.com/tokio-rs/tokio/pull/8192

[#&#8203;8193]: https://redirect.github.com/tokio-rs/tokio/pull/8193

[#&#8203;8202]: https://redirect.github.com/tokio-rs/tokio/pull/8202

[#&#8203;8214]: https://redirect.github.com/tokio-rs/tokio/pull/8214

[#&#8203;8231]: https://redirect.github.com/tokio-rs/tokio/pull/8231

[#&#8203;8239]: https://redirect.github.com/tokio-rs/tokio/pull/8239

[#&#8203;8242]: https://redirect.github.com/tokio-rs/tokio/pull/8242

[#&#8203;8251]: https://redirect.github.com/tokio-rs/tokio/pull/8251

[#&#8203;8254]: https://redirect.github.com/tokio-rs/tokio/pull/8254

[#&#8203;8259]: https://redirect.github.com/tokio-rs/tokio/pull/8259

[#&#8203;8260]: https://redirect.github.com/tokio-rs/tokio/pull/8260

[#&#8203;8266]: https://redirect.github.com/tokio-rs/tokio/pull/8266

[#&#8203;8270]: https://redirect.github.com/tokio-rs/tokio/pull/8270

[#&#8203;8276]: https://redirect.github.com/tokio-rs/tokio/pull/8276

[#&#8203;8277]: https://redirect.github.com/tokio-rs/tokio/pull/8277

###
[`v1.52.4`](https://redirect.github.com/tokio-rs/tokio/releases/tag/tokio-1.52.4):
Tokio v1.52.4

[Compare
Source](https://redirect.github.com/tokio-rs/tokio/compare/tokio-1.52.3...tokio-1.52.4)

### 1.52.4 (July 16th, 2026)

##### Fixed

- runtime: don't skip the driver when `before_park` schedules work
([#&#8203;8222])

##### Fixed (unstable)

- taskdump: remove crate disambiguators from output ([#&#8203;8264])

[#&#8203;8264]: https://redirect.github.com/tokio-rs/tokio/pull/8264

[#&#8203;8222]: https://redirect.github.com/tokio-rs/tokio/pull/8222

</details>

<details>
<summary>toml-rs/toml (toml)</summary>

###
[`v1.1.3`](https://redirect.github.com/toml-rs/toml/compare/toml-v1.1.2...toml-v1.1.3)

[Compare
Source](https://redirect.github.com/toml-rs/toml/compare/toml-v1.1.2...toml-v1.1.3)

</details>

---

### Configuration

📅 **Schedule**: (in timezone Asia/Shanghai)

- Branch creation
  - "before 10am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/oxc-project/oxc).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI2NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
2026-07-19 18:22:30 +01:00
overlookmotel 77230c5c4d feat(ast): accept arrays for ArenaVec params of AST builder methods (#24621)
Builds on #24620.

Make generated and hand-written AST builder methods take `impl IntoIn<'a, ArenaVec<'a, T>>` instead of `ArenaVec<'a, T>` for their `Vec` params, so callers can pass an array literal (e.g. `[]` or `[a, b]`) in place of building an `ArenaVec` explicitly.

This enables shortening code in a lot of places - #24622 and following PRs.
2026-07-17 13:49:38 +00:00
overlookmotel ba65790029 perf(semantic, allocator): branchless clone_in for semantic IDs (#24564)
Follow-on after #24422.

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

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

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

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

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

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

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

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

No visible change on our benchmarks, as we don't use `CloneIn` much, but it may well be a measurable improvement for Rolldown, which clones whole ASTs.
2026-07-16 04:14:55 +00:00
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
Boshen d05224dbcc feat(ast_visit): generate VisitJs visitor that skips TypeScript type-space (#24499)
## What

Generates a new **`VisitJs<'a>`** trait (+ `walk_js` module) in `oxc_ast_visit` that traverses **only the JavaScript parts** of the AST. It skips pure TypeScript type-space nodes (`TSType`, `TSTypeAnnotation`, interfaces, type aliases, …) entirely, but still descends into the JavaScript nested inside a small set of TS wrapper nodes: the five expression casts (`x as T`, `x satisfies T`, `<T>x`, `x!`, `f<T>`), decorators, enum member initializers, namespace/module bodies, `export = expr`, and `import x = require(..)` value aliases.

`VisitJs` is for visiting the JavaScript parts of a **TypeScript** AST — it is not a visitor for ASTs where TypeScript has already been transformed out. The TS constructs carrying runtime JS must therefore still be walked, so their walk code remains in the binary; only the pure type grammar is pruned.

## Why (part of #24317)

The generated `walk_*` functions are generic over the visitor (`fn walk_foo<'a, V: Visit<'a>>(…)`), so every `impl Visit` that is actually traversed monomorphizes the full transitive closure of walks it reaches — **including all the pure-type `walk_ts_*` functions**. Those TS-grammar walks are ~282 KiB (≈23%) of oxlint's visitor machinery, and `oxc_linter` alone has 44 helper visitors that only care about runtime JavaScript.

Because the generated `walk_js_*` functions never reference the pure-type walks, an `impl VisitJs` monomorphizes **zero** TS type-grammar traversal. A visitor that only inspects JS can switch `impl Visit` → `impl VisitJs` and the TS walks drop out of its binary contribution.
2026-07-14 16:01:16 +00:00
Boshen f297654a68 refactor(allocator, ast_tools): generate a single CloneIn traversal via a runtime flag (#24422)
## Summary

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

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

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

## Results

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

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

The two binaries move differently because they clone differently:

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

## Behavior & perf

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

---
🤖 AI-assisted (Claude Code), reviewed and measured by the author.
2026-07-14 15:49:54 +00:00
leaysgur b7b82e0a33 fix(formatter): suppressed trailing semi (#24482)
Fixes suppressed statements (`oxfmt-ignore`) emitting their trailing `;` verbatim, which broke idempotency with `semi: false`.

A suppressed statement's span includes its (possibly distant) trailing `;`, so it was printed verbatim. And with `semi: false`, the formatter also prepends an ASI-guard `;` to the next statement, duplicating it (the doubled `;` = extra `EmptyStatement`):

```js
// oxfmt-ignore
while   (   1)   foo (   )

;[].sort()

// BEFORE

// oxfmt-ignore
while   (   1)   foo (   )

;
;[].sort()

// AFTER
// oxfmt-ignore
while   (   1)   foo (   )

;[].sort()
```
2026-07-14 06:28:49 +00:00
leaysgur 25306e9f21 fix(formatter): do not add extra parens with type cast comment (#24444)
Fixes #20182
2026-07-13 07:44:14 +00:00
overlookmotel 533a4ec049 refactor(ast_tools): clarify inlining in CloneIn generator (#24396)
Tiny refactor to `CloneIn` generator. Clarify the logic about which `clone_in` methods get an `#[inline(always)]` attribute.
2026-07-12 10:25:11 +00:00
overlookmotel c15c423487 docs(ast_tools): correct comments concerning enum inheritance (#24395)
Follow-on after #23555.

Since that PR was written, the `inherit_variants!` macro was removed, so some of the comments were out of date.

Bring those comments up to date. Also shorten the comments a bit and reformat them.
2026-07-12 10:25:10 +00:00
Boshen f85f0d88b8 perf(ast): delegate inherited enum variants in clone_in and estree derives (#23555)
## What

Enums built with the `inherit_variants!` macro (`Argument`,
`PropertyKey`, `ForStatementInit`, `Statement`, `AssignmentTarget`, ...)
inline a parent enum's variants (e.g. all ~30 of `Expression`'s) with
**matching discriminants**. The `clone_in` and `estree` derives
previously re-emitted a `match` arm for every inherited variant, so the
parent enum's entire dispatch was duplicated into each inheriting enum,
in each operation.

This delegates inherited variants to the parent enum's own impl instead
of expanding them:

- read-only (`estree`): `... =>
self.to_expression().serialize(serializer)`
- producing (`clone_in`): `... =>
Self::from(CloneIn::clone_in(self.to_expression(), allocator))`

using the `to_<parent>()` reference cast and the `From<Parent>` impl
that `inherit_variants!` **already generates**. Both are zero-cost
transmutes, sound because the inherited variants share discriminants and
layout with the parent enum.

Only the generators in
`tasks/ast_tools/src/derives/{clone_in,estree}.rs` are hand-edited; the
rest is regenerated.

## Why

These inheritance enums account for a large share of `oxc_ast`'s
generated code. The duplication is the dominant size cost in the AST
derives (most visible in downstream consumers like rolldown, where
`ESTree` serialize is also monomorphized per serializer config).
Delegation is behaviour-identical and removes ~1,250 net lines of
generated code.

## Verification

- ESTree output is **byte-identical**: `cargo coverage estree` unchanged
(same pass rate, same pre-existing mismatches, no snapshot diff).
- `clone_in`: `oxc_transformer` / `oxc_semantic` / `oxc_minifier` test
suites pass; `cargo llvm-lines -p oxc_ast` shows -17% `CloneIn` IR.
2026-07-12 11:14:10 +01:00
overlookmotel 19ad4a8c17 refactor(ast): move AstKind::ty out of generated code (#24390)
Follow-on after #24381. `AstKind::ty` is pure static code. It doesn't need to be generated. Move it out of the generated code.
2026-07-11 18:03:25 +00:00
overlookmotel 347c61b410 refactor(ast): use NonNull for ref casting (#24389)
Follow-on after #24381. When casting a `&` reference, it's preferable to use `NonNull` - that safely gains the "pointer is not null" fact, rather than unsafely promising it with `as_ref_unchecked` on a raw pointer.
2026-07-11 18:03:25 +00:00
Boshen a82a400c15 refactor(rust): use stabilized ptr::as_ref_unchecked (#24381)
Rust 1.95 stabilized [`<*const T>::as_ref_unchecked`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref_unchecked) (MSRV bumped in #24359). This replaces the `ptr.as_ref().unwrap_unchecked()` idiom at its 5 call sites:

- `oxc_parser` `lexer/source.rs` `read()`/`read2()` — the doc comment there explains the idiom exists because producing a `&u8` carries `nonnull`/`dereferenceable` LLVM metadata that a raw deref does not (7% lexer speedup). `as_ref_unchecked` is implemented as exactly that reference production (`&*self`), so codegen is unchanged — it just skips the `Option` detour. Comment updated to name the new method.
- `oxc_codegen` `lib.rs` — `</script` search loop.
- `oxc_ast_visit` `utf8_to_utf16/translation.rs` — chunk read.
- `oxc_ast` `generated/ast_kind.rs` `AstKind::ty` — changed via its generator in `tasks/ast_tools`, regenerated with `just ast`.

Verified: `cargo test` for the touched crates, and parser/semantic/codegen conformance (test262 / babel / typescript / misc) with **zero snapshot diffs**. CodSpeed should show this perf-neutral.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-11 17:45:52 +00:00
camc314 66e1a25c0e chore(all): fix clippy::manual_assert_eq (#24351) 2026-07-10 09:57:05 +00:00
renovate[bot] 0e00b91b5a chore(deps): update dependency rust to v1.97.0 (#24328)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [rust](https://redirect.github.com/rust-lang/rust) | minor | `1.96.1`
→ `1.97.0` |

---

### Release Notes

<details>
<summary>rust-lang/rust (rust)</summary>

###
[`v1.97.0`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1970-2026-07-09)

[Compare
Source](https://redirect.github.com/rust-lang/rust/compare/1.96.1...1.97.0)

\==========================

<a id="1.97.0-Language"></a>

## Language

- [Consider `Result<T, Uninhabited>` and `ControlFlow<Uninhabited, T>`
to be equivalent to `T` for must use
lint](https://redirect.github.com/rust-lang/rust/pull/148214)
- [Add allow-by-default `dead_code_pub_in_binary` lint for unused pub
items in binary
crates](https://redirect.github.com/rust-lang/rust/pull/149509)
- [Stabilize the `div32`, `lam-bh`, `lamcas`, `ld-seq-sa` and `scq`
target features](https://redirect.github.com/rust-lang/rust/pull/154510)
- [Stabilize
`cfg(target_has_atomic_primitive_alignment)`](https://redirect.github.com/rust-lang/rust/pull/155006)
- [Allow trailing `self` in imports in more
cases](https://redirect.github.com/rust-lang/rust/pull/155137)

<a id="1.97.0-Platform-Support"></a>

## Platform Support

- [nvptx64-nvidia-cuda: drop support for old architectures and old
ISAs](https://redirect.github.com/rust-lang/rust/pull/152443)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

[platform-support-doc]:
https://doc.rust-lang.org/rustc/platform-support.html

<a id="1.97.0-Stabilized-APIs"></a>

## Stabilized APIs

- [`Default for
RepeatN`](https://doc.rust-lang.org/stable/std/iter/struct.RepeatN.html#impl-Default-for-RepeatN%3CA%3E)
- [`Copy for
ffi::FromBytesUntilNulError`](https://doc.rust-lang.org/stable/std/ffi/struct.FromBytesUntilNulError.html#impl-Copy-for-FromBytesUntilNulError)
- [`Send for std::fs::File` on
UEFI](https://redirect.github.com/rust-lang/rust/pull/154003)
-
[`<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_highest_one)
-
[`<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_lowest_one)
-
[`<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.highest_one)
-
[`<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.lowest_one)
-
[`<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.bit_width)
-
[`NonZero<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_highest_one)
-
[`NonZero<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_lowest_one)
-
[`NonZero<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.highest_one)
-
[`NonZero<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.lowest_one)
-
[`NonZero<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.bit_width)

These previously stable APIs are now stable in const contexts:

-
[`char::is_control`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_control)

<a id="1.97.0-Cargo"></a>

## Cargo

- [Stabilize `build.warnings`
config.](https://redirect.github.com/rust-lang/cargo/pull/16796) This
controls how lint warnings from local packages are treated. Useful for
enforcing a warning-free build in CI, replacing `-Dwarnings`.
[docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildwarnings)
- [Stabilize `resolver.lockfile-path`
config.](https://redirect.github.com/rust-lang/cargo/pull/16694) This
allows specifying the path to the lockfile to use when resolving
dependencies. Useful when working with read-only source directories.
[docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#resolverlockfile-path)
- [cargo-clean: Error when `--target-dir` doesn't look like a Cargo
target
directory.](https://redirect.github.com/rust-lang/cargo/pull/16712) This
prevents accidental deletion of non-target directories.
- [Add `-m` shorthand for
`--manifest-path`](https://redirect.github.com/rust-lang/cargo/pull/16858)
- [Remove `curl` dependency from `crates-io`
crate](https://redirect.github.com/rust-lang/cargo/pull/16936)

<a id="1.97.0-Rustdoc"></a>

## Rustdoc

- [Stabilize `--emit`
flag](https://redirect.github.com/rust-lang/rust/pull/146220)
- [Stabilize
`--remap-path-prefix`](https://redirect.github.com/rust-lang/rust/pull/155307)

<a id="1.97.0-Compatibility-Notes"></a>

## Compatibility Notes

- [Emit a future-compatibility warning when relying on `f32:
From<{float}>` to constrain
`{float}`](https://redirect.github.com/rust-lang/rust/pull/139087)
- [Rust will use the v0 symbol mangling scheme by
default.](https://redirect.github.com/rust-lang/rust/pull/151994) This
may cause some tools (such as debuggers or profilers, especially with
old versions) to fail to demangle symbols emitted by Rust. It may also
cause the formatting of text in backtraces to change.
- [Prevent deref coercions in `pin!`, in order to prevent
unsoundness.](https://redirect.github.com/rust-lang/rust/pull/153457)
The most likely case where this might impact users is: writing `pin!(x)`
where `x` has type `&mut T` will now always correctly produce a value of
type `Pin<&mut &mut T>`, instead of sometimes allowing a coercion that
produces a value of type `Pin<&mut T>`. This coercion was previously
incorrectly allowed since Rust 1.88.0.
- [Deprecate `std::char` constants and
functions](https://redirect.github.com/rust-lang/rust/pull/153873)
- [Warn on linker output by
default](https://redirect.github.com/rust-lang/rust/pull/153968)
- [Remove hidden `f64` methods which have been deprecated since
1.0](https://redirect.github.com/rust-lang/rust/pull/153975)
- [report the `varargs_without_pattern` lint in
deps](https://redirect.github.com/rust-lang/rust/pull/154599)
- [Forbid passing generic arguments to module path segments even if the
module reexports a generic enum
variant](https://redirect.github.com/rust-lang/rust/pull/154971)
- [Error on invalid macho `link_section`
specifier](https://redirect.github.com/rust-lang/rust/pull/155065)
- The encoding of certain `enum`s [have
changed](https://redirect.github.com/rust-lang/rust/pull/155473). This
is not a breaking change, as it only applies to `enum`s without layout
guarantees, but is noted here as we've seen people impacted from having
made assumptions about the layout algorithm.
- [Error on `#[export_name = "..."]` where the name is
empty](https://redirect.github.com/rust-lang/rust/pull/155515)
- [Syntactically reject tuple index shorthands in struct
patterns](https://redirect.github.com/rust-lang/rust/pull/155698)
- [validate `#[link_name = "..."]` & `#[link(name = "...")]`
parameters](https://redirect.github.com/rust-lang/rust/pull/155817)
- On Windows, after calling `shutdown` on a socket to shut down the
write side, attempting to write to the socket will now produce a
`BrokenPipe` error rather than `Other`. [Map `WSAESHUTDOWN` to
`io::ErrorKind::BrokenPipe`](https://redirect.github.com/rust-lang/rust/pull/156063)

</details>

---

### Configuration

📅 **Schedule**: (in timezone Asia/Shanghai)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/oxc-project/oxc).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-07-09 18:34:23 +01:00
leaysgur 42ec8de556 fix(formatter): keep comments inside surviving parens and suppressed statement terminators (#24253)
Fix regression found in this series of PRs.
2026-07-07 09:09:55 +00:00
leaysgur 3abbed59d2 fix(formatter): print ; before jsdoc type-cast parens with no-semi (#24208)
Follows https://github.com/prettier/prettier/blob/79f6cdfd9873a91be9b25c9c6a41d26dcd9a6656/changelog_unreleased/javascript/18751.md

```js
;/** @type {string[]} */ (["foo", "bar"]).forEach(doStuff)
```

With `no-semi`, this `;` should stay the same position.

Also fixes minor bugs and idempotency issues.
2026-07-06 06:01:18 +00:00
overlookmotel 7db7a29aab feat(allocator): add ReplaceWith trait (#24012)
## The problem

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

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

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

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

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

## Solution in this PR

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

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

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

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

## Implementation

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

A naive implementation would allow UB as follows:

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

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

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

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

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

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

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

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

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

However, in practice this is fine, because:

#### 1. It shouldn't happen

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

#### 2. Panic usually exits process

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

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

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

With `panic = "abort"`, panics are converted to process abort. In that case, the compiler sees that the guard can never fire. It removes the guard and all the code which creates `Allocator`s and allocates dummies into them. So all these mechanics are completely zero cost, and no leak ever occurs.
2026-07-05 14:44:06 +00:00
camc314 3a7a606544 fix(ast_tools): suggest pnpm install for missing oxfmt (#24089)
When agents run `just ast`, and `oxfmt` is not installed, the agent often tries to build oxfmt from source instead of running `pnpm i`, this PR update the error msg to give the agent an actionable solution to the problem (run `pnpm i`)
2026-07-02 19:38:05 +00:00