This PR is inspired by #26333, addresses the repeated `with_hasher_in(IdentBuildHasher, allocator)` call, and removes the extra `'ident` lifetime.
`ArenaHashMap::new_in` works with any `Default` hasher, so `ArenaIdentHashMap::new_in(allocator)` picks `IdentBuildHasher` automatically. `ArenaHashSet` only has `new_in`, `with_capacity_in` and `from_iter_in` for `FxBuildHasher`, so an `Ident`-keyed arena set has to call `with_hasher_in(IdentBuildHasher, allocator)` every time (see #26333).
Mirror the map's `S: Default` impl on the set and add an `ArenaIdentHashSet<'alloc>` alias next to `ArenaIdentHashMap`. Nothing calls the `FxBuildHasher`-only constructors today, so no other code changes.
The alias takes a single lifetime, same as `ArenaIdentHashMap`. A separate lifetime for the `Ident` keys is not needed, and dropping it is sound: both `ArenaHashSet` and `Ident` are covariant, so when the idents are borrowed from somewhere other than the arena (for example `Scoping` in the mangler), the borrow checker shrinks `'alloc` to the shorter of the two borrows. The set can never outlive the strings its idents point to.
`SequenceExpression` inlining from `minimize_conditional_after_if` is a
more optimal version of inlining when we are constructing conditional
expr, with this change we will do this always instead of falling to
`minimize_conditional_expression`
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
add test cases for uncovered code and branches that should be tested:
verified with llvm-cov
```sh
cargo +nightly llvm-cov clean --workspace && cargo +nightly llvm-cov --branch --html -p oxc_minifier --test mod
```
i found out that our coverage is not really ideal while working on
#26264
Fixes#26290, #26291.
Also, many changes to follow refined parser acceptance line.
---
CI will fail until parser version bumped, needs oxc-project/oxc-css-parser/pull/166
The duplicate class element check stored only the last element for each name. This allowed a public element to hide an earlier private declaration, and a setter to hide an earlier getter (or vice versa):
```js
class DuplicateField {
#x;
x;
#x;
}
class DuplicateGetter {
get #x() {}
set #x(value) {}
get #x() {}
}
```
Both classes were accepted despite declaring the same private field or getter twice. The same problem affected setter/getter/setter sequences.
Key a single map by both the element name and whether it is private, so public elements cannot replace private declarations. This keeps the namespaces separate while reserving map capacity only once. For each private name, retain separate slots for setters and other declarations. Look for a previous declaration in the matching slot first, then fall back to the other slot. This catches repeated getters and setters while still allowing one getter/setter pair when both have the same static status.
The existing diagnostic logic continues to reject private names shared by static and instance elements, including the TypeScript-specific diagnostic. Public elements continue through the existing public-element duplicate check.
`run_file` is an internal function, which does not get called by the language server.
Instead, it calls the parent function `run_diagnostic`. Refactor the code, so internal implication is kept secret in tests.
The parameter check only recognized `FormalParameter`, allowing `await` and `yield` in destructuring defaults inside rest parameters:
```js
async function f(...[x = await 0]) {}
function* g(...[x = yield 0]) {}
```
Include `FormalParameterRest` so both cases report the existing parameter diagnostic. The check still stops at nested functions, where `await` and `yield` can be valid.
Reject labeled and unlabeled `break` and `continue` statements that cross an arrow-function boundary.
For example:
```ts
while (true) {
(() => {
break;
});
}
```
now reports an illegal break, while jumps targeting loops inside the arrow remain valid.
Use `Ident` instead of `Str` in `KeepVar`. This reduces the `Ident` -> `Str` -> `Ident` conversion.
I used AI to generate the code and then reviewed the code.
Use `IdentHashSet` in `PrivateMemberUsageStack`. This reduces the `Ident` -> `Str` conversion and also makes the `Set::contains` operation faster as the hashing doesn't happen.
I used AI to generate the code and then reviewed the code.
`TriviaBuilder::pure_comments` and `no_side_effects_comments` are index ranges into `comments`.
Previously they were stored as `Option<(usize, usize)>` (24 bytes). Instead, store them as `Option<(u32, NonZeroU32)>` (8 bytes, due to `NonZeroU32`'s niche).
`end` is always at least `start + 1`, so it cannot be 0. Comment count cannot exceed `u32::MAX` because of the `MAX_LEN` check in `Source::new`.
`LexerCheckpoint` contains both ranges, so this change shrinks it by 32 bytes.
Micro-optimization to `parse_jsx_element_name`.
`parse_jsx_element_name` inspects the first byte of the identifier name to determine if it should be treated as a reference (`<Foo>` vs `<div>`).
`parse_jsx_identifier` already ensured the identifier is valid, so that first byte can only be one of `[A-Za-z$_]` or the identifier starts with a non-ASCII character. Utilize this constraint to make the "is this a reference" check cheaper.
The new version compiles down to a single `cmp` instruction, shaving off one instruction from the previous version.
It's only 1 instruction, but why not?
Small perf optimization to parser.
Remove the `if self.fatal_error.is_some()` branch from `parse_jsx_element_name`.
The guard existed so that code below it doesn't try to read the first byte of the dummy identifier's empty name after `parse_jsx_identifier` fails. Instead, make the error path return `contains_dash = true`, which skips reading the name in `parse_jsx_element_name`.
In `parse_jsx_member_expression`, a dash is already treated as an error, and its `fatal_error` call is a no-op because a fatal error has already been recorded. The other callers ignore the flag.
This removes a check, but main motivation is preparation for larger changes to how fatal errors are handled in further PRs to follow.
Refactors the `oxc_language_server::Tool` trait and associated LSP plumbing to pass `TextDocument` and `CodeActionParams` by value, aligning tool APIs with “consume” semantics and simplifying call sites that previously had to keep borrowed values alive.
Technically a breaking change, but one that's extremely unlikely to affect any real-world applications.
On 64-bit systems, the maximum source text length was exactly `u32::MAX`.
Lower it by 256 bytes, for 2 reasons:
1. `oxc_lexer` will require source text to be followed by 64 bytes of padding. Reducing max source length ensures _padded_ length can always fit in a `u32`.
2. Counts of tokens, comments and errors are all bounded by source length plus a small constant. By reducing max source length, these can all be stored as `u32`s without any possibility of overflow.
`oxc_lexer` only needs 64 bytes of padding. Have gone with 256 to leave headroom in case later redesigns require more than 64. This avoids another breaking change later. We can always switch to 64 later on if we want, and this later relaxation would not be a breaking change.
All the changes to other crates in this PR are just updating comments which talk about max source length.
Small perf optimization to parser.
`Lexer::token` is the token currently being built. Every method which produces a token ends in `finish_next_inner` or `finish_re_lex`, and both reset it to `Token::default()`. So between lexer calls it is always `Token::default()`, so there is no point in saving and restoring it - it's always a no-op.
Remove the `token` field from `LexerCheckpoint`, which reduces its size by 16 bytes.
Add a debug asserts in `checkpoint` and `checkpoint_with_error_recovery` checking this invariant.
<summary>Pull request overview</summary>
This PR refactors the language server’s `WorkspaceWorker::collect_diagnostics_with` to avoid building an intermediate map when collecting tool diagnostics, returning the tool’s `DiagnosticResult` directly.
This was an old code path when `oxc_language_server` did used both tools (`oxlint` & `oxfmt`) at the same time
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) | action
| patch | `v1.50.0` → `v1.50.1` |
---
### Release Notes
<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>
###
[`v1.50.1`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.50.1)
[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.50.0...v1.50.1)
#### \[1.50.1] - 2026-09-01
##### Fixes
- Don't correct `asend` in Python code
</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:eyJjcmVhdGVkSW5WZXIiOiI0NC42MS4zIiwidXBkYXRlZEluVmVyIjoiNDQuNjEuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
I am the codeowner of memory allocation tracking infra. However, this has meant that I get added as a reviewer on every PR which alters the snapshot files (notably a torrent of PRs on minifier, which I know nothing about). This makes it harder to track what PRs I _should_ actually be looking at.
Attribute ownership of the snapshot files to the codeowners of the relevant crates. This seems more logical.
With https://github.com/oxc-project/oxc/pull/23329 the conversion from offset to line/column position is now done by a helper function in `oxc_language_server`.
There was a helper function inside `oxlint` to call the function. Instead, call it directly now.
Moved the tests to the correct place, and removed duplicated ones.
The `Option<>` wrap for `Vec<LinterCodeAction>` is not needed, after each lint-run we save an entry (even if empty) to the map.
There is no different code branch for `None` as a value.
I'm not actually the primary owner of this code - @ShanonJackson is. But add me to codeowners file, so I get added as a reviewer on any PRs touching the crate, so I don't miss them.
Fix tokens produced when `<<` opens a type argument list. e.g.:
```typescript
type Bar = ReturnType<<T>(x: T) => number>;
```
Previously only 1 `<` token was produced between `ReturnType` and `T`. This PR corrects that so 2 `<` tokens are produced.
TS-ESLint get this wrong too - hence why this wasn't noticed before, as our conformance tests compare to TS-ESLint.
PR submitted to fix TS-ESLint: https://github.com/typescript-eslint/typescript-eslint/pull/12821
In meantime, this PR skips the erroneously failing ESTree tokens conformance tests. We can can re-enable those tests once the fix lands in TS-ESLint.
## Summary
- update `oxc-project/setup-node` from v1.4.0 to v1.4.1 across all workflows
- pick up `pnpm/action-setup` v6.0.10, whose newer bootstrap fixes broken `@pnpm/exe` shims during package-manager self-updates
The setup action fix was released after oxc-project/setup-node#34.
## Summary
The `id-length` lint rule lets users opt out of checking property name
lengths via a `properties: "never"` setting. This exemption worked
correctly for plain JavaScript object properties, but was silently
ignored for TypeScript interface members and object type literal
members. As a result, users who explicitly disabled property length
checks would still see false-positive warnings on their TypeScript type
definitions.
## Why
TypeScript interfaces and type literals describe object shapes using
their own dedicated syntax, distinct from JavaScript object literals.
The rule's internal logic only recognized the JavaScript form when
deciding whether to skip the length check, so the TypeScript equivalents
were treated as ordinary identifiers instead of property names, even
though users intended the same exemption to apply to both.
## Changes
- The rule now recognizes TypeScript interface and type literal members
as property names, so the `properties: "never"` setting applies to them
consistently with plain JavaScript object properties.
- Added test coverage confirming both the exempted and non-exempted
behavior for TypeScript interface and type literal members.
## Test plan
- Added passing test cases showing the exemption now applies to
TypeScript interface and type literal members.
- Added failing test cases confirming the rule still flags these members
under its default settings.
- Verified the fix manually against the exact reproduction case reported
in the issue, confirming the false-positive warning no longer occurs.
- Full linter test suite passes with no regressions.
## AI disclosure
Per the repo's AI usage policy: this fix was drafted with AI assistance
(Claude), and reviewed, tested, and understood by me before submission.
The linked issue itself was also AI-drafted and reviewed by me.
Resolves#26309
---------
Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
Historically, only JS formatter has had the `detect_code_removal` feature, which uses CI to check for missing nodes or comments.
While this would be useful in other languages (especially CSS), but setting up the mechanism is difficult. For now, just start with something simple that just guarantees the number of comments.
That said, even with a mechanism in place, it can't detect anything unless the cases are fully covered by fixtures, so this only tells us that the current state is problem-free.
If line-comment is printed with this and forget to add `hardline` after it, content rides on line-comment and disappear.
To prevent, hide raw API and always use safer struct based one.
## Summary
- update workspace test dependencies to Vitest 5.0.0
- pin Vitest and related tooling to Vite 8.2.2
- migrate removed sequential and benchmark APIs
- update the pnpm lockfile
#26263 added the ability to run tests and conformance for both versions of `oxc_lexer` (SIMD and scalar fallback) on any platform.
Run tests and conformance on CI (both SIMD and scalar) on any PR which touches `oxc_lexer` crate or other files which affect conformance runner.
Additionally, run Clippy on the SIMD version (main Lint CI task doesn't lint it, due to the `target_feature = "avx2"` gate on the code).
All of this sets up `oxc_lexer` crate for further development, catching any regressions in CI.
Follow-on after #26263. That PR added `just` commands to run `oxc_lexer` tests and conformance on any platform. Add a comment to the justfile pointing to solution for the cryptic error you get if `x86_64-apple-darwin` target is not installed on an M-chip Mac (it's not by default).