Commit Graph
21853 Commits
Author SHA1 Message Date
overlookmotel e9bdc066ab refactor(lexer): replace super::* imports (#28784)
Pure style change.

Remove `use super::*;` statements from `disambiguate` and explicitly import all types and functions from where they come from. This makes it easier to trace dependencies.
2026-11-03 23:57:43 +00:00
overlookmotel 7b2eb1a339 refactor(lexer): convert pair! macro to a function (#28786)
Pure refactor.

Remove `pair!` macro in `compress` and replace it with a function. This produces identical codegen, but is IMO a bit clearer to read because you get type annotations in IDE.

Also reorder a few lines and add line breaks to make the code clearer.
2026-11-03 23:51:35 +00:00
overlookmotel 4c47b4d330 refactor(lexer): remove defunct #![allow] attrs from lanes.rs (#28776)
Pure refactor.

`lanes` is a submodule of `pipeline`, and `pipeline/mod.rs` has the same `#![allow]` attributes, which apply to all its submodules. So the ones in `lanes.rs` have no effect. Remove them.
2026-11-03 18:44:46 +00:00
overlookmotel b555e3703a perf(lexer): reduce allocations in macros (#28777)
Tiny optimization to macros.

`Cpu::from_ident` finds the CPU feature set named by an identifier (e.g. the `Avx2` in `#[cpu_version(Avx2)]`). It compared the `Ident` with each feature set's name in turn.

Inside a proc macro, comparing a `proc_macro2::Ident` with a `&str` converts the `Ident` to a `String` every time. So that was up to 3 allocations. Convert it to a `String` once, and compare that instead.

This only affects how fast the macros run, not their output.
2026-11-03 18:02:36 +00:00
overlookmotel 0a710083ae fix(lexer): prevent dead code warnings when compiling crate for WASM (#28775)
Fix `dead_code` warnings when compiling `oxc_lexer` for WASM, which were introduced by #28772 and earlier PRs.
2026-11-03 18:02:36 +00:00
overlookmotel f8fa6b2a03 refactor(lexer): order variants of DynCpu per architecture (#28774)
Define `DynCpu` separately for each architecture, so its variants can be in a different order on each. `Generic` is always first, then the SIMD feature set which the platform has, then the variants which can't exist on the platform:

- x86_64: `Generic`, `Avx2`, `Neon`.
- aarch64: `Generic`, `Neon`, `Avx2`.
- Other platforms: `Generic`, `Avx2`, `Neon`.

`Generic` is always 0, and the platform's SIMD feature set is 1. Uninhabited variants don't exist at runtime, so `DynCpu` is like a `bool`. On aarch64 with Neon enabled at compile time, and on other platforms, only one variant can exist, so `DynCpu` is zero-sized.

Each variant which can't exist on the platform is `#[expect(dead_code)]`, with no `cfg_attr` needed.

This alters codegen slightly in a few places, but those changes are entirely incidental - same instructions but in a different order, and only in code which only runs once per lex.
2026-11-03 18:02:35 +00:00
overlookmotel 84a5cd9906 refactor(lexer): remove cpu_select! macro (#28773)
Pure refactor - no changes to codegen.

Now that `DynCpu` has all its variants on every platform (#28769), and all arms are compiled everywhere (#28772), `cpu_select!` macro does nothing which a plain `match` doesn't.

Replace it with `match to_dyn(cpu) { ... }` everywhere, and remove the macro.

A `match` is just Rust, so there's no macro syntax to learn, and rustfmt and Rust Analyzer handle it fully. Arms which do the same thing can also be combined with `_`, which `cpu_select!` didn't allow:

```rust
// Before
cpu_select!(cpu, {
    Avx2(cpu) => bm_any_avx2(bm, nw, cpu),
    Neon(_) => bm_any_generic(bm, nw),
    Generic(_) => bm_any_generic(bm, nw),
})

// After
match to_dyn(cpu) {
    DynCpu::Avx2(cpu) => bm_any_avx2(bm, nw, cpu),
    _ => bm_any_generic(bm, nw),
}
```

This uses the `to_dyn` function, not the `Cpu::to_dyn` method. The method is an extra `#[inline(always)]` function around `to_dyn`, which rustc's MIR inliner can't inline into code which is generic over the CPU type. That changes the codegen of some callers. Those codegen changes may be incidental and have no perf impact, but impossible to know for sure without benchmarking, so using `to_dyn` is the conservative choice to avoid any accidental perf regressions.
2026-11-03 18:02:35 +00:00
overlookmotel 187b995274 feat(lexer): compile all code on all platforms (#28772)
Remove all `#[cfg(target_arch = ...)]` attributes from the lexer, except in the `cpu` and `simd` modules.

The code for every CPU feature set (AVX2, Neon and generic) is now compiled, type-checked and linted on every platform, including the tests. Rust Analyzer sees all of the code for all platforms "as one". So e.g. AVX2 code can be worked on with full IDE support on an ARM Mac, and `cargo check` / `cargo clippy` on either kind of machine catches errors in the code for the other.

Code for a feature set which the platform doesn't have still can't run, because that feature set's type is uninhabited there (see #28769). The compiler removes it.

The macros change to support this:

- `#[cpu_version]` no longer adds `#[cfg(target_arch = ...)]`. Its `#[target_feature(...)]` becomes `#[cfg_attr(target_arch = ..., target_feature(...))]`, because enabling another platform's target features is an error.
- `#[cpu_multiversion]` generates all 3 entry points (`__avx2`, `__neon` and `__generic`) on every platform, with their target features in a `cfg_attr` in the same way.
- `cpu_select!` compiles all 3 arms on every platform, instead of replacing the bodies of other platforms' arms with `unreachable!()`.

On a platform where a feature set's type is uninhabited, its `#[cpu_multiversion]` entry point starts with `unreachable!()`, before it calls the function body:

```rust
#[inline(never)]
#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))]
#[allow(unreachable_code)]
unsafe fn __neon(src: &[u8], n: usize, cpu: Neon) -> usize {
    #[cfg(not(target_arch = "aarch64"))]
    unreachable!();
    __impl(src, n, cpu)
}
```

Without it, the copies of every function for that feature set (e.g. `__impl::<Neon>` on x86_64) would be part of the call graph, even though they can never run. That changes how rustc's MIR inliner treats other code, which changes the codegen of functions which only have a generic version. rustc removes code after `unreachable!()` early enough that this doesn't happen.

The call to `__impl` stays, so the entry point still uses its parameters. Without it, clippy reports lints on their types which don't apply, e.g. `ptr_arg` warning on a `&mut Vec` parameter.

With these workarounds, this PR makes no difference to generated ASM - it's a pure refactor.
2026-11-03 18:02:34 +00:00
overlookmotel b8ea9641c8 refactor(lexer): move prefetch function into simd module (#28771)
Pure refactor - no change to codegen.

Preparatory step for compiling all of the lexer's code on all platforms (#28772).

`prefetch` was in `pipeline/coalesce`, and was the only code outside `simd` which used `std::arch::x86_64` intrinsics directly. Move it into `simd/prefetch.rs`, as a public free function.
2026-11-03 18:02:34 +00:00
overlookmotel e920957d12 refactor(lexer): make Vec128 and Vec256 exist for all CPU types on all platforms (#28770)
Pure refactor - no change to codegen.

Preparatory step for compiling all of the lexer's code on all platforms (#28772).

Previously, the `simd/avx2` module was only compiled on x86_64, and `simd/neon` only on aarch64. So `Vec128<Avx2>` and `Vec256<Avx2>` only existed on x86_64, and `Vec128<Neon>` and `Vec256<Neon>` only on aarch64. Other platforms had no vector types at all.

Compile both modules on all platforms. On other platforms, a vector wraps a `VecData128` / `VecData256` instead of the platform's SIMD type, which has the same size and alignment:

```rust
type Vec128Inner = cfg_select! {
    target_arch = "x86_64" => __m128i,
    _ => VecData128,
};
```

There, the CPU type is uninhabited, so the vector can never be created.

Add a `#[cpu_impl]` attribute macro, for each `impl` block of a CPU type's vector methods. It wraps each method's body in `cfg_select!`, so the body is only compiled on that CPU's platform, and is `unreachable!()` elsewhere. Signatures don't change, so the methods exist on every platform, and code which calls them compiles everywhere.

```rust
#[cpu_impl(Avx2)]
impl Vec256<Avx2> {
    pub fn movemask(self) -> u32 {
        // SAFETY: `Vec256` type guarantees platform supports AVX2
        unsafe { _mm256_movemask_epi8(self.inner).cast_unsigned() }
    }
}
```

expands to:

```rust
#[cfg_attr(
    not(target_arch = "x86_64"),
    allow(dead_code, unused_variables, clippy::unused_self, clippy::needless_pass_by_value)
)]
impl Vec256<Avx2> {
    pub fn movemask(self) -> u32 {
        cfg_select! {
            target_arch = "x86_64" => {
                // SAFETY: `Vec256<Avx2>` type guarantees platform supports AVX2
                unsafe { _mm256_movemask_epi8(self.inner).cast_unsigned() }
            }
            _ => unreachable!(),
        }
    }
}
```

The `simd` tests are compiled on all platforms too.
2026-11-03 18:02:33 +00:00
overlookmotel 6d5be86ea1 refactor(lexer): make CPU types and all DynCpu variants exist on all platforms (#28769)
Pure refactor - no change to codegen.

Preparatory step for compiling all of the lexer's code on all platforms (#28772).

Previously, `Avx2` only existed on x86_64, and `Neon` only on aarch64, and the variants of `DynCpu` had `#[cfg]` attributes to match. `Generic` existed on all platforms, but was uninhabited on aarch64 with Neon enabled.

Make `Avx2` and `Neon` work the same way as `Generic`. All these types now exists on every platform. Each has a private field which is `()` on its own platform, and `Never` (an enum with no variants) everywhere else:

```rust
pub struct Avx2(
    cfg_select! {
        target_arch = "x86_64" => (),
        _ => Never,
    },
);
```

On platforms which can't support the CPU feature set, the type is uninhabited, so it can never be created, and the compiler removes code which handles it.

`DynCpu` has all 3 variants on every platform. Variants which can't exist on a platform are `#[expect(dead_code)]` there.

The field is private, so only code in the `cpu` module knows that a type is uninhabited. A `match` anywhere else needs an arm for every variant, including ones which can't exist. So `cpu_select!` and `#[cpu_multiversion]` add arms for them, e.g. `DynCpu::Neon(_) => unreachable!()` on x86_64.
2026-11-03 18:02:33 +00:00
overlookmotel b03fadad8d refactor(lexer): add all common methods to Vec128Impl and Vec256Impl traits (#28768)
Pure refactor - no change to codegen.

Preparatory step for compiling all of the lexer's code on all platforms (#28772).

Previously, the methods of `Vec128` and `Vec256` were inherent methods, defined separately for each CPU type in the `avx2` and `neon` modules. Code which is generic over the CPU type couldn't call them.

Move every method which has both AVX2 and Neon versions into the `Vec128Impl` and `Vec256Impl` traits (introduced in #28766) - 31 methods for `Vec128`, and 36 for `Vec256`. Each CPU type implements the traits in its own module.

`Vec128<C>` and `Vec256<C>` also have generic inherent methods with the same names, which call the trait methods. So callers don't need the traits in scope, and call sites don't change.

The structs, the traits and the generic methods are in new `simd/vec128.rs` and `simd/vec256.rs` files.

The `!`, `&`, `|` and `^` operators, and the `Operand128` / `Operand256` traits, are generic over the CPU type too. The operators move from `bit_traits.rs` into `vec128.rs` and `vec256.rs`. The `CpuType` alias, which picked the platform's CPU type with `#[cfg]`, is removed.

Methods which only one CPU has stay inherent methods of that CPU's types:

- AVX2 only: `movemask` and `Vec256::shuffle_u32s`.
- Neon only: `shl_u8`, `shr_u8`, `pairwise_add_u8` and `test_u8`.

`Vec256::shuffle_u8s` stays separate for each CPU too, because it does different things on each: 2 lookups in 16-entry tables on AVX2, 1 lookup in a 32-entry table on Neon.

Neon's `Vec256` gets the methods it didn't have yet, which work on its 2 `Vec128` halves.

Tests of the common methods move from `avx2/tests.rs` and `neon/tests.rs` into `simd/tests.rs`, and run with the CPU type which the platform has. `avx2/tests.rs` and `neon/tests.rs` keep the tests for methods which only one CPU has, or which behave differently on each.
2026-11-03 18:02:32 +00:00
overlookmotel f0d84e7514 docs(lexer): add module doc comments to all files in simd directory (#28767)
Comments-only change.

Add a top-level doc comment to each file in the `simd` directory which didn't have one.
2026-11-03 18:02:32 +00:00
overlookmotel d6c045cc80 feat(lexer): introduce aligned VecData types (#28766)
Introduce `VecData128` and `VecData256` types, in `simd/data.rs`.

They're byte arrays aligned on 16 and 32 bytes, for lookup tables and other data which is read into SIMD vectors.

- `Vec128::read` / `Vec256::read` read one into a vector, with an aligned load.
- `Vec128::write` / `Vec256::write` write a vector into one, with an aligned store.
- `VecData128::read(cpu)` / `VecData256::read(cpu)` are the same as `Vec128::read` / `Vec256::read`, but read better for tables, e.g. `PH_A.read(cpu)`.

Convert the lookup tables which SIMD code reads into vectors: `PH_A`, `PH_B`, `PH_T`, `MERGED_LUTS`, `WORD_LUTS`, `BITS` (Neon), `QCOMPACT` and `BCOMPACT`.

The aligned read and store operations have minimal advantage over their unaligned versions. The realbenefit is ensuring that loads/stores do not touch multiple cache lines, which has a perf cost.

The change to aligned types has a positive impact for `QCOMPACT` and `BCOMPACT`, which were previously under-aligned, so reads could cross cache lines. Now that they're made up of aligned `VecData256` / `VecData128` blocks, every load is guaranteed to be within a single cache line.

`VecData128::read` and `VecData256::read` are generic over the CPU type. To support that, `CpuSimd` gets `Vec128` and `Vec256` associated types, and there are new `Vec128Impl` and `Vec256Impl` traits, which only contain `read` for now.

Both types deref to a byte array, so they can be indexed like an array, except in `const` contexts. `as_bytes_mut` and `as_u32s_mut` are for building tables in a `const` or `static`.
2026-11-03 18:02:31 +00:00
Mikhail Baev ae43c804e4 fix(linter/unicorn/prefer-single-call): remove duplicate fix status (#28763)
Noticed that `unicorn/prefer-single-call` rule declared both `pending`
and `fix` as its fix status, this PR removes `pending` and also changes
to `fix` to `conditional_fix` since the fix is skipped in 2 cases.

Also small change to `declare_oxc_lint` so that it rejects a second fix
capability
2026-11-03 15:04:12 +00:00
renovate[bot] 62a8e7818e chore(deps): update dependency oxlint to v1.91.0 (#28760)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [oxlint](https://oxc.rs/docs/guide/usage/linter)
([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxlint))
| [`1.90.0` →
`1.91.0`](https://renovatebot.com/diffs/npm/oxlint/1.90.0/1.91.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/1.91.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/1.91.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/1.90.0/1.91.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/1.90.0/1.91.0?slim=true)
|

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

###
[`v1.91.0`](https://redirect.github.com/oxc-project/oxc/blob/HEAD/npm/oxlint/CHANGELOG.md#1910---2026-11-02)

[Compare
Source](https://redirect.github.com/oxc-project/oxc/compare/oxlint_v1.90.0...oxlint_v1.91.0)

##### 🚀 Features

-
[`419c1b0`](https://redirect.github.com/oxc-project/oxc/commit/419c1b0)
linter: Add allow and allowPattern options to react/capitalized-calls
(Christian Vuerings)

</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:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDQuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjIwNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-11-03 14:22:48 +00:00
camc314 b904d51667 fix(react-compiler): avoid panic on non-ASCII ref-like names (#28758)
Reading `object_name[..1]` panics when a ref-like identifier starts with a multibyte character, such as `한글Ref`. Use `.chars().next()` to preserve the ASCII name check without slicing through a UTF-8 character. Ports the fix and regression fixture from https://github.com/facebook/react/pull/38025.

Fixes #28278.
2026-11-03 14:13:47 +00:00
camc314 a8a621a46d fix(linter/eslint/no-fallthrough): ignore unreachable exits from infinite loops (#28755)
Cases ending in `while (true)`, `do ... while (true)`, or `for (;;)` cannot fall through unless a reachable break exits the loop. Oxlint reports these cases even though ESLint accepts them.

Traverse CFG edges within each switch and skip the normal exit from statically true loop conditions. Preserve reachable break paths and unused fallthrough comment diagnostics, with regression coverage for async polling, nested breaks, parenthesized conditions, and unreachable breaks.

Linter tests and lint pass. The broader Oxlint suite fails in type-aware suppression tests: one produces different suppression counts, and another aborts because its expected suppression file is missing.

Fixes #28579
2026-11-03 13:36:19 +00:00
camc314 7a17a80d9e fix(linter/eslint/prefer-spread): report calls on non-member expressions (#28753)
`prefer-spread` now reports `(c ? a : b).apply(null, args)` and `m!.apply(null, args)`, matching ESLint. Check whether the applied expression is a member expression rather than requiring an identifier, while retaining receiver checks for method calls.

Fixes #28728
2026-11-03 12:10:45 +00:00
camc314 28442f7246 fix(oxlint): prevent --init from overwriting existing configs (#28754)
`oxlint --init` now exits with an error if any supported config filename already exists in the current directory, preserving existing configs and avoiding conflicting ones. Exclusive file creation also prevents overwrites if `.oxlintrc.json` appears after the check.

Fixes #28741
2026-11-03 11:55:31 +00:00
camc314 0ae350d7d8 fix(linter/unicorn/no-zero-fractions): preserve exponent case (#28752)
Trim trailing zeros from the fractional part while preserving the exponent suffix verbatim. This avoids false positives on literals such as `4.6566128731E+100` and fixes `1.0E+10` to `1E+10`, matching Unicorn 62.

Fixes #28743
2026-11-03 11:49:00 +00:00
renovate[bot] 4d8d482157 chore(deps): update dependency oxfmt to ^0.76.0 (#28750)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [oxfmt](https://oxc.rs/docs/guide/usage/formatter)
([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt))
| [`^0.75.0` →
`^0.76.0`](https://renovatebot.com/diffs/npm/oxfmt/0.75.0/0.76.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/oxfmt/0.76.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxfmt/0.76.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxfmt/0.75.0/0.76.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxfmt/0.75.0/0.76.0?slim=true)
|

---

### 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDIuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjIwMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-11-03 11:18:44 +00:00
Dominic ElmandCameron 3dbf1409ab fix(parser): treat escaped contextual keywords as names, not modifiers (#28748)
Treat contextual keywords spelled with a Unicode escape (`st\u0061tic`,
`\u0061sync`, `g\u0065t`, …) as property names rather than modifiers.
Such a token is an IdentifierName and cannot match a keyword terminal,
so `class A { st\u0061tic
x }` declares two fields named `static` and `x`. Previously it was
parsed as `static x` and reported "Keywords cannot contain escape
characters".

`try_parse_modifier` and `parse_contextual_modifier` now skip escaped
tokens. Invalid forms such as `class X { st\u0061tic y() {} }` are still
rejected. The error is now reported at the token after the name, as
Babel does, which accounts for the updated parser snapshots.

The new `misc/pass` fixture covers fields named `static`, `async`,
`get`, `set`, and `accessor` that end at a line break. The rejected
forms are already covered by Babel and test262.

Validated with scoped Rust checks and full parser conformance.

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-11-03 11:14:17 +00:00
Sysix b5b3f3fc88 fix(linter/eslint/no-misleading-character-class): detect more combining characters (#28733)
fixes https://github.com/oxc-project/oxc/issues/28729

Generated by Claude Sonnet 5.5 🤖

The upstream rules uses regex to detect if it is a combining character with the `\p` shorthand:
https://github.com/eslint/eslint/blob/main/lib/rules/utils/unicode/is-combining-character.js#L11

Because there is no similar utility in rust, we currently have a self-managed list of character detection.
This list now get extended to detect more characters for other languages like Arabic, Hebrew, Thai ...

Verified manually that the failing test cases also fails upstream.
Did not verfify if the list is really valid and covers all possible combining characters.
I hope that the AI did no mistakes and included characters which are still valid.
2026-11-03 09:47:30 +00:00
Dunqing 74d50c1f32 perf(linter): overlap import graph batches instead of polling on a pool worker (#28323)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
With `--import-plugin`, `main` builds the module graph in batches of `threads × 4` entry files and waits for each batch and its dependency closure to finish before starting the next. The waiter is a pool worker that polls with `try_recv` + `yield_now`. In the profiles, the other workers spend much of the run spinning in rayon's work-stealing loop, the spinning grows toward the end of the run, and the run ends with a long tail where only a few cores are busy.

The graph loop now runs on the calling thread (`rayon::in_place_scope`) and blocks on `recv`. A new batch can start while earlier ones are still open. When one open batch imports from another, union-find merges them and they finish together, so at most `3 × group_size − 1` entry files (plus their dependencies) are in flight. Without the import plugin, the loop keeps `rayon::scope` and is unchanged; scope selection and module resolution share one resolver predicate. The language server (`run_source`) and the rule test harness (`run_test_source`) choose the scope the same way, so with the import plugin they also run the graph loop on the calling thread.

The speedup comes mostly from less total CPU work and a shorter tail, not from higher core utilization. samply profiles (4 threads, `-A all --import-plugin`, median of 5 runs):

| Corpus | Wall | CPU | Spent in `steal` / `find_work` / `try_recv` |
|---|---|---|---|
| next.js | 799 → 482 ms | 1,728 → 1,272 ms | 25.3% → 0.1% of samples (438 → 2 ms) |
| VS Code | 1,479 → 1,183 ms | 4,240 → 3,887 ms | 9.6% → 1.3% (402 → 51 ms) |

On `main`, one worker is on-CPU for almost the whole run (next.js: 3,098 samples vs 899–1,493 on the other three workers). With this PR the four workers are even (1,124–1,405), and the top of the inverted call tree is lexer, allocator, and semantic code instead of crossbeam's deque.

<img width="1290" height="358" alt="next.js: main vs this PR" src="https://github.com/user-attachments/assets/ffd00f15-4ad9-46e1-bd60-ca23c0efa696" />
<img width="1290" height="358" alt="VS Code: main vs this PR" src="https://github.com/user-attachments/assets/b917dbee-21ce-4262-b374-93af0df0beb7" />

Thread timelines from Firefox Profiler, `main` above and this PR below, same time scale.

## Results

`-c empty.json --disable-nested-config --silent -A all --import-plugin`. VS Code `src` at `1306698e` (11,260 files) and next.js `packages/` at `3bccd4df` (3,404 files). Time is median (min … max). Peak RSS is the median of `wait4` max RSS. Δ time is the median of paired per-round differences.

| Machine | Threads | Corpus | main | this PR | Δ time | Δ peak RSS |
|---|---|---|---|---|---:|---:|
| Linux x86-64, 8 vCPU | 4 | VS Code | 1,673 ms (1,566 … 2,282) | 1,403 ms (1,231 … 1,754) | −19.7% | +3.8% |
| | 4 | next.js | 744 ms (649 … 949) | 444 ms (396 … 904) | −40.4% | +16.7% |
| | 8 | VS Code | 960 ms (832 … 1,256) | 771 ms (661 … 900) | −18.5% | +5.6% |
| | 8 | next.js | 626 ms (539 … 821) | 426 ms (374 … 509) | −30.1% | +14.1% |
| Apple M4 Max (12P + 4E) | 8 | VS Code | 440 ms (427 … 494) | 359 ms (354 … 404) | −18.3% | +4.4% |
| | 8 | next.js | 263 ms (248 … 282) | 191 ms (183 … 202) | −28.0% | +4.2% |
| | 16 | VS Code | 378 ms (362 … 571) | 328 ms (321 … 530) | −12.6% | +8.9% |
| | 16 | next.js | 238 ms (228 … 306) | 168 ms (164 … 249) | −29.1% | +2.6% |

Linux: 20 rounds per row. M4 Max: 40 rounds (two passes of 20). In every 20-round set, this PR was faster in at least 17 rounds.

CPU time also drops on the 8-thread Linux run: user + sys 1.73 → 1.26 s on next.js and 4.10 → 3.72 s on VS Code.

Without `--import-plugin`, time and RSS are within noise on both machines: paired medians range from −6% to +3%, and this PR was faster in 16–26 of 40 rounds. On the M4 Max, lint output (`-D correctness --import-plugin -f unix`, sorted) is byte-identical to `main`.

## Memory

Peak RSS is 3–17% higher with the import plugin. The largest increase is next.js on Linux; on the M4 Max the same corpus is +3% to +4%. Three things contribute:

- Overlapping batches keep up to `3 × group_size − 1` entry files and their dependencies in flight, compared with one batch on `main`.
- Lint jobs are spawned from the calling thread, so they go to rayon's global FIFO queue behind parses that are already queued. ASTs are linted and freed later.
- The per-file arena allocators keep their capacity and the pool does not shrink, so the higher peak persists until exit. On next.js, `MIMALLOC_PURGE_DELAY=0` reduced the gap from 86 MB to 28 MB in an earlier measurement.

Runs without the import plugin are unaffected. A lint-first scheduler did not reduce peak RSS on 4 cores. A cap on in-flight work is left for a follow-up.

Linking `loaded_modules` stays serial on the graph thread. A parallel iterator started from a non-worker thread would queue behind pending parses and block the loop.

## Tests

A unit test covers merging into a larger dependency group when `group_id` changes: it checks the new root, the accumulated counts and paths, and that the donor is cleared. Other `ModuleGroups` tests cover adding a group, path compression, and refusing to merge a finished group. The `import_plugin_multi_group` CLI fixture (9 files, `-D import/no-cycle`) matches `main`.

<details>
<summary>Reproduce</summary>

```sh
cargo build --release -p oxlint --bin oxlint --no-default-features --features allocator
echo '{}' > empty.json
# Linux: taskset -c 0-3 or 0-7. macOS: --threads 8 or --threads 16.
oxlint -c empty.json --disable-nested-config --silent -A all --import-plugin .
```

Run from VS Code `src` (`1306698e`) or next.js `packages/` (`3bccd4df`). Each build has its own `CARGO_TARGET_DIR`. Binaries run round-robin in rotating order with 2 warmups each. On Linux, rounds where other processes used more than 0.5 core were redone. On macOS, each round waited until other processes used less than 1.5 cores. Profiles: `samply record --rate 4000` pinned to separate cores, 1 warmup and 5 runs per binary.

`main` is `31cbe378d4`. Timings were taken on an earlier revision of this branch (`be6ef780c8`), before the branch was squashed into one commit. Changes made after that revision were not re-timed: tests and refactors, a deduplicated resolver check, the same scope selection for the language server and rule tests, one fewer `Arc` clone per already-queued import, and a simpler debug check.

</details>
<!-- CURSOR_AGENT_PR_BODY_END -->
2026-11-03 09:18:02 +00:00
YTandqing deng e3b1bb4e44 fix(transformer/class-properties): keep the value of a private accessor assignment (#28716)
If the target is `es2021` or lower, `this.#x += k`, `this.#x ??= k`, and
`++this.#x` on a private accessor give the wrong value. They should give
the new value of `#x`, but they give the return value of the setter
instead. Most setters return nothing, so the value is usually
`undefined`.

```js
class Counter {
  #n = 1;
  get #count() { return this.#n; }
  set #count(v) { this.#n = v; }
  add(k) { return (this.#count += k); }
}
console.log(new Counter().add(2)); // 3
```

The transformed code prints `undefined`, because the result of the
expression is the setter call:

```js
// before
return _set_count.call(_assertClassBrand(_Counter_brand, this), _get_count.call(_assertClassBrand(_Counter_brand, this)) + k);

// after
return _toSetter(_set_count.bind(_assertClassBrand(_Counter_brand, this)))._ = _get_count.call(_assertClassBrand(_Counter_brand, this)) + k;
```

Plain `=` and postfix `x++` are already correct, and plain `=` already
uses `_toSetter`. This PR uses the same form if the value of the
expression is used. If the expression is a statement, the output does
not change.

I did not use a temp var, because a temp var in a default parameter is
shared between calls, and a recursive setter overwrites it.

The new fixture `oxc/private-accessor-assignment-value` covers instance
and static accessors, all the operators above, an object from a function
call, arrow function bodies, and the recursive case. Its `exec.js` fails
on main with `expected 'from setter' to be 3`.

---

AI assistance was used for this change. I reviewed and tested it.

---------

Co-authored-by: qing deng <dengqing0821@gmail.com>
2026-11-03 15:48:08 +08:00
Dunqing ae7474e1f2 fix(minifier): keep array concat chain reads before later side effects (#28261)
Each call in an array `concat` chain reads its arguments before the next call evaluates its own. Merging the calls moves those reads later, so a later argument can change an earlier one first:

```js
function f(arr) { return [].concat(arr).concat(arr.pop()); }
```

With `arr = [1, 2, 3]` this returns `[1, 2, 3, 3]`, but main merges it into `[].concat(arr, arr.pop())`, which returns `[1, 2, 3]`.

A chain now merges only when no later call has an argument with side effects after an earlier call has an argument that `concat` may read. Primitives and array literals don't count as reads. This is the array half of #27580; the string half landed in #27098.

When the merge is refused, the innermost call still folds its leading literals into its array: `[1].concat(1, arr).concat(arr.pop())` → `[1, 1].concat(arr).concat(arr.pop())`.

## Size

`echarts.js` grows by 8 B minified and 6 B gzipped; nothing else in `minsize` changes. The chain `[].concat(ecIns._componentsViews).concat(ecIns._chartsViews)` no longer merges, because with the default `propertyReadSideEffects` a getter on `_chartsViews` could change `_componentsViews`. With `propertyReadSideEffects: false` it merges as before.

monitor-oxc: 48 files larger (+960 B raw, +76 B gzip), 8 B per `concat` call that no longer merges.

## Not covered

With `propertyWriteSideEffects: false`, `arr[0] = 9` counts as free of side effects, so `[].concat(arr).concat(arr[0] = 9)` still merges. The string guard has the same gap; it needs a separate fix.

Part of #26242.

Implemented with AI assistance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_018ogydZW3PPvmx3orpBQot4
2026-11-03 07:02:13 +00:00
YTandqing deng ec327aa1ec fix(semantic): keep block type bindings separate from hoisted vars (#28601)
Fixes #28600.

`var I; interface I {}` in a nested block merged the interface into the
hoisted `var`, so the type moved out of the block. TypeScript keeps the
interface in the block. #27936 fixed the opposite order, `interface I {}
var I;`.

## Cause

Hoisting a `var` records it in `hoisting_variables` for each
intermediate scope. `check_redeclaration` falls back to that map, and
`declare_symbol_on_scope` then reused the returned symbol, even for a
type declaration.

## Changes

- `declare_symbol_on_scope`: a declaration with no value flags only
reuses a symbol bound in the target scope. Otherwise it creates a new
symbol there.
- `check_redeclaration`: a binding in the scope and a hoisted `var` are
now checked separately. Without this, the block's type binding would
hide the conflict and the first change would lose the error in `{ var X;
interface X {} let X; }`.
- `check_redeclaration`: only the value flags of a hoisted symbol are
compared. The symbol can also carry type flags from a declaration in the
outer scope, and those do not conflict with a declaration in the block.

The second change also fixes a missed error in `{ interface X {} var X;
let X; }`. tsc rejects both forms with TS2481.

The value-flag restriction also fixes a false error on main: `type X =
number; { var X = 0; interface X {} }` was reported as a redeclaration,
but tsc accepts it.

An uninstantiated `namespace` in a block also has no value flags, so it
now gets its own block symbol too. tsc already rejects namespaces in
blocks (TS1235).

## Tests

- `tests/integration/symbols.rs`: symbol separation and reference
resolution, including the switch case, plus the redeclaration errors and
the valid programs where an outer type shares a symbol with the hoisted
`var`.
- `tests/fixtures/oxc/ts/interfaces/hoisted-var-then-interface.ts`: a
new snapshot of the scope placement. No existing semantic snapshots
changed.

---

AI assistance was used for this change. I reviewed and tested it.

---------

Co-authored-by: qing deng <dengqing0821@gmail.com>
2026-11-03 14:54:52 +08:00
camc314 3fd78d7451 fix(codegen): preserve names in duplicate source mappings (#26051)
When an unnamed parent node and a mangled identifier begin at the same source position, codegen deduplicated the identifier mapping. This retained location data but discarded the original identifier name, leaving consumers unable to restore inferred function names in source-mapped Node stack traces.

```mermaid
flowchart LR
    A[AssignmentExpression mapping<br/>unnamed] --> B[Token at source position P<br/>name: none]
    C[Identifier mapping<br/>name: longName] --> D{Same source position P?}
    D -->|before| E[Drop duplicate<br/>names: empty]
    D -->|after| F[Enrich existing token<br/>name: longName]
```

### Changes

- Cache whether the last emitted token has a name, keeping duplicate-position handling cheap in the source-map builder hot path.
- Enrich an unnamed duplicate token when a later identifier mapping supplies the original name.
- Add a unit regression test for the builder behavior.
- Extend the existing Node source-map stack-trace snapshot test with a mangled ES module fixture.

The Node fixture verifies:

```text
function longFunctionName(longName) { ... }
            ↓ mangled
function e(e) { ... }
```

and asserts that the source map includes both original names while Node restores the inferred inner frame as:

```text
at longName (/project/input.js:6:15)
```
2026-11-03 02:22:04 +00:00
renovate[bot] 406bc50d1d chore(deps): update oxc to ^0.157.0 (#28734)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [@oxc-project/types](https://oxc.rs)
([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types))
| [`^0.156.0` →
`^0.157.0`](https://renovatebot.com/diffs/npm/@oxc-project%2ftypes/0.156.0/0.157.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@oxc-project%2ftypes/0.157.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@oxc-project%2ftypes/0.157.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@oxc-project%2ftypes/0.156.0/0.157.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@oxc-project%2ftypes/0.156.0/0.157.0?slim=true)
|
| [oxc-parser](https://oxc.rs/docs/guide/usage/parser)
([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/napi/parser))
| [`^0.156.0` →
`^0.157.0`](https://renovatebot.com/diffs/npm/oxc-parser/0.156.0/0.157.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/oxc-parser/0.157.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxc-parser/0.157.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxc-parser/0.156.0/0.157.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxc-parser/0.156.0/0.157.0?slim=true)
|

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxc-parser)</summary>

###
[`v0.157.0`](https://redirect.github.com/oxc-project/oxc/blob/HEAD/napi/parser/CHANGELOG.md#01570---2026-11-02)

##### 🐛 Bug Fixes

-
[`3421516`](https://redirect.github.com/oxc-project/oxc/commit/3421516)
parser: Preserve default import names in reexports
([#&#8203;28442](https://redirect.github.com/oxc-project/oxc/issues/28442))
(Boshen)
-
[`ed7df7d`](https://redirect.github.com/oxc-project/oxc/commit/ed7df7d)
ast/estree: Include comments in TS Program span
([#&#8203;28326](https://redirect.github.com/oxc-project/oxc/issues/28326))
(camc314)

</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 these
updates 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDIuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjIwMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-11-02 22:04:22 +00:00
oxc-guard f3fcdcc61b release(apps): oxlint v1.91.0 && oxfmt v0.76.0 (#28718)
# Oxlint
### 🚀 Features

- 004647a track_memory_allocs: Add allocation tracking for linter rules (#28470) (Cam McHenry)
- 419c1b0 linter: Add allow and allowPattern options to react/capitalized-calls (Christian Vuerings)

### 🐛 Bug Fixes

- a205cd3 linter/eslint/no-control-regex: Detect raw control characters (#28548) (camc314)
- 7c65777 linter/unicorn/prefer-global-this: Align typeof and worker API handling (#28549) (camc314)
- 987d5f1 linter/unicorn/no-invalid-fetch-options: Avoid treating unknown methods as GET (#28547) (camc314)
- e234dc8 linter/unicorn/no-useless-error-capture-stack-trace: Move to suspicious category (#28545) (camc314)
- b3dc411 linter/typescript/no-non-null-asserted-nullish-coalescing: Move to suspicious category (#28546) (camc314)
- cb64638 regular_expression: Reject incomplete Unicode atom escapes (#28448) (Boshen)
- c67167e linter: Only run React Compiler rules on files with top-level components or hooks (Boshen)
- 4c9d5aa linter: Treat Reanimated shared values as ref-like in React Compiler rules (Boshen)
- 61fe2af linter/react: Ignore impure calls in async components in react/purity (Rahul Kashyap)
- ed7df7d ast/estree: Include comments in TS Program span (#28326) (camc314)
- 31cbe37 linter: Make gitlab fingerprints unique per diagnostic (#28307) (camc314)

### ⚡ Performance

- bb947dc linter: Skip numeric separator formatting for short integers (#28485) (Cam McHenry)
- 1043dfa linter: Avoid collecting prefer-const binding identifiers (#28484) (Cam McHenry)
- 7c93eef linter: Reduce branches-sharing-code scratch allocations (#28483) (Cam McHenry)
- c0065fd linter: Allocate escape-case replacements only when needed (#28482) (Cam McHenry)
- d837414 linter: Lazily normalize capitalized-comments text (#28481) (Cam McHenry)
- 6baaad6 linter: Avoid no-var binding identifier allocations (#28480) (Cam McHenry)
- 882053b linter: Skip read-only parameters in no-param-reassign (#28479) (Cam McHenry)
- 92deebf linter: Reduce no-unmodified-loop-condition allocations (#28478) (Cam McHenry)
- f01d52d linter: Reduce no-duplicate-case allocations (#28477) (Cam McHenry)
- 6a72d84 linter: Reduce no-dupe-class-members allocations (#28476) (Cam McHenry)
- 77599ef linter: Reduce no-this-before-super allocations (#28475) (Cam McHenry)
- 2ea9f58 linter/no-dupe-else-if: Use smallvec to reduce allocs (#28474) (Cam McHenry)
- 69f0204 linter/react/rules-of-hooks: Reduce allocs (#28473) (Cam McHenry)
- b138c20 linter: Reduce allocs in no-dupe-keys (#28472) (Cam McHenry)
- 6f3e982 linter: Avoid operator allocations in no-useless-length-check (#28471) (Cam McHenry)
- fd68928 react-compiler: Skip passes lint does not need (Yagiz Nizipli)
- 224f4dd linter/eslint/sort-keys: Reserve initial capacity for string (#27624) (Cam McHenry)
- e4e01a3 linter/import/no-cycle: Compare visited modules by absolute resolved path (#27517) (Cam McHenry)
- 741b29b linter/import/no-cycle: Cache sorted module record snapshots (#27516) (Cam McHenry)
- 20022bb linter/import/no-cycle: Reuse traversal buffers between imports (#27481) (Cam McHenry)
- fd5edb7 linter/import/no-cycle: Reuse module records in diagnostic stack (#27480) (Cam McHenry)
- 73e877c linter/import/no-cycle: Don't allocate paths already seen (#27478) (Cam McHenry)
- 1515d53 linter/react/forward-ref-uses-ref: Narrow forward-ref-uses-ref dispatch (#28367) (Cam McHenry)
- ee51c01 linter/react/no-string-refs: Narrow AST dispatch (#28366) (Cam McHenry)
- bbfd692 linter/react/no-children-pop: Narrow AST dispatch (#28365) (Cam McHenry)
- a86158b linter/unicorn/prefer-set-size: Narrow AST dispatch (#28364) (Cam McHenry)
- a777fa6 linter/eslint/no-iterator: Narrow member dispatch (#28363) (Cam McHenry)
- bc5e94d linter/eslint/no-extra-boolean-cast: Narrow AST dispatch (#28362) (Cam McHenry)
- 93c6e6d linter/unicorn/no-useless-fallback-in-spread: Narrow AST dispatch (#28361) (Cam McHenry)
- 1a1f66e linter/typescript/no-this-alias: Narrow AST dispatch (#28360) (Cam McHenry)
- b102a91 linter/typescript/no-wrapper-object-types: Index references (#28359) (Cam McHenry)
- dbd8a0b linter/eslint/no-unassigned-vars: Batch by declaration (#28358) (Cam McHenry)
- 1108352 linter/typescript/prefer-as-const: Narrow AST dispatch (#28357) (Cam McHenry)
- d65531f linter/eslint/no-const-assign: Batch by declaration (#28356) (Cam McHenry)
- 1ec9991 linter/oxc/number-arg-out-of-range: Narrow AST dispatch (#28355) (Cam McHenry)
- 7a1243e linter/unicorn/no-invalid-fetch-options: Narrow AST dispatch (#28354) (Cam McHenry)
- 2ab73cc linter/unicorn/no-single-promise-in-promise-methods: Narrow AST dispatch (#28353) (Cam McHenry)
- 483c4c9 linter/eslint/no-compare-neg-zero: Narrow no-compare-neg-zero dispatch (#28352) (Cam McHenry)
- 56c99c6 linter/eslint/no-unsafe-negation: Narrow AST dispatch (#28351) (Cam McHenry)
- 84d974b linter/unicorn/prefer-string-starts-ends-with: Narrow AST dispatch (#28350) (Cam McHenry)
- 142ef4f unicorn: Narrow no-await-in-promise-methods dispatch (#28349) (Cam McHenry)
- a3dc7a2 linter/unicorn/no-await-expression-member: Optimize AST dispatch (#28348) (Cam McHenry)
- 481b638 linter/plugins: Create AST nodes with `Object.create` instead of `__proto__` literals (#27816) (Charles Wang)
# Oxfmt
### 💥 BREAKING CHANGES

- 296623d oxfmt: [**BREAKING**] Format `parser:html` files by `oxc_formatter_htmlish` (#28273) (Yuji Sugiura)

### 🚀 Features

- 336b2c1 oxfmt: Support html-in-js templates closing a dynamic tag name (#28389) (Yuji Sugiura)
- 0a42af6 formatter_htmlish: Format end tags of omitted `html`, `head` and `body` (#28388) (Yuji Sugiura)
- 44d8c2b formatter_htmlish: Implement HTML-ish formatter `Html` variant (#28272) (Yuji Sugiura)
- 36e5ddb oxfmt: Add debug span for reading files (#28204) (leaysgur)

### 🐛 Bug Fixes

- dbcc358 formatter: Keep arrow conditional body parens before a leading `function`/`class` (#28710) (Yuji Sugiura)
- fef809a formatter_htmlish: Handle XHTML (#28703) (leaysgur)
- f3dd201 formatter_htmlish: Keep SVG text under `xml:space="preserve"` as written (#28382) (Yuji Sugiura)
- fb3aefa formatter: Keep source parens around html-in-js placeholders past comments (#28381) (Yuji Sugiura)
- e738dae formatter: Quote keys added in an HTML event handler with single quotes (#28380) (Yuji Sugiura)
- 2e7a629 oxfmt: Format HTML in a `<script>` body as an embedded part (#28379) (Yuji Sugiura)
- f00862b formatter: Keep parens around a generic function type in a type assertion (#28173) (vidigoat)
- 9ead5c4 formatter: Handle `this.b` and `super.b` like `foo.b` (#28200) (leaysgur)
- 72a54cf formatter: Break parens of `(a + b)[c]` like `(a + b).c` (#28199) (leaysgur)
- b8f4f34 formatter: Print `x.b` and `x.#b` samely (#28193) (leaysgur)
- 9ddccb7 formatter: Keep parens for private field (#28188) (leaysgur)

### ⚡ Performance

- b6f1ac9 formatter_markdown: Bump parser to 0.0.4 and skip unread work (#28713) (leaysgur)
- 633ee96 formatter_css: Use `u32` spans (#28708) (leaysgur)
- f680ee1 formatter_yaml: Use `multiline` flag for flow scalars (#28707) (leaysgur)
- ff7c203 formatter_css: Avoid repeated scans and allocations in values (#28706) (leaysgur)
- 7cd46cb formatter_htmlish: Search script end tags by `<` in embedded (#28704) (leaysgur)
- 46f8efd formatter_graphql: Print block string lines borrowed from the source (#28701) (leaysgur)
- dce1485 formatter_htmlish: Skip escaping attr values without the chosen quote (#28700) (leaysgur)
- e9392c8 formatter_core: Search line terminators with memchr (#28699) (leaysgur)
- ceb26be formatter_htmlish: Use `char` to escape attr quotes (#28697) (leaysgur)
- 8239dac parser: Replace dragonbox_ecma with zmij_ecma (#28282) (Boshen)

### 📚 Documentation

- 303d9e8 formatter: Strengthen `start-token-no-double-parens` rationale (#28711) (Yuji Sugiura)
- a82ce83 oxfmt: Update unclassified divergence (#28685) (Yuji Sugiura)
- 7f18c20 formatter_htmlish: Note `class` and `srcset` are formatted with `embeddedLanguageFormatting: off` (#28465) (Yuji Sugiura)
- 01ed68a oxfmt,formatter_htmlish,formatter: Clean up behavior comments (#28384) (Yuji Sugiura)
- a363c27 formatter_css,formatter_yaml: Point parser citations at AGENTS.md (#28466) (Yuji Sugiura)
- 6586050 formatter_core: Document tab width in text width policy (#28393) (leaysgur)
- 49fbffb formatter_core: Tighten FORMATTER_POLICY (#28206) (leaysgur)
oxlint_v1.91.0 oxfmt_v0.76.0
2026-11-02 19:47:18 +00:00
oxc-guard f799f174b5 release(crates): oxc v0.157.0 (#28719)
### 🚀 Features

- 8dd67a6 transform-react: Expose TypeScript transform options (Tomas Särnhammar)
- deed7dc parser: More friendly error for duplicate import attribute key (#28262) (sapphi-red)

### 🐛 Bug Fixes

- eaa8e90 ecmascript: Avoid numeric `+` folding when an operand may be a string (#28544) (camc314)
- b03c7b8 regular_expression: Allow incomplete oversized legacy quantifiers (#28449) (Boshen)
- 128c67d semantic: Check strict eval and arguments writes by reference flags (#28445) (Boshen)
- c97c43c parser: Reject TypeScript default exports in JavaScript (#28440) (Boshen)
- fdd119f span: Count Unicode characters when filtering typo suggestions (#28452) (Boshen)
- cb64638 regular_expression: Reject incomplete Unicode atom escapes (#28448) (Boshen)
- fc0eed3 semantic: Stop let-binding scans at arrow functions (#28444) (Boshen)
- 3421516 parser: Preserve default import names in reexports (#28442) (Boshen)
- a1e7e5f parser: Reject optional class members in JavaScript (#28439) (Boshen)
- 5b9a7bc parser: Parse sloppy let expressions and labels (#28438) (Boshen)
- 8e61652 regex: Reject templates without cooked values (#28345) (Boshen)
- 3b36543 parser: Preserve in-context after TypeScript type arguments (#28300) (Boshen)
- 2e3126c regex: Decode constructor patterns and flags consistently (#28298) (Boshen)
- 8a90be2 regex: Track capture groups and disjunction identity (#28297) (Boshen)
- 3dc2cb0 regex: Preserve escape values and boundaries (#28296) (Boshen)
- fc8acbc parser: Handle leading-zero numeric exponents (#28295) (Boshen)
- 91a638d parser: Normalize raw template line endings with invalid escapes (#28285) (Boshen)
- 2ac9a42 parser: Allow parenthesized private-in expressions (#28284) (Boshen)
- 052bca9 ast: Prevent greyed out fields in AST types in IDEs (#28335) (overlookmotel)
- ed7df7d ast/estree: Include comments in TS Program span (#28326) (camc314)

### ⚡ Performance

- 9b6b7bf regular_expression: Advance validated Unicode escapes directly (#28514) (Boshen)
- 82e1982 regular_expression: Share Unicode property name prefixes (#28513) (Boshen)
- 5395a40 regular_expression: Parse class set character prefixes once (#28512) (Boshen)
- 3708934 regular_expression: Defer literal range dash allocation (#28511) (Boshen)
- d85ee3d semantic: Skip arguments ancestry checks outside classes (#28510) (Boshen)
- cf81d07 semantic: Reuse private reference resolution (#28509) (Boshen)
- a79eebc semantic: Avoid duplicate maps for public JavaScript members (#28508) (Boshen)
- 235ca3b semantic: Skip escape checks for unescaped strings (#28507) (Boshen)
- babf44c parser: Reuse shorthand assignment target identifiers (#28504) (Boshen)
- 444f24a ecmascript: Short-circuit simple parameter checks for rest (#28503) (Boshen)
- 04b20c0 semantic: Compute duplicate parameter policy once per list (#28446) (Boshen)
- dbf8482 parser: Use token lookahead for simple function types (#28489) (Boshen)
- bca1275 parser: Avoid unused destructuring rest capacity (#28499) (Boshen)
- 691be5c parser: Compact cover initialized name storage (#28495) (Boshen)
- b3b5444 parser: Build simple arrow bindings directly (#28493) (Boshen)
- e1c9baf parser: Disambiguate single-identifier arrow heads (#28491) (Boshen)
- 550e98a regular_expression: Avoid rescanning unique capture names (#28450) (Boshen)
- 10e9fc7 parser: Skip duplicate lookup for singleton import attributes (#28441) (Boshen)
- 5238839 parser: Use a stack buffer for float separators (#28435) (Boshen)
- 6e35e3c parser: Fast-path leaf expressions before delimiters (#28432) (Boshen)
- 3d7512c parser: Decode Unicode identifiers once (#28414) (Boshen)
- 30f59f4 parser: Factor object type opening brace (#28430) (Boshen)
- 028a267 parser: Always inline postfix update parsing (#28429) (Boshen)
- 1b5a171 parser: Reuse export kind classification (#28424) (Boshen)
- 179a432 parser: Factor return type identifier parsing (#28416) (Boshen)
- 553dd1d parser: Inline hot paths and fold function dispatch (#28404) (Boshen)
- de75d56 parser: Combine parenthesized arrow lookahead (#28412) (Boshen)
- a768a0c parser: Reuse comment annotations after rewind (#28408) (Boshen)
- 563d92a parser: Skip redundant source cursor updates (#28409) (Boshen)
- 7941ca9 parser: Factor tuple identifier parsing (#28398) (Boshen)
- 426c151 parser: Construct completed tokens without intermediate writes (#28291) (Boshen)
- df42953 minifier: Don't request another pass for block unwrapping (#28315) (Dunqing)
- c2d663d parser: Avoid vectors for single parenthesized expressions (#28374) (Boshen)
- 50bd0a9 parser: Size delimited lists after the first element (#28373) (Boshen)
- bd869b5 parser: Bypass redundant operand parsing (#28372) (Boshen)
- 455fc09 parser: Skip keyword checks for plain identifier expressions (#28371) (Boshen)
- 61303fb parser: Fast-path identifier operands (#28306) (Boshen)
- 6cf5b1a parser: Guard arrow and conditional parsing (#28305) (Boshen)
- 3666939 parser: Specialize identifier reference parsing (#28303) (Boshen)
- 0d6641a parser: Skip inapplicable expression parsing paths (#28294) (Boshen)
- 3be7997 parser: Reduce lexer search batch size (#28292) (Boshen)
- ab5655a regex: Remove redundant character-class and quantifier checks (#28288) (Boshen)
- ec8e8d4 parser: Remove redundant grammar and lexer checks (#28287) (Boshen)
- c903f5c parser: Specialize known-token parsing (#28286) (Boshen)
- 2683225 str: `static_ident!` and `static_str!` macros guarantee const evaluation (#28340) (overlookmotel)
- df90836 ast: Reduce compile time of `#[ast]` macro (#28336) (overlookmotel)
- 8239dac parser: Replace dragonbox_ecma with zmij_ecma (#28282) (Boshen)
crates_v0.157.0
2026-11-02 19:40:44 +00:00
Brooooooklyn f8982585aa test(lsp): remove duplicate dynamic watchers capability test (#28585)
<!-- claude-projects-attribution: {"github_login":"Brooooooklyn"} -->
_Requested by **Brooklyn** · [project thread](https://claude.ai/code/project/chan_018s9RxRdtH424j2EhXdzV3N?thread=cmsg_018s9RxRdtH424j2EhXdzV3NQxbeR8Pt7qaqSQwoq9rEFo)_

<!-- routine:prune-tests:duplicate-lsp-capability-test -->

Before: `capabilities::test::test_dynamic_watchers_intellij` and `test_dynamic_watchers_vscode` in `crates/oxc_language_server/src/capabilities.rs` have been byte-identical since both were added in #11075: the same `ClientCapabilities` with `workspace.did_change_watched_files.dynamic_registration: Some(true)`, and the same `assert!(capabilities.dynamic_watchers)`.

After: the IntelliJ copy is gone. Only lines inside `#[cfg(test)] mod test` change.

### Totals

- Files touched: 1.
- Cases deleted: **1 of 111** collected by `cargo test -p oxc_language_server -- --list` (110 run, 1 ignored); after: 110.
- Cases merged: 0. Net lines: **−17**.
- Runtime saved: 0.09 ms, the removed case's duration.

### Why it is safe

- Mutation M1, `capabilities.rs:41`: `dynamic_registration.is_some_and(...)` → `relative_pattern_support.is_some_and(...)`. Both capability tests fail identically; since the bodies are identical, every mutation affects both the same way.
- Still covered by `test_dynamic_watchers_vscode`. The 4 `tests::test_suite` dynamic-watcher tests also notice M1, but by hanging (see below).

Kept after defense: none.

| suite | before | after | delta |
|---|---|---|---|
| `oxc_language_server` (lib) | 111 (110 pass, 1 ignored) | 110 (109 pass, 1 ignored) | −1 |

0 failures before and after; the same test is ignored in both runs.

### What ran and what did not

- Ran at `d14dbd1e1` and on this branch: `cargo test -p oxc_language_server` (with `--list`, and JSON output with `--report-time`); M1, reverted; `cargo clippy -p oxc_language_server --all-targets -- --deny warnings`; `rustfmt --check --edition 2024` on the file; `git diff --check`. All clean.
- Not run: the rest of the workspace and the coverage/conformance suites.

Open pull requests: none of the open PRs touches `capabilities.rs`. Age: the test dates from 6b68de0da (#11075, 2025-05-18) and was not added for a bug.

To revert: `git revert 4bf9d3fea`.

---
_Generated by [Claude Code](https://claude.ai/code/session_01PxVsmvB9xfZKfVnUonY8kf)_
2026-11-02 18:27:22 +00:00
Dunqing 564559dd11 perf(linter/prefer-const): avoid collecting resolved references (#28732)
Two checks in `prefer_const.rs` collected resolved references into temporary vectors before scanning them. Borrow the resolved reference ID slices and map IDs to references in each existing pass, removing both allocations while preserving reference order, predicates, and behavior.
2026-11-02 13:31:55 +00:00
Dunqing 563cf449a9 chore(runtime): sync helpers with Babel 8.0.8 (#28172)
Sync the complete published `@babel/runtime@8.0.8` helper tree so future updates have a recorded upstream baseline.

Import all 95 CommonJS/ESM helper pairs, the MIT license, conditional exports, and `sideEffects: false`. Preserve 26 legacy Babel helper pairs, three TypeScript decorator helper pairs, and existing import paths.

189 of the 191 upstream files are byte-identical. The two `setFunctionName` implementations retain Oxc's existing behavior and match main. Add a hash manifest, a repeatable sync script, CJS/ESM tests in CI, and an async-generator execution regression. No new helper optimizations or correctness patches are included.

Validation: upstream hashes, compatibility preservation, sync idempotence and drift refusal, formatting, lint, syntax, and whitespace checks passed. Runtime tests were not rerun after restoring this tree because automatic approval blocked helper execution; the newer Babel fixture revision pinned by main was not run locally.

AI assistance: Claude.
2026-11-02 09:38:54 +00:00
Yuji SugiuraandClaude Opus 5.5 303d9e803f docs(formatter): strengthen start-token-no-double-parens rationale (#28711)
The leading parens only guard a statement or arrow body start, so
Prettier prints the same object lookup idiom without them elsewhere
(`const get = ({ a: 1, b: 2 } as const)[key];`).
List that sibling and the assignment arrow body in `Why`, and replace
the `export default` example with a measured one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:45:32 +09:00
Yuji SugiuraandClaude Opus 5.5 dbcc358aee fix(formatter): keep arrow conditional body parens before a leading function/class (#28710)
`should_add_parens` skipped the parens of an arrow's conditional body
when it starts with an object, function or class, assuming the leading
node prints its own.
Only an object does in an arrow body, so `() => (function () {}).a ? b :
c` printed with no parens at all.

Matches Prettier: skip them only for a leading object.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:45:31 +09:00
leaysgur b6f1ac9b05 perf(formatter_markdown): bump parser to 0.0.4 and skip unread work (#28713)
- bump oxc-markdown-parser 0.0.4 which skips autolink literal attempts that cannot match
- Compute a text's next words only under `proseWrap: always`, the only mode that reads them
- Print link destinations with nothing to escape as is

About 15% CPU instruction reduction.
2026-11-02 08:26:19 +00:00
leaysgur 633ee960da perf(formatter_css): use u32 spans (#28708)
- bump oxc-css-parser 0.0.19 which changes `Span` from `usize` to `u32`
- update conversion codes

Peak memory is reduced 5% ~ 20% in large CSS file.
2026-11-02 08:26:19 +00:00
leaysgur f680ee1f2e perf(formatter_yaml): use multiline flag for flow scalars (#28707)
- bump `oxc-yaml-parser` which has `multiline` flag
- eliminate all `\n` contains check from formatter

Reduce 5~7% CPU instructions.
2026-11-02 08:26:18 +00:00
leaysgur ff7c203711 perf(formatter_css): avoid repeated scans and allocations in values (#28706)
Use `smallvec` and optimize `split_comma_groups()`.
2026-11-02 08:26:18 +00:00
Dunqing bc152ac05c chore(coverage): remove stale runtime timeout from snapshot (#28715)
The runtime snapshot still records a transformer timeout for async-private-gen-meth-static-ary-ptrn-elem-id-init-fn-name-arrow.js, which now passes. Regenerate the snapshot, increasing the passing count from 17,314 to 17,315 of 18,277.

Verified with cargo coverage runtime, which reproduced the monitor-oxc CI diff exactly, and an isolated run of the affected test.

AI assistance was used to prepare and verify this update.
2026-11-02 07:32:44 +00:00
Shanon Jackson bab0206079 fix(lexer): lex type arguments after an optional chaining dot (#28714)
- Only a spread dot precedes an operand, so a?.<T>(b) opens type
arguments instead of JSX and a./x/ is a division, as in tsc
- Split a << after a dot or a non-null mark into two type lists
- Accept const in a type parameter list nested in type arguments
2026-11-02 20:23:00 +13:00
leaysgur 7cd46cb70b perf(formatter_htmlish): search script end tags by < in embedded (#28704)
Optimize `</script` finding in js-in-html.
2026-11-02 05:03:29 +00:00
leaysgur 46f8efd1fb perf(formatter_graphql): print block string lines borrowed from the source (#28701)
GraphQL block string `"""..."""` may or may not contain CR,CRLF.

But if not, we can use source as-is.
2026-11-02 05:03:28 +00:00
leaysgur dce14853f6 perf(formatter_htmlish): skip escaping attr values without the chosen quote (#28700)
Skip useless attr escape if possible.
2026-11-02 05:03:28 +00:00
leaysgur 73a368faee test(formatter_css): classify grammar-owner-rejected errors (#28705)
And add as tests.
2026-11-02 04:57:27 +00:00
leaysgur e9392c88c6 perf(formatter_core): search line terminators with memchr (#28699)
For languages like CSS, YAML, Markdown, etc, `normalize_newlines` is called for CRLF normalization.

About 3~6% improvement for CPU instruction.
2026-11-02 04:52:00 +00:00
leaysgur ceb26be83c perf(formatter_htmlish): use char to escape attr quotes (#28697)
single character `&str` with `cow_replaces` is not efficient, `char` is enough here.
2026-11-02 04:52:00 +00:00
leaysgur fef809a7f3 fix(formatter_htmlish): handle XHTML (#28703)
- bump `oxc-htmlish-parser` to support Xhtml dialect
- Use it in formatter
2026-11-02 04:52:00 +00:00