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
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.
#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.
Comments-only change.
While reviewing #26173 and #26223, I noticed that various comments on UTF-8 to UTF-16 offset conversion code were out of date or unclear. Bring them up to date, and add some clarifications.
Closes#26162.
`Utf8ToUtf16::new_with_offset` seeds the translation table with an
absolute BOM entry, `{ utf8_offset: 3, utf16_difference: 3 }`, but
`build_translations` records later entries relative to the trimmed
source text. From the first non-ASCII character on, the table is 3 bytes
short and offsets land on the wrong side of a boundary.
For the issue repro, a JS plugin reading
`context.sourceCode.getAllComments()[0]` gets `0..4`, so
`text.slice(start, end)` is `"/*x*"` instead of `"/*x*/"`. Parsing the
BOM'd file and converting comment spans the way `parse.rs` does gives
`comment span = 0..4`, `"/*x*"` before the change and `comment span =
0..5`, `"/*x*/"` after.
Add `offset` back on when pushing the entry so the table stays absolute.
This is one line in `build_translations`.
`Utf8ToUtf16::new` passes `offset: 0`, so the parser, coverage, and
benchmarks are unchanged. The four non-zero-offset callers are in the
linter's JS-plugin path: `crates/oxc_linter/src/lib.rs:752` (BOM) and
`:754` (leading newline of a Vue/Astro/Svelte fragment),
`apps/oxlint/src/js_plugins/parse.rs:249` and `fix.rs:34`. Each trims
the prefix from `source_text` before constructing this type. I left the
other two spots the reporter flagged alone because they look like
separate bugs.
Both new tests fail on `db66f58` before the change and pass after:
- `translate_offsets_after_trimmed_bom`: comment end `8` converts to
`4`, expected `5`.
- `translate_offsets_with_trimmed_prefix`: sweeps every byte offset of
three sources against the same source with nothing trimmed, for a 1-byte
and a 3-byte prefix. One source puts the non-ASCII characters in the
SIMD aligned-chunk path rather than the short-source path.
Ran on `db66f58`, macOS arm64, rustc 1.98.0:
- `cargo test -p oxc_ast_visit --lib --features serialize`: 5 passed, 0
failed (3 passed, 2 failed without the fix)
- `cargo test -p oxc_linter`: 1256 passed, 0 failed
- `cargo clippy -p oxc_ast_visit --all-features --all-targets -- -D
warnings`: clean
- `cargo fmt --all --check`: clean
Per the AI usage policy: I used an AI assistant on this. The diagnosis,
the tests and the numbers above are all mine and I ran every command
quoted here.
## Summary
Fixes#26088
`NO_SIDE_EFFECTS` can either be applied to a function:
```rs
/* #__NO_SIDE_EFFECTS__ */ function foo() {}
```
Or be meaningless for its position, since it only works on function
declaration:
```rs
/* #__NO_SIDE_EFFECTS__ */ value;
```
But oxc could not distinguish these two cases.
This PR adds `CommentContent::NoSideEffectsNotApplied`, mirroring the
existing `PureNotApplied`.
---------
Co-authored-by: Cameron <cameron.clark@hey.com>
## Summary
- replace bare any rest parameter annotations with unknown[] when fixToUnknown is enabled
- preserve existing element-wise fixes for any[] and Array<any>
- add regression coverage for function declarations, arrow functions, call signatures, and method signatures reported in https://github.com/typescript-eslint/typescript-eslint/issues/12813
## Summary
- report text literals in JSX nested inside expression containers
- preserve upstream element override inheritance through nested JSX and fragments
- add regression coverage for logical, conditional, and plain JSX expressions
Fixes#26208
```less
// input
@a: /* c */ 1;
// before
@a: 1;
// after
@a: /* c */ 1;
```
Comment should be preserved.
```scss
// input
$x: // c
1;
// before
$x:
// c1;
// after
$x: // c
1;
```
Content should be preserved.
Aligned the handling of line comments immediately following an opening `{`.
(For GraphQL only,) previously kept their own line, now matches other languages by inserting a line break and treating them as leading comments for the next element.
This brings it in line with Prettier's style. The only drawback is that the behavior of `-line` style comments. However, since most of these can be replaced with `-next-line` style comments, we are going to standardize on this consistent behavior for now.
prettier/prettier#1267 (won't fix)
Keep these as-is.
```scss
foo: ("a",);
$x: "a",;
```
Even if they consist of a single element, these are still lists, and if the commas are removed, their type changes.
And also fixed inconsistent comment printing.
When the parser records a fatal error it fast-forwards the lexer to end of file. `re_lex_template_substitution_tail` did not check for that, so an incomplete template substitution would still re-lex the `}` and derive the token's start from a cursor which had already jumped to the end.
With tokens enabled, that fails the debug assertion in `finish_next_inner`, because the token being replaced is no longer the one on top of the collected token stream. Release builds silently overwrite an unrelated token instead. Tokens are only collected for Oxlint's JS plugins, which bail out if any parsing error, which is why this hadn't been noticed before.
Fix by adding the same `fatal_error` guard, added to other re-lex methods in #11023.
This does make a difference to diagnostics output for invalid code matching this pattern. TypeScript snapshot loses an "Unterminated string" diagnostic on 2 fixtures. Those errors came from the bogus re-lex reading past end of file. Both these fixtures already have an "Unexpected token" error. The additional "Unterminated string" errors were incorrect, and this fix has the side effect of removing them.
`Lexer::finalize_tokens` discards the last collected token - the `Eof` that terminates every token stream - and debug asserted that it was `Eof`.
Turns out that assumption does not always hold. Where the lexer cannot lex any further it emits `Kind::Undetermined`, and no `Eof` ever follows, so the last token is `Undetermined` and the debug assertion fails. This can be triggered by an unterminated string, for example:
```js
foo();
'unterminated
```
Like `Eof`, `Undetermined` is not a "real" token, so either way it's fine to remove it from the token stream. The fix is just to relax the debug assertion to accept either.
Release behaviour is unchanged - it was only a debug assert. Only came across this while running the conformance tests which compare current lexer to the new one (`oxc_lexer`).
## Summary
- recognize destructuring assignment targets when ordering reads and writes
- record the target write after the assignment right side and target defaults
- add array, object, and defaulted destructuring regression coverage
Fixes#25923 Closes https://github.com/oxc-project/oxc/pull/25924
Updates the minifier documentation to use supported validation commands and fixes broken relative links. Also refreshes the coverage task README so it no longer advertises removed suite selectors.
## What
`eslint/no-array-constructor`'s fixer replaced the call with `[...]`
unconditionally, so a `[` could land at the start of a line whose
predecessor has no semicolon:
```js
foo
Array()
```
`oxlint --fix` rewrote that to
```js
foo
[]
```
which is a member access on `foo` and does not parse — `Unexpected
token`. The fix is applied to the file, so the result is a source file
the user can no longer build.
This is not a hypothetical: the linter's own fixer harness catches it.
Adding `("foo\nArray()", …)` to the fix table on `main` fails with
```
Linter fixer produced invalid syntax.
Input code: foo\nArray()
Fixed code: foo\n[]
Parse errors: "Unexpected token"
```
The two cases at the bottom of that table were commented out with
*"TODO: These currently produce invalid syntax, need to fix the fixer."*
rather than fixed, so nothing in CI was exercising it.
## Why this shape
`ast_util::could_be_asi_hazard` already answers exactly this question,
and every other fixer that can emit a statement-initial `[` calls it:
| rule | uses helper |
|---|---|
| `unicorn/no-new-array` | ✅ |
| `unicorn/prefer-spread` | ✅ |
| `unicorn/no-negation-in-equality-check` | ✅ |
| `unicorn/prefer-response-static-json` | ✅ |
| `shared/no-negated-condition` | ✅ |
| **`eslint/no-array-constructor`** | ❌ — this PR |
I checked the other fixers that emit a leading `[` (`no-div-regex`,
`no-iterator`, `consistent-empty-array-spread`,
`no-single-promise-in-promise-methods`); none of them can be at
statement start, so this rule was the only gap. That also means no new
machinery — one call to the helper the codebase already trusts.
---
*Found by sweeping the linter for fixers that can emit a
statement-initial `[`, after noticing the `no-array-constructor` TODO.*
**AI disclosure:** this change was prepared with the assistance of
Claude Code. I reviewed the change and ran the verification above before
opening, and I stand behind them as my own.
*Branch note: based on an older `main` because the token used here lacks
the `workflow` scope needed to push newer upstream commits touching
`.github/workflows/` (the fork cannot be synced for the same reason).
`no_array_constructor.rs` is identical on both bases and
`could_be_asi_hazard` is unchanged, so it applies to current `main`
as-is — happy to rebase.*
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## 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()`
## Summary
- preserve plugin names when tracking default and enabled rules
- prevent same-named rules from inheriting another plugin’s state in Markdown and JSON output
- add regression coverage for all known rule-name collisions
Fixes#26209.
## Summary
- add the ESLint-compatible `checkConditionalExpressions` option to `no-unmodified-loop-condition`, defaulting to `false`
- port tests added by eslint/eslint#21175 and add regression coverage for repeated references within and across binary groups
Closes#26182.
## Summary
- compile Allocator::data_end_ptr only in supported fixed-size allocator builds
- remove the dead-code warning from coverage builds while preserving fixed-size pool usage
## Summary
- Return `OutOfRange` when a `StringCharAt` position cannot fit in `usize`, avoiding a panic on 32-bit targets.
- Cover the `u32` boundary in direct evaluation and minifier constant folding, alongside existing conversion and UTF-16 edge cases.