Commit Graph

1534 Commits

Author SHA1 Message Date
overlookmotel c966acaac5 fix(parser): do not omit < token opening type argument list (#26328)
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.
2026-09-04 12:08:49 +00:00
camc314 f6b75b4a6b test(parser): group annotation application cases (#26294)
## Summary

- group independent `PURE` and `NO_SIDE_EFFECTS` application checks
- clarify test names for unapplied annotation cases
2026-09-03 14:50:56 +00:00
碳苯 Carbon bfb4c57a5d feat(parser): distinguish unapplied no-side-effects comments (#26120)
## 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>
2026-09-03 11:14:24 +01:00
overlookmotel 3ab9bd122c fix(parser): do not re-lex template substitution tail after fatal error (#26230)
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.
2026-09-02 21:41:11 +00:00
overlookmotel 07851b9c5d fix(parser): fix debug assert failure when lexer error with tokens enabled (#26229)
`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`).
2026-09-02 21:41:10 +00:00
oxc-guard 894c8f9cd8 release(crates): oxc v0.148.0 (#26198)
### 💥 BREAKING CHANGES

- a31567a allocator: [**BREAKING**] Make `Allocator::cursor_ptr` and `data_end_ptr` private (#26081) (overlookmotel)

### 🚀 Features

- 784e9fa minifier: Invert `!0` and `!1` in place for boolean context to `1` and `0` (#26050) (Armano)
- 1e902cc minifier: Expand fold leading assignments into the var decl (#26142) (Armano)
- 3ed4f6a minifier: Expand de morgan's optimization to allow move of `!` (#25930) (Armano)
- 5672585 parser: Attach all comments to nodes (#25944) (camc314)

### 🐛 Bug Fixes

- 4adfb4c semantic: Validate chained continue labels (#26157) (camc314)
- b874f48 ecmascript: `Math.round` only exact half ties (#26150) (camc314)
- a625378 minifier: Coerce omitted `indexOf` search argument (#26149) (camc314)
- dc7398b ecmascript: Trim trailing whitespace in string to number (#26148) (camc314)
- 243b685 transformer/object-rest: Lower multiple declarators correctly (#26147) (camc314)
- dc09a3a parser: Avoid panic on escaped string export names (#26146) (camc314)
- e412cf2 linter: Clamp invalid JS plugin locations (#26144) (camc314)
- c3dedc9 semantic: Skip function body bindings in parameters (#26099) (Dunqing)
- e74de61 transform-react: Match Babel diagnostic reporting (#26128) (Boshen)
- d5163d0 parser: Correctly classify unapplied pure annotations (#26084) (camc314)
- 8531b9b linter/plugins: Alter method for obtaining mutable `Program` when sending AST to JS plugins (#26077) (overlookmotel)
- 9b51658 regular_expression: Allow oversized decimal escape for Annex B (#26070) (leaysgur)
- c8de4df regular_expression: Reject oversized backreferences (#26055) (camc314)

### ⚡ Performance

- ac4785a diagnostics: Use fixed ANSI styles (#26130) (Boshen)
- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)
- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)
- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)
- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)
2026-09-01 08:15:07 +00:00
camc314 dc09a3aa9d fix(parser): avoid panic on escaped string export names (#26146)
## Summary

Fix a parser panic when a string-named export decodes to a literal `\u` sequence.

Lone-surrogate validation now uses the metadata captured by the lexer instead of rescanning decoded string contents. This also prevents false diagnostics for names such as `"\\uD800"`.

## Before:

```

thread 'main' (10990283) panicked at crates/oxc_ast/src/ast_impl/literal.rs:89:42:
end byte index 4 is out of bounds for string of length 0
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::str::slice_error_fail_rt
   3: core::str::slice_error_fail
   4: <core::ops::range::RangeTo<usize> as core::slice::index::SliceIndex<str>>::index
   5: <str as core::ops::index::Index<core::ops::range::RangeTo<usize>>>::index
   6: <oxc_ast::ast::literal::StringLiteral>::is_string_well_formed_unicode
   7: <oxc_parser::ParserImpl<oxc_parser::config::NoTokensParserConfig>>::parse_module_export_name
```
2026-08-28 13:37:48 +00:00
camc314 d5163d0faa fix(parser): correctly classify unapplied pure annotations (#26084)
Mark all `#__PURE__` annotations not directly applied to a `NewExpression` or CallExpression` as `PureNotApplied`.

This changes the approach by letting pure annotations start of their life as `CommentContent::PureNotApplied`, and only changing to `CommentContent::Pure` once they have successfully been applied to a node.

This aligns the behaviour with Rollup.

Rollup Playground:

https://rollupjs.org/repl/?version=4.63.0&shareable=eyJleGFtcGxlIjpudWxsLCJtb2R1bGVzIjpbeyJjb2RlIjoidmFyIGZvbyAvKiAjX19QVVJFX18gKi8gPSBwdXJlT3BlcmF0aW9uKCk7XG5mb28gLyogI19fUFVSRV9fICovID0gcHVyZU9wZXJhdGlvbigpO1xuZm9vIC8qICNfX1BVUkVfXyAqLyArIHB1cmVPcGVyYXRpb24oKTtcbmZvbyAvKiAjX19QVVJFX18gKi8gJiYgcHVyZU9wZXJhdGlvbigpOyIsImlzRW50cnkiOnRydWUsIm5hbWUiOiJtYWluLmpzIn1dLCJvcHRpb25zIjp7fX0=

Rolldown Playground: (only the 1st pure comment is marked as not applied).

https://repl.rolldown.rs/#eNqlkU1LxDAQhv/KEGG/LO294kk8K4K3QEnbyW60nYR0uu5S+t9NUlwRPQgLgczHOy/zMJPQopyEoRZP+dsQYxLld56JJqRH5UFbC8UObqrq+fXlsapgV8A9uNHjk0Ov2FjabO+kl3Sd8vbfytXqlzTsi6JkP+KcCW+7rrUflDeWtNnnfMH7o7OAmt5ZzzBBi9oQPqQ2zKC97WH9NbaWJAlPSRqEauzSfxnYTJIADLmRS1gs8x5Z5amUQVEAHxC06XCAATtsGFuoz6laj8yWIDxUzQFY1dEsjFgXIYeY2ZGT9wS9IaPPJURkmCXNW0kisDvVvKs9hhPaALxQ/6gtvGlRKVp0GC5OjcFBiuAbnaJRcDoGWacYBxbzJ8uvunY=

fixes https://github.com/oxc-project/oxc/issues/23669
closes https://github.com/oxc-project/oxc/pull/24161
2026-08-26 10:33:48 +00:00
camc314 5672585a34 feat(parser): attach all comments to nodes (#25944)
## Summary

Comments are now attached to their adjacent tokens using source boundaries.

A leading comment belongs to the token that follows it:

```js
/* leading */ value
              ^ attached_to
```

A trailing comment belongs to the token that precedes it:

```js
value /* trailing */
     ^ attached_to
```

`Comment::attached_to` now consistently represents either:

- the start of the following token for leading comments;
- the end of the preceding token for trailing comments.
2026-08-25 09:38:36 +00:00
oxc-guard 4e258430cd release(crates): oxc v0.147.0 (#26046)
### 🚀 Features

- b6e9e5e minifier: Fold switch stmt whose parent is not block stmt (#25403) (Armano)

### 🐛 Bug Fixes

- 8a9bdbd estree: Include decorators in `FormalParameterRest ` spans (#26021) (camc314)
- 6f0c7cf minifier: Avoid merging `if` to `for` in sloppy mode when containing function declaration (#25638) (sapphi-red)
- 2cde1f6 rust: Address nightly deprecations (#25998) (Boshen)
- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)
- 2dad1e0 parser: Track irregular line terminators in trivia (#25947) (camc314)
- 58ba651 minifier: Avoid merging `if` to `for` when the body contains a function declaration (#25637) (sapphi-red)
- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)
- af82a07 minifier: Keep unary `+` when the other operand has side effects (#25645) (Kotaro Chikuba)

### ⚡ Performance

- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)
- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)
- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)
- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)
- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)
- fe444cc codegen: Avoid allocations for single comment anchors (#25598) (camc314)
- a0aee81 minifier: Do not rebuild expression when substituting void to null (#25964) (Armano)
- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)
- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)
- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)
- f09c8ab minifier: Avoid creation of unnecessary conditional expressions (#25931) (Armano)
- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)

### 📚 Documentation

- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)
- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)
2026-08-24 11:19:12 +00:00
camc314 2dad1e0ec7 fix(parser): track irregular line terminators in trivia (#25947)
## Summary

- notify `TriviaBuilder` when the lexer consumes U+2028 or U+2029
- keep Annex B HTML-close comments after irregular line terminators attached as leading trivia
- add a script-mode regression test covering both Unicode separators
2026-08-20 18:46:29 +00:00
renovate[bot] d5d575c1de chore(deps): update rust to v1.98.0 (#25945)
This PR contains the following updates:

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

---

### Configuration

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

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

🚦 **Automerge**: Enabled.

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

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

---

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

---

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

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

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-20 18:04:07 +00:00
oxc-guard 0389c4010e release(crates): oxc v0.146.0 (#25912)
### 🚀 Features

- 83cb516 minifier: Improve negation of expressions in boolean context (#25755) (Armano)

### 🐛 Bug Fixes

- 134fb22 isolated-declarations: Preserve unresolved parameter types (#25909) (Dunqing)
- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)
- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)

### ⚡ Performance

- 5ef0c93 minifier: Unwrap all unary exprs without creating dummy expressions (#25891) (Armano)
- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)
- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)
- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)
- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)
- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)
- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)
- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)

### 📚 Documentation

- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)
2026-08-19 08:29:51 +00:00
oxc-guard[bot] 4a4a1fb2e7 release(crates): oxc v0.145.0 (#25865)
### 💥 BREAKING CHANGES

- 365274e packages/codegen: [**BREAKING**] `printSync` return an object
(#25720) (overlookmotel)

### 🚀 Features

- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename`
option (#25854) (overlookmotel)
- 300763f packages/codegen: Add full source map support (#25585)
(camc314)
- a169e4a napi/minify: Expose property name mangling options (#24741)
(Dunqing)
- 2f5cdb1 minifier: Add property name mangling (#24740) (Dunqing)
- 4922141 mangler: Deduplicate private accessor names (#25601) (camc314)
- 1c4f519 napi: Add Relay transform plugin (#25503) (Boshen)
- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)

### 🐛 Bug Fixes

- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)
- 345f981 react_compiler: Skip node_modules by default (#25859) (Boshen)
- ab81f3f minifier: Drop side-effect-free additions (#25639) (Dunqing)
- 241c559 napi/minify: Accept RegExp for property filters (#25827)
(Dunqing)
- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)
- 53f9295 isolated-declarations: Preserve undefined for defaulted any
(#25292) (camc314)
- 8ab883a codegen: Preserve property key annotations (#25766) (Dunqing)
- b13fd48 minifier: Model uninitialized module vars as undefined
(#25497) (Dunqing)
- f6000ac ecmascript: Fold `**` with `Number::exponentiate`, not IEEE
`pow` (#25644) (Kotaro Chikuba)
- b846abc isolated-declarations: Handle ambient expando properties
(#25655) (camc314)
- dec0a86 react-compiler: Standardize diagnostics (#25702) (Boshen)
- fca2e0c parser: Reject initialized lexical declarations in for-in
(#25700) (Boshen)
- ae9be8f parser: Forbid type parameters on quoted constructors (#25696)
(Boshen)
- c5f188a react-compiler: Use Babel v1 validation defaults (#25676)
(Boshen)
- db44651 napi: Disable reuseWorker in browser bindings (#25640)
(leaysgur)
- ce35d47 react_compiler: Preserve JSX import source pragmas (#25592)
(Boshen)
- 059784d semantic: Respect shadowed `Infinity` and `NaN` in enum
evaluation (#25604) (camc314)
- bb5a232 minifier: Keep variable declaration initilized with class when
keepNames is enabled (#25584) (sapphi-red)
- f49229d minifier: Keep side effects when rotating bitwise operands
(#25596) (Kotaro Chikuba)
- e82495b ecmascript: Derive `ToNumber` of `!x` from `ToBoolean`
(#25595) (Kotaro Chikuba)
- 509931b semantic: Classify global references per identifier (#25608)
(camc314)
- 80484ce mangler: Correct base54 safety comment (#25606) (camc314)
- c1369a7 codegen: Resolve private names in class heritage (#25588)
(camc314)
- c002f29 codegen: Escape sources for empty import specifiers (#25586)
(camc314)
- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582)
(camc314)
- cdf1846 semantic: Allow legacy escapes in JSX attributes (#25576)
(Boshen)
- e75e102 minifier: Preserve block statement in labeled iteration
statements (#25162) (Armano)
- 771d79a mangler: Exclude non-manglable symbols from slot assignment
(#25539) (sapphi-red)
- 76b19f5 minifier: Avoid duplicating large folded strings (#25532)
(Dunqing)
- 59e8895 codegen: Validate starting indent level (#25550) (camc314)
- 88b34f2 clippy: Remove unneeded `unsafe` (#25551) (camc314)
- 23a7ad0 parser: Stop delimited lists at end of file (#25542) (Boshen)
- 73acba9 parser: Preserve fatal errors during await reparse (#25541)
(Boshen)
- c3e99d1 minifier: Avoid invalid octal escapes in template folds
(#25495) (Dunqing)
- b4e6a9e codegen: Output newline after `export default interface`
(#25487) (overlookmotel)
- 5fcf683 minifier: Correct issue with try finally termination (#25185)
(Armano)
- 1645d93 react_compiler: Preserve source spans (#25462) (Boshen)
- 8d7f9cf react_compiler: Honor eslint suppressions (#25394) (Boshen)

### ⚡ Performance

- 568203e ecmascript: Use binary search for known globals (#25817)
(Boshen)
- c84ede3 estree_tokens: Share JS token update entry point (#25826)
(Boshen)
- 9a6e862 minifier: Move owned statements directly (#25835) (Dunqing)
- 61b2aef minifier: Move owned AST nodes directly (#25837) (Dunqing)
- 673b04b minifier: Replace expressions without take_in dummies (#25836)
(Dunqing)
- 63ff8ef linter: Outline diagnostic construction (#25762) (Boshen)
- 621808e diagnostics: Measure graphemes lazily (#25723) (Boshen)
- f62ed0e diagnostics: Optimize graphical number rendering (#25715)
(Boshen)
- 7152834 diagnostics: Reduce graphical formatting overhead (#25714)
(Boshen)
- 10fc4b7 diagnostics: Reduce graphical rendering allocations (#25711)
(Boshen)
- 9c8abab diagnostics: Batch graphical reports (#25710) (Boshen)
- 757f3d4 mangler: Share allocated names across reused slots (#25605)
(camc314)
- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)

### 📚 Documentation

- 58f7ab9 packages/codegen: Reformat docs and comments (#25848)
(overlookmotel)
- 627466e transform-react: Document all options (#25800) (Boshen)
- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722)
(overlookmotel)
- 386a699 packages/codegen: Correct JSDoc comment (#25716)
(overlookmotel)
- 31e571d mangler: Update code example (#25599) (camc314)
- fd62354 codegen: Revamp package readme (#25560) (camc314)
- ffa3153 codegen: Clarify raw transfer Node requirement (#25565)
(camc314)
- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)
- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-08-18 22:26:40 +08:00
Dunqing 8ab883afaa fix(codegen): preserve property key annotations (#25766)
`@__KEY__` and `#__KEY__` are currently treated as normal comments. Codegen can drop them before a later property-mangling pass. The property may then be renamed while the annotated string keeps its old name.

This classifies the marker as a property-key annotation. Codegen keeps it next to string and no-substitution template literals when annotation comments are enabled, and the property mangler reads that classification instead of scanning source text. Final output still follows the normal comment options, so whitespace-minified or annotation-disabled output removes the marker.

## Example

Input:

```js
const object = { _field: 1 };
const key = /* #__KEY__ */ "_field";
object[key];
```

Before, an intermediate codegen pass could remove the marker:

```js
const object = { _field: 1 };
const key = "_field";
object[key];
```

After, the marker survives for the later property-mangling pass.

Verified with the affected crate suites, `just minsize`, workspace lint, docs, AST generation, and independent review. `just ready` is only blocked locally by existing Node-version and terminal-color snapshot differences.

Implemented with AI assistance (OpenAI Codex). I reviewed and understand the changes and remain responsible for this contribution under the repository AI usage policy.
2026-08-17 09:23:04 +00:00
Ulrich Stark c1f7d7d1c2 refactor(parser): remove dead match options in get_modifier (#25725)
`cur_kind` can never be `Kind::Get` or `Kind::Set` because then the
`ModifierKind::try_from(cur_kind).ok()?` line above would have returned
earlier.
2026-08-17 09:33:10 +01:00
Boshen fca2e0cfed fix(parser): reject initialized lexical declarations in for-in (#25700)
Rejects initialized `let` and `const` declarations in `for...in` during parsing while preserving Annex B's non-strict `var` form.

Adds misc coverage for valid and invalid initializer forms.

Verified with `just ready` and parser conformance.

Closes #25149.

AI-assisted.
2026-08-15 06:58:08 +00:00
Boshen ae9be8f8bd fix(parser): forbid type parameters on quoted constructors (#25696)
Quoted constructor methods bypassed the AST-based constructor validation. Validate type parameters after resolving the method key, preserving the AST shape.

Validation: `just ready`, parser conformance, and allocation checks.

Closes #25692.

AI-assisted with Codex.
2026-08-15 05:02:23 +00:00
Boshen 23a7ad096a fix(parser): stop delimited lists at end of file (#25542)
Stop parsing delimited-list elements after a separator when EOF or an undetermined token follows, preventing malformed tuple syntax from creating reversed spans during error recovery.

Validated with `cargo coverage -- parser --filter oxc-23670 --detail`.

AI assistance was used to prepare this change.

Closes #23670.
2026-08-12 13:58:51 +00:00
Boshen 73acba93fb fix(parser): preserve fatal errors during await reparse (#25541)
Fixes #23681.

Preserve initial fatal parser errors during top-level `await` reparsing, preventing malformed recovery AST nodes from reaching Oxlint.

Validated with parser unit/conformance suites and allocation snapshots. `just ready` only fails three unrelated ANSI snapshot checks in the current environment.

AI-assisted.
2026-08-12 11:25:27 +00:00
oxc-guard b6f1458dec release(crates): oxc v0.144.0 (#25474)
### 💥 BREAKING CHANGES

- a33788e ast: [**BREAKING**] Group class heritage into `ClassHeritage` (#25360) (camc314)
- 5c5cdcd ast: [**BREAKING**] Narrow `TSInterfaceHeritage::expression` to TSTypeName (#24360) (camc314)
- 6be314f ast: [**BREAKING**] Remove duplicated `VariableDeclarator::kind` (#25319) (camc314)
- 44fd320 ast: [**BREAKING**] Split TS external modules & Namespace Declarations (#25284) (camc314)

### 🚀 Features

- 9a37c41 napi: Restructure React transform options (#25326) (Boshen)
- b3c1b30 allocator: Add `ArenaVec::push_mut` (#25338) (overlookmotel)

### 🐛 Bug Fixes

- bbd6ed3 codegen: Wrap string literal in parens after directives (#25435) (overlookmotel)
- d07a293 codegen: Preserve cast parentheses in conditional tests (#25115) (camc314)
- 8b7d441 codegen: Preserve satisfies parentheses before bitwise operators (#25113) (camc314)
- e0fb2d7 minifier: Avoid unsafe return removal in labeled blocks (#25406) (Armano)
- c49f4ce parser: Preserve terminal license comments (#25400) (camc314)
- 9d19ab3 transform_react: Downgrade nonfatal compiler diagnostics (#25418) (Boshen)
- 937825c react_compiler: Disable exhaustive memo validation by default (#25417) (Boshen)
- 12937b3 parser: Reject comma expressions in class fields (#25385) (Cameron)
- 7d8a9b5 react_compiler: Preserve hook spans for React Refresh (#25393) (Boshen)
- 9e93ba6 parser: Reset auto-accessor initializer context (#25384) (camc314)
- cc2e28d parser: Reject trailing tokens in `Parser::parse_expression` (#25371) (camc314)
- 697c629 minifier: Preserve Annex B labeled-function bindings in implicit-return rewrite (#25349) (Armano)
- 4d723ca parser: Report TS1035 for quoted module names (#25305) (camc314)
- 0c9b980 semantic: Report module declaration diagnostics (#25285) (camc314)
- 2a1058a napi: Omit parser header from WASI declarations (#25254) (camc314)

### ⚡ Performance

- dd1430b semantic: Avoid allocation in class overload check (#25440) (camc314)
- e420816 parser: Optimize trailing comma tracking (#25376) (camc314)
- 6529d1a minifier: Short-circuit scope check when alternate already determines result (#25318) (Armano)

### 📚 Documentation

- fe111eb allocator: Improve doc comments for `Arena` methods (#24922) (overlookmotel)
2026-08-10 09:57:10 +00:00
Boshen 030857b509 refactor(diagnostics): update oxc-miette to v4 (#25395)
Remove the miette `Report` wrapper and update oxc-miette to v4's narrowed diagnostic and source protocol.

AI-assisted by OpenAI Codex.
2026-08-10 07:48:02 +00:00
camc314 c49f4ce415 fix(parser): preserve terminal license comments (#25400)
## Summary

- Preserve legal comments whose `@license` marker ends exactly at comment-content EOF.
- Add parser and minifier regression coverage.

## Details

The optimized marker scan excluded the final valid start for the eight-byte `@license` marker. Comments such as `/*x@license*/` were parsed but classified as ordinary comments and could therefore be dropped from legal-comment output or extraction.

The parser hot path now includes that final candidate while retaining unchecked reads, with an explicit remaining-length guard before reading the nine-byte `@preserve` marker.

## Ecosystem compatibility

This makes Oxc recognize terminal markers such as `/*x@license*/` and `//x@license`, matching tested behavior in esbuild 0.28.1 and SWC 1.15.47 when their legal/`"some"` comment retention is enabled.
2026-08-09 09:18:05 +00:00
Cameron 12937b3201 fix(parser): reject comma expressions in class fields (#25385)
## Summary

- parse ordinary class field initializers as `AssignmentExpression`
- reject unparenthesized comma expressions while preserving
parenthesized sequence expressions
- add paired misc conformance fixtures and update the generated
snapshots

## Root cause

Ordinary class field initializers used `parse_expr`, which accepts a
top-level comma and constructs a sequence expression. The ECMAScript
`Initializer` grammar requires an `AssignmentExpression`, so the
unparenthesized comma must remain for the class-element terminator check
to reject.
2026-08-08 10:59:23 +00:00
camc314 9e93ba6908 fix(parser): reset auto-accessor initializer context (#25384)
## Summary

- Align auto-accessor initializer parsing with class-field initializer grammar.
- Add regressions for `in` in a `for` initializer and disallowed inherited `await`/`yield` contexts.

## Root cause

The auto-accessor path only enabled `new.target`. It therefore inherited outer `~In`, `Await`, and `Yield` grammar contexts: `a in b` could not parse in a `for` initializer, while `await` and `yield` could incorrectly parse from enclosing async/generator functions. The fix applies the same `In | NewTarget` addition and `Yield | Await` removal already used for class fields, while retaining AssignmentExpression parsing.

## References

- [ECMAScript `FieldDefinition` grammar](https://tc39.es/ecma262/#prod-FieldDefinition)
- [ECMAScript `Initializer` grammar](https://tc39.es/ecma262/#prod-Initializer)
- [TC39 decorators proposal: class auto-accessors](https://github.com/tc39/proposal-decorators#introduction)
2026-08-07 23:03:24 +00:00
camc314 e1659205c9 refactor(parser): remove unused byte match table (#25370)
## Summary

- remove the unused `ByteMatchTable` type, implementation, and `byte_match_table!` macro from the parser lexer search module
- update the surrounding module and macro documentation to describe the remaining safe byte-search API

This dead code was added ~ 2 yrs ago (https://github.com/oxc-project/oxc/commit/d3a59f27f7d8ba925146fda992fda868661a7954) and was already marked for removal by a TODO and had no live references. Removing it reduces parser maintenance surface and eliminates the unused unsafe search path.
2026-08-07 21:42:03 +00:00
camc314 e4208164e1 perf(parser): optimize trailing comma tracking (#25376)
## Summary

- Track trailing-comma spans only for array and object literals ending in a spread.
- Consume the stored span while covering a rest assignment target.

## Rationale

Avoids parser-state map work for ordinary trailing commas while retaining constant-time diagnostics for invalid rest-target trailing commas.
2026-08-07 21:30:28 +00:00
camc314 cc2e28d3e3 fix(parser): reject trailing tokens in Parser::parse_expression (#25371)
## Summary

`ReplaceGlobalDefinesConfig` validates replacement values with `Parser::parse_expression`, which previously accepted a valid prefix and ignored the remaining tokens. Require the parser to consume the whole input so invalid values such as `console.log foo` are rejected instead of being truncated.

Fixes #25369
2026-08-07 21:04:52 +00:00
camc314 a33788e13e feat(ast)!: group class heritage into ClassHeritage (#25360)
## Summary

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

## Breaking Change

The Rust AST changes from:

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

to:

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

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

## Why?

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

```text
ClassHeritage : extends LeftHandSideExpression
```

TypeScript additionally permits type arguments on the heritage expression:

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

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

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

This allowed an invalid state to be constructed:

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

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

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

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

## Class heritage forms

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

```js
class Base {}
```

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

A class extending an expression has heritage without type arguments:

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

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

TypeScript type arguments belong to the same heritage structure:

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

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

The heritage expression may be any valid class heritage expression:

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

## How to migrate

Code that previously accessed the superclass expression directly:

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

can use the compatibility accessor:

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

Code that needs both parts should access the grouped heritage:

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

Mutating consumers should update the nested fields:

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

Common migrations:

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

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

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

fixes https://github.com/oxc-project/backlog/issues/216
2026-08-07 14:18:59 +00:00
Ulrich Stark 702fdbc69f refactor(parser): simplify is_start_of_function_type_or_constructor_type (#25352)
15 less lines and a slightly cheaper lookahead for the Kind::Abstract
match arm.

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-07 10:54:40 +01:00
camc314 c3918302a8 refactor(parser): check multiple for-loop declarations (#25344)
Moves the validation that rejects multiple declarations in `for...in` and `for...of` loop heads from semantic analysis into the parser.

Part of #25149.
2026-08-07 08:35:21 +00:00
camc314 5c5cdcd522 feat(ast)!: narrow TSInterfaceHeritage::expression to TSTypeName (#24360)
## Summary

Narrow `TSInterfaceHeritage` from a general JavaScript `Expression` to `TSTypeName`.

```rust
pub struct TSInterfaceHeritage<'a> {
    pub type_name: TSTypeName<'a>,
    pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```

## Breaking Change

The Rust AST field changes from:

```rust
expression: Expression<'a>
```

to:

```rust
type_name: TSTypeName<'a>
```

## Why?

The current AST can represent shapes such as:

```ts
interface A extends foo() {}
interface A extends A + B {}
interface A extends new Foo() {}
interface A extends true {}
```

Which is an overly wide type since all of the above variants are invalid.

This PR tightens up the AST, making these invalid variants irrepresentable.

## How to migrate?

This should be a trivial migration:
1. instead of accessing `expression` on `TSInterfaceHeritage`, now access `type_name`
2. change any pattern matching on the `type_name` field to use `TypeName` (this is a smaller subset so should allow deleting code!)

closes https://github.com/oxc-project/backlog/issues/215
2026-08-07 06:00:42 +00:00
camc314 6be314f226 feat(ast)!: remove duplicated VariableDeclarator::kind (#25319)
## Summary

- Removes `VariableDeclarator::kind`.
- Makes `VariableDeclaration::kind` the single source of truth for every declarator in its declaration list.
- Makes contradictory parent and child declaration kinds unrepresentable.

## Breaking Change

The Rust AST changes from:

```rust
pub struct VariableDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub kind: VariableDeclarationKind,
    pub declarations: Vec<'a, VariableDeclarator<'a>>,
    #[ts]
    pub declare: bool,
}

pub struct VariableDeclarator<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    #[estree(skip)]
    pub kind: VariableDeclarationKind,
    #[estree(via = VariableDeclaratorId)]
    pub id: BindingPattern<'a>,
    #[ts]
    #[estree(skip)]
    pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
    pub init: Option<Expression<'a>>,
    #[ts]
    pub definite: bool,
}
```

to:

```rust
pub struct VariableDeclaration<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    pub kind: VariableDeclarationKind,
    pub declarations: Vec<'a, VariableDeclarator<'a>>,
    #[ts]
    pub declare: bool,
}

pub struct VariableDeclarator<'a> {
    pub node_id: Cell<NodeId>,
    pub span: Span,
    #[estree(via = VariableDeclaratorId)]
    pub id: BindingPattern<'a>,
    #[ts]
    #[estree(skip)]
    pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
    pub init: Option<Expression<'a>>,
    #[ts]
    pub definite: bool,
}
```

## Why?

A variable declaration has one declaration kind shared by every declarator in its declaration list:

```js
const first = 1, second = 2;
```

Previously, the AST stored that kind on both the parent declaration and every child declarator:

```text
VariableDeclaration {
    kind: Const,
    declarations: [
        VariableDeclarator { kind: Const, ... },
        VariableDeclarator { kind: Const, ... },
    ],
}
```

This allowed contradictory ASTs to be constructed:

```text
VariableDeclaration {
    kind: Const,
    declarations: [
        VariableDeclarator { kind: Var, ... },
    ],
}
```

Different consumers read different copies:

- Code generation used `VariableDeclaration::kind`.
- Semantic binding used `VariableDeclarator::kind`.
- Lint rules, transforms, and minifier passes used a mixture of both.

As a result, one AST could be emitted as `const` while its binding was treated as function-scoped `var`.

Transforms also had to keep the copies synchronized manually. For example, const-to-let normalization updated the parent and then looped through every declarator to update each child. Explicit resource management performed similar paired mutations for `using` declarations.

Keeping the kind only on `VariableDeclaration` removes this synchronization requirement and prevents consumers from observing conflicting declaration semantics.

## Declaration forms

The declaration kind now belongs exclusively to the node that owns the declaration list:

```js
let value;
```

```text
VariableDeclaration {
    kind: Let,
    declarations: [
        VariableDeclarator { id: value, ... },
    ],
}
```

Multiple declarators share the same parent kind:

```js
const first = 1, second = 2;
```

```text
VariableDeclaration {
    kind: Const,
    declarations: [
        VariableDeclarator { id: first, ... },
        VariableDeclarator { id: second, ... },
    ],
}
```

## How to migrate

Code that previously read the kind from a declarator:

```rust
if declarator.kind.is_const() {
    // ...
}
```

should now read it from the containing declaration:

```rust
if declaration.kind.is_const() {
    // ...
}
```

Consumers operating through an AST node store should retrieve the parent `VariableDeclaration`:

```rust
let AstKind::VariableDeclaration(declaration) =
    nodes.parent_kind(declarator.node_id())
else {
    unreachable!();
};

let kind = declaration.kind;
```

Common migrations:

| Previous representation | New representation |
| --- | --- |
| `declarator.kind` | Containing `declaration.kind` |
| Match `VariableDeclarator::kind` during semantic binding | Read the parent `VariableDeclaration` from ancestry |
| Pass `kind` to `VariableDeclarator::new` | Remove the `kind` argument |
| Update parent and every child kind | Update only `VariableDeclaration::kind` |
| Recover kind after detaching a declarator | Capture or pass the parent kind before detaching it |
| Inspect a symbol’s declarator kind in lint rules | Resolve its parent declaration through the node store |

When constructing declarators, use the generated kindless `VariableDeclarator` builders and place them inside a `VariableDeclaration` carrying the required kind.

fixes https://github.com/oxc-project/backlog/issues/237
2026-08-05 16:31:46 +00:00
camc314 44fd320324 feat(ast)!: split TS external modules & Namespace Declarations (#25284)
## Summary

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

## Breaking Change

The Rust AST changes from:

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

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

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

to:

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

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

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

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

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

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

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

## Why?

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

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

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

The shared representation allowed invalid states such as:

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

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

The two declaration families have stronger independent invariants:

### External modules

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

### Namespace declarations

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

Dedicated nodes encode these differences directly.

## Declaration forms

External modules use `TSExternalModuleDeclaration`:

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

Identifier-based declarations use `TSNamespaceDeclaration`:

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

Dotted namespaces are represented as nested namespace declarations:

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

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

Only `TSExternalModuleDeclaration::body` is optional.

## ESTree compatibility

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

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

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

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

## How to migrate

Code that previously matched all forms through one variant:

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

should now match the required declaration family:

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

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

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

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

Common field migrations:

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

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

fixes https://github.com/oxc-project/backlog/issues/218
2026-08-05 14:37:35 +00:00
camc314 4d723caaff fix(parser): report TS1035 for quoted module names (#25305)
Quoted module names are only valid in ambient contexts in TypeScript, but we were accepting them in regular TypeScript files.

This reports TS1035 when a quoted module name appears outside a declaration file or ambient declaration. The conformance snapshots now cover both TypeScript cases, and the old invalid codegen fixture has been replaced with a valid internal module declaration.
2026-08-05 10:21:52 +00:00
overlookmotel dae01e1967 refactor(parser): rename more vars for span starts (#25264)
Pure refactor. Continuation of #25262.

Rename vars in parser called `*_span` to `*_start` where the var is a `u32` representing the _start_ of a span, rather than a whole `Span`.
2026-08-03 21:46:58 +00:00
overlookmotel baefdcf49d refactor(parser): rename cur_start method (#25263)
Pure refactor. Continuation of #25262.

Rename `ParserImpl::start_span` method to `cur_start`.

This name matches `cur_token`, `cur_kind`, `cur_src` etc. It more clearly describes what it does - returns the current span start as a `u32`, rather than the old name which suggested that it returned a `Span`.
2026-08-03 21:46:58 +00:00
overlookmotel 7ea5d4b530 refactor(parser): rename vars for span starts (#25262)
Pure refactor.

Rename vars which contain a `u32` for start of a span to `start`.

Previously there were vars named variously `span` or `start_span` - both of which imply the var contains a `Span`, not the `u32` representing the _start_ of a span. Other vars which _do_ contain a `Span` were also named `span` or `start_span`. It was confusing.

After this PR:

* All vars called `start` are a `u32` representing the start of a span.
* All vars called `span` are an actual `Span`.
2026-08-03 21:46:57 +00:00
oxc-guard 45a17c25d1 release(crates): oxc v0.143.0 (#25250)
### 💥 BREAKING CHANGES

- 4120da0 semantic: [**BREAKING**] Return borrowed JSDoc so the parse cache works (#25186) (Connor Shea)
- f0a41c8 allocator: [**BREAKING**] Remove `ArenaBox::dangling` method (#25236) (overlookmotel)
- 067da8c ast: [**BREAKING**] Store single parameter in `TSIndexSignature::parameter` (#25154) (camc314)
- a0c7788 ast: [**BREAKING**] Change `TSIndexSignatureName::name` to `Ident` (#25150) (camc314)
- 1bdedd1 ast: [**BREAKING**] Introduce `ExportDeclaration`, `ExportFromDeclaration` (#25095) (camc314)
- c917f20 ast: [**BREAKING**] Introduce `ArrowFunctionBody` enum (#24987) (camc314)
- 7e1199c ast: [**BREAKING**] Remove conversion to `Box` from AST builder methods (#25038) (overlookmotel)

### 🚀 Features

- 3c8312e allocator: Make `Box` `Send` + `Sync` when the type it contains is (#25242) (overlookmotel)
- cb5bc08 ast: Derive `GetSpan` for `Comment` (#25147) (leaysgur)
- 8541e18 ast_tools: Generate minifier scope collector (#25088) (camc314)
- 5acdae2 ecmascript: Skip transparent TS expressions (#25072) (camc314)
- 85d0f4d napi: Add React Compiler transform binding (#24934) (Boshen)
- 70d6ece minifier: Fold safe integer exponentiation (#25009) (Dunqing)
- 73fc747 minifier: Enable termination statement optimization for do while (#24921) (Armano)
- 5eadfdb minifier: Fold safe integer division and remainder (#24981) (Dunqing)

### 🐛 Bug Fixes

- 6c9ee14 minifier: Preserve if-chain idempotency and correct side effects when alternate should not be moved (#25041) (Armano)
- 8a47ff3 ecmascript: Do not treat lone-surrogate strings as constants (#25084) (Dunqing)
- 468b970 isolated-declarations: Strip re-export attributes (#25090) (camc314)
- a70035d parser: Report export imports in namespaces (#25086) (camc314)
- 5d8ccf5 react_compiler: Preserve nonfatal diagnostics (#25066) (Boshen)
- d349e4a react_compiler: Match eslint suppression gating (#25068) (Boshen)
- 0dc16ba react_compiler: Track catch block declarations (#25067) (Boshen)
- e6734a5 minifier: Fold typeof comparisons with known object strings (#25017) (Dunqing)
- 3a9d5a5 minifier: Keep `typeof foo == ['object']` as-is (#24941) (sapphi-red)
- 10ec276 ast: Custom AST builder methods take `IntoIn<ArenaBox>` where child method does (#25033) (overlookmotel)
- fbe8d31 minifier: Respect targets when folding RegExp source (#24790) (Dunqing)

### ⚡ Performance

- fb402b0 minifier: Stop re-deriving printed numeric spellings (#25103) (Dunqing)
- 7251e52 react_compiler: Avoid large types on stack (#25037) (overlookmotel)
- e07718f isolated_declarations: Avoid large types on stack (#25036) (overlookmotel)
- 9fb1e3e transformer: Avoid large types on stack (#25035) (overlookmotel)
- 8b80f8b parser: Avoid large types on stack (#25034) (overlookmotel)

### 📚 Documentation

- 2ea0aa9 ast: Correct grammar in AST builder methods doc comments (#25173) (overlookmotel)
2026-08-03 11:07:37 +00:00
connorshea 5e5178bd05 chore(parser): Fix a typo in the Namespace keyword string. (#25184)
I don't think this matters as I'm unclear where this is even used (no snapshots are updated by this change), but we may as well fix it.
2026-08-02 08:39:00 +00:00
camc314 067da8c4e7 feat(ast)!: store single parameter in TSIndexSignature::parameter (#25154) 2026-07-31 20:35:37 +00:00
camc314 1bdedd11ca feat(ast)!: introduce ExportDeclaration, ExportFromDeclaration (#25095)
## Summary

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

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

## Breaking Change

The Rust AST changes from:

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

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

to:

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

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

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

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

## Why?

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

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

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

## How to migrate

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

Common field migrations:

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

closes https://github.com/oxc-project/backlog/issues/219
2026-07-31 10:25:47 +00:00
camc314 a70035d743 fix(parser): report export imports in namespaces (#25086)
## Summary

- report TS(1147) for exported TypeScript import-equals declarations that reference external modules inside namespaces
- inspect the wrapped `TSImportEqualsDeclaration` within `ExportNamedDeclaration`
- update TypeScript parser coverage snapshots for all affected conformance cases
2026-07-29 15:44:48 +00:00
camc314 c917f204a7 feat(ast)!: introduce ArrowFunctionBody enum (#24987)
## Summary

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

## Breaking Change

The Rust AST changes from:

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

to:

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

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

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

## Why?

The previous representation required consumers to maintain this implicit invariant:

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

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

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

## How to migrate

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

Common field migrations:

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

closes https://github.com/oxc-project/backlog/issues/211
2026-07-29 13:32:32 +00:00
overlookmotel 7e1199cbbb feat(ast)!: remove conversion to Box from AST builder methods (#25038)
AST builder methods accepted an `IntoIn<ArenaBox<T>>` for all params where an `ArenaBox<T>` is required. You could pass a plain `T`, and the builder method would automatically allocate space for it, and copy it into the arena.

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

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

Additional benefits of this change:

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

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

Note: AST builder methods still define params which are `ArenaVec`s as `IntoIn<ArenaVec<T>>`, as this does add genuine value - it allows passing `[]` instead of the much more verbose `ArenaVec::new_in(self)`.
2026-07-28 22:40:04 +00:00
overlookmotel 8b80f8bf2f perf(parser): avoid large types on stack (#25034)
In parser, move some allocations to happen earlier.

When we need an `ArenaBox<T>` for later method calls, it's preferable to allocate it into arena as swiftly as possible, so only hold an 8-byte `ArenaBox` on stack, instead of the whole `T`, which is much larger. This reduces stack and register pressure.
2026-07-28 22:40:02 +00:00
oxc-guard[bot] fc702c1fa9 release(crates): oxc v0.142.0 (#24977)
### 💥 BREAKING CHANGES

- 1ac5ac7 ast: [**BREAKING**] Tighten bound on `AstBuild` trait (#24925)
(overlookmotel)

### 🚀 Features

- c7c5696 minifier: Fold parenthesized boolean ternary tests (#24968)
(Dunqing)
- 82c5806 minifier: Fold sequence branches in boolean ternaries (#24943)
(Dunqing)
- cc623d9 ast_visit: Add VisitJsMut visitor (#24828) (Boshen)
- b31713a minifier: Add new IsTerminated trait and use it in exit-point
optimization (#24950) (Armano)
- e9e0f24 minifier: Shorten integer zero comparisons (#24933) (Dunqing)
- 34f5559 minifier: Convert switch with one case to if statement
(#24611) (Armano)
- 9107e74 str: Add `static_str!` macro (#24887) (overlookmotel)
- 4d6f623 parser: Emit error for type args in JS files (#24896)
(camc314)
- ec08afc ast: Re-export `Ident` from `oxc_ast` crate (#24861)
(overlookmotel)
- 73cfdfa oxc: Re-export `oxc_str` types from `oxc` crate (#24860)
(overlookmotel)
- 11f5d1f ast_visit: Add `Utf8ToUtf16::convert_program_and_comments`
(#24859) (overlookmotel)

### 🐛 Bug Fixes

- cb5acfb minifier: Optimize termination statements in labeled
statements (#24813) (Armano)
- 4618300 minifier: Avoid longer numeric constant folds (#24875)
(Dunqing)
- f7971f1 minifier: Keep side effects in typeof comparisons (#24940)
(sapphi-red)
- 4597d6f minifier: Keep side effects when compressing `== true` to `==
1` (#24939) (sapphi-red)
- c3a48df minifier: Keep side effects values in template literals
(#24938) (sapphi-red)
- e19f183 minifier: Keep side effects when folding `Number` call
(#24937) (sapphi-red)
- 9d7745a react-compiler: Preserve warning bailouts (#24935) (Boshen)
- 0126aba codegen: Preserve orphaned file coverage comments (#24815)
(Dunqing)
- 9fbbaf8 parser: Clarify return in class static blocks (#24899)
(camc314)
- e7d5c78 str: Fix hygiene of `format_ident!` and `format_str!` macros
(#24886) (overlookmotel)
- 07e9acc ecmascript: Keep side effects in parent of `toString` calls
(#24846) (sapphi-red)
- 16a40ad ecmascript: Detect side effects in unused arguments to
`charCodeAt` (#24845) (sapphi-red)
- 5b4ae54 parser: Use specific for-await diagnostic (#24856) (camc314)
- ffcc33a parser: Report for-await in non-async functions (#24855)
(camc314)
- df230d0 minifier: Avoid reordering this in nested class keys (#24785)
(Dunqing)
- d24b76b minifier: Isolate derived constructor this state (#24784)
(Dunqing)
- 582805d parser: Parse unary conditional after satisfies (#24807)
(Boshen)
- 23b904f minifier: Preserve default parameter side effects (#24791)
(Dunqing)
- 3048594 parser: Use correct diagnostic for `await using` in bare
switch case (#24798) (camc314)
- e80574f estree: Handle empty spans serializing `ImportMeta` and
`NewTarget` (#24775) (overlookmotel)
- 4b12840 minifier: Do not fold a `-0` ternary branch to `+a` (loses the
sign) (#23637) (Jerry Zhao)
- 133bb35 ecmascript: Detect side effects of `foo().#bar` correctly when
PropertyReadSideEffects::None (#24769) (sapphi-red)

### ⚡ Performance

- a086d91 ecmascript: Avoid number literal allocations (#24889)
(Dunqing)
- 98f2ecb react_compiler: Compare value set pointers before probing in
state merge (#24805) (Marius Schulz)
- b287f29 estree: Avoid ident hash calculations when serializing
(#24774) (overlookmotel)

### 📚 Documentation

- 4d2e2bf ast: Fix incorrect return value in `common_js_require` docs
(#24945) (connorshea)
- b539e74 estree: Document custom serializers for `ImportMeta` and
`NewTarget` (#24777) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-07-27 23:07:40 +08:00
Dunqing 0126aba1d9 fix(codegen): preserve orphaned file coverage comments (#24815)
File-level V8 and Istanbul coverage directives attach to the first AST statement. When TypeScript import removal or another upstream pass deletes that statement, codegen never visits the comment attachment position and silently drops the directive. Coverage tooling then instruments a file that was explicitly excluded.

The lexer now classifies `v8 ignore file` and `istanbul ignore file` separately, including line-comment forms and explanatory suffixes. Codegen lets those directives share the existing legal-comment orphan flush, preserving source order without scanning source text during printing. Position-sensitive directives such as `ignore next`, `start`, and `stop` remain unchanged.

The recovery path is named and documented around orphaned attachments. Hashbang ordering remains intact, and disabling annotation comments still suppresses these directives. The transformer regression stays in the Rust integration harness because transform conformance deliberately clears comments before codegen.

## Example

Input:

```ts
/* v8 ignore file */
import type { Foo } from "./types";

export default { typed: "stuff" } satisfies Foo;
```

Before:

```js
export default { typed: "stuff" };
```

After:

```js
/* v8 ignore file */
export default { typed: "stuff" };
```

Covered by direct codegen regressions for removed anchors, hashbang ordering, disabled annotation comments, and position-sensitive negative cases, plus a TypeScript transformer integration test. Verified with `env -u NO_COLOR just ready`, including all-feature tests, linting, documentation, generated-code checks, formatting, and a clean-tree check.

closes #23667

This PR was assisted by OpenAI Codex.
2026-07-24 15:26:48 +00:00
camc314 9fbbaf8607 fix(parser): clarify return in class static blocks (#24899)
## Summary

- report TypeScript diagnostic TS18041 for `return` statements in class static initialization blocks
- reuse the existing `NewTarget`/`Return` parser-context invariant without adding a new context flag
- update the TypeScript, Babel, and Test262 parser snapshots

## Root cause

Class static blocks disable the parser's `Return` context, so their `return` statements previously fell through to the generic top-level TS1108 diagnostic. Static blocks already enable `NewTarget`, which distinguishes this case when `Return` is absent.
2026-07-24 14:26:41 +00:00
camc314 4d6f623cda feat(parser): emit error for type args in JS files (#24896)
## Summary

- Emit TS8011 when a JavaScript class heritage clause uses TypeScript type arguments.
- Cover the diagnostic with a misc parser failure fixture and snapshot.
2026-07-24 09:46:34 +00:00