refactor(oxc_parser): introduce ParserConfig#2
refactor(oxc_parser): introduce ParserConfig#2lilnasy wants to merge 114 commits intorustify-tokensfrom
ParserConfig#2Conversation
| type ByteHandler = unsafe fn(&mut Lexer<'_>) -> Kind; | ||
|
|
||
| /// Lookup table mapping any incoming byte to a handler function defined below. | ||
| /// <https://github.com/ratel-rust/ratel-core/blob/v0.7.0/ratel/src/lexer/mod.rs> | ||
| #[rustfmt::skip] | ||
| static BYTE_HANDLERS: [ByteHandler; 256] = [ | ||
| // 0 1 2 3 4 5 6 7 8 9 A B C D E F // | ||
| ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, SPS, LIN, ISP, ISP, LIN, ERR, ERR, // 0 | ||
| ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, // 1 | ||
| SPS, EXL, QOD, HAS, IDT, PRC, AMP, QOS, PNO, PNC, ATR, PLS, COM, MIN, PRD, SLH, // 2 | ||
| ZER, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, COL, SEM, LSS, EQL, GTR, QST, // 3 | ||
| AT_, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, // 4 | ||
| IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, BTO, ESC, BTC, CRT, IDT, // 5 | ||
| TPL, L_A, L_B, L_C, L_D, L_E, L_F, L_G, IDT, L_I, IDT, L_K, L_L, L_M, L_N, L_O, // 6 | ||
| L_P, IDT, L_R, L_S, L_T, L_U, L_V, L_W, IDT, L_Y, IDT, BEO, PIP, BEC, TLD, ERR, // 7 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // 8 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // 9 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // A | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // B | ||
| UER, UER, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // C | ||
| UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // D | ||
| UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // E | ||
| UNI, UNI, UNI, UNI, UNI, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // F | ||
| ]; | ||
| // TODO: this should be a static array, not a function | ||
| const fn byte_handlers<Config: ParserConfig>() -> [for<'a> fn(&mut Lexer<'a, Config>) -> Kind; 256] { | ||
| [ | ||
| // 0 1 2 3 4 5 6 7 8 9 A B C D E F // | ||
| ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, SPS, LIN, ISP, ISP, LIN, ERR, ERR, // 0 | ||
| ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, // 1 | ||
| SPS, EXL, QOD, HAS, IDT, PRC, AMP, QOS, PNO, PNC, ATR, PLS, COM, MIN, PRD, SLH, // 2 | ||
| ZER, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, COL, SEM, LSS, EQL, GTR, QST, // 3 | ||
| AT_, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, // 4 | ||
| IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, BTO, ESC, BTC, CRT, IDT, // 5 | ||
| TPL, L_A, L_B, L_C, L_D, L_E, L_F, L_G, IDT, L_I, IDT, L_K, L_L, L_M, L_N, L_O, // 6 | ||
| L_P, IDT, L_R, L_S, L_T, L_U, L_V, L_W, IDT, L_Y, IDT, BEO, PIP, BEC, TLD, ERR, // 7 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // 8 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // 9 | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // A | ||
| UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // B | ||
| UER, UER, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // C | ||
| UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // D | ||
| UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // E | ||
| UNI, UNI, UNI, UNI, UNI, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, UER, // F | ||
| ] | ||
| } |
There was a problem hiding this comment.
I was running in circles trying to figure this out. This is what claude suggested but the function call probably has a cost (maybe #[inline] helps). I'll have to come back to this another time.
There was a problem hiding this comment.
This is tricky. The reason we want a static is that statics are stored in the binary only once, whereas a const is inlined everywhere it's used. Lexer::handle_byte has 2 call sites, so the danger is that we end up with 2 copies of the table in the binary, which is wasteful. The table is 2 KiB (256 x 8 bytes).
There doesn't seem to be any way to do that, though. Rust doesn't allow statics to be generic.
Maybe this is slightly better:
/// Byte handler.
#[expect(type_alias_bounds)]
type ByteHandler<Config: ParserConfig> = unsafe fn(&mut Lexer<'_, Config>) -> Kind;
impl<Config: ParserConfig> Lexer<'_, Config> {
/// Byte handlers for this `ParserConfig`.
const BYTE_HANDLERS: [ByteHandler<Config>; 256] = byte_handlers();
/// Handle next byte of source.
///
/// # SAFETY
///
/// * Lexer must not be at end of file.
/// * `byte` must be next byte of source code, corresponding to current position of `lexer.source`.
/// * Only `BYTE_HANDLERS` for ASCII characters may use the `ascii_byte_handler!()` macro.
// `#[inline(always)]` to ensure is inlined into `read_next_token`
#[expect(clippy::inline_always)]
#[inline(always)]
pub(super) unsafe fn handle_byte(&mut self, byte: u8) -> Kind {
// SAFETY: Caller guarantees to uphold safety invariants
unsafe { Self::BYTE_HANDLERS[byte as usize](self) }
}
}
/// Lookup table mapping any incoming byte to a handler function defined below.
/// <https://github.com/ratel-rust/ratel-core/blob/v0.7.0/ratel/src/lexer/mod.rs>
#[rustfmt::skip]
const fn byte_handlers<Config: ParserConfig>() -> [ByteHandler<Config>; 256] {
[
// ....
]
}At least then byte handlers table is a const, rather than stored in a local var.
(side benefit: this version also doesn't have the weird for<'a> syntax)
Maybe compiler is smart enough to de-duplicate the tables and store it only once. To find out, we'll need to build it, and then check the binary to see if it increases in size or not.
crates/oxc_parser/src/lexer/mod.rs
Outdated
| fn push(&mut self, token: Token); | ||
| fn checkpoint(&self) -> Self::Checkpoint; | ||
| fn rewind(&mut self, checkpoint: Self::Checkpoint); | ||
| // TODO: add another method that concretely returns a Vec of tokens |
There was a problem hiding this comment.
We basically need a wrapper for std::mem::replace(&mut self.tokens, Vec::new_in(allocator)).
There was a problem hiding this comment.
Maybe:
pub trait TokenStore<'a> {
// ...
fn into_tokens(self, allocator: &'a Allocator) -> ArenaVec<'a, Token>;
}
impl<'a> TokenStore<'a> for NoopTokenStore {
// ...
fn into_tokens(self, allocator: &'a Allocator) -> ArenaVec<'a, Token> {
oxc_allocator::Vec::new_in(allocator)
}
}
impl<'a> TokenStore<'a> for RealTokenStore<'a> {
// ...
fn into_tokens(self, _allocator: &'a Allocator) -> ArenaVec<'a, Token> {
self.tokens
}
}Then in ParserImpl::parse:
let tokens = self.lexer.tokens.into_tokens(self.ast.allocator);into_tokens consumes self, so no need for mem::replace.
crates/oxc_parser/src/lexer/mod.rs
Outdated
| #[derive(Debug)] | ||
| pub struct LexerCheckpoint<'a, Config: ParserConfig> { | ||
| source_position: SourcePosition<'a>, | ||
| token: Token, | ||
| errors_snapshot: ErrorSnapshot, | ||
| token_checkpoint: <Config::Tokens<'a> as TokenStore<'a>>::Checkpoint, | ||
| } | ||
|
|
||
| impl<Config: ParserConfig> Clone for LexerCheckpoint<'_, Config> { | ||
| fn clone(&self) -> Self { | ||
| Self { | ||
| source_position: self.source_position, | ||
| token: self.token, | ||
| errors_snapshot: self.errors_snapshot.clone(), | ||
| token_checkpoint: self.token_checkpoint.clone(), | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Clone can simply be derived but that causes trait-satisfaction errors in ParserCheckpoint that I can't make sense of yet.
There was a problem hiding this comment.
The auto-derived trait impl is this:
impl<Config: Clone> Clone for LexerCheckpoint<Config>
where
Config: ParserConfig,
Config::Tokens<'a>: Clone,
{
fn clone(&self) -> Self {
match self {
LexerCheckpoint {
source_position: source_position,
token: token,
errors_snapshot: errors_snapshot,
token_checkpoint: token_checkpoint,
} => LexerCheckpoint {
source_position: source_position.clone(),
token: token.clone(),
errors_snapshot: errors_snapshot.clone(),
token_checkpoint: token_checkpoint.clone(),
},
}
}
}This has bounds that both Config: Clone and Config::Tokens<'a>: Clone, and it doesn't implement Clone on LexerCheckpoint unless both those are satisfied.
Not sure why it includes those bounds, as they're not required. Probably it can't statically determine that they're not required - macros operate as text manipulators only - they don't have type information.
Neither Config nor Config::Tokens are Clone, and neither can be, because oxc_allocator::Vec isn't Clone.
So no way I can see to avoid manual implementation of Clone here. I'd suggest just adding a short comment saying why.
(If you use VS Code, you can see the expansion of any macro by putting cursor on the macro, and Command Palette -> "rust-analyser: Expand macro recursively at caret")
crates/oxc_parser/src/lexer/mod.rs
Outdated
| @@ -99,6 +185,24 @@ impl<'a> Lexer<'a> { | |||
| source_type: SourceType, | |||
| unique: UniquePromise, | |||
| ) -> Self { | |||
| Self::new_with_config(allocator, source_text, source_type, unique) | |||
| } | |||
| } | |||
|
|
|||
| impl<'a, Config: ParserConfig> Lexer<'a, Config> { | |||
| /// Create new `Lexer`. | |||
| /// | |||
| /// Requiring a `UniquePromise` to be provided guarantees only 1 `Lexer` can exist | |||
| /// on a single thread at one time. | |||
There was a problem hiding this comment.
There is a separate impl block for StandardParserConfig to avoid having to add type annotations in the many places using Lexer::new. That usage remains unambiguous and continues to work as-is. It reduces the PR diff size and I imagine makes life slightly easier for non-oxc projects using our crates.
Parser and ParserImpl have similar refactors.
crates/oxc_parser/src/lib.rs
Outdated
| /// | ||
| /// Filled only when the parser is being used by oxlint. | ||
| /// Empty when `ParserConfig` is the default one. | ||
| pub tokens: ArenaVec<'a, Token>, |
There was a problem hiding this comment.
This is oxc_allocator::Vec.
…#16765) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | [`0.8.5` -> `0.8.6`](https://renovatebot.com/diffs/npm/oxlint-tsgolint/0.8.5/0.8.6) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/tsgolint (oxlint-tsgolint)</summary> ### [`v0.8.6`](https://github.com/oxc-project/tsgolint/releases/tag/v0.8.6) [Compare Source](https://github.com/oxc-project/tsgolint/compare/v0.8.5...v0.8.6) ##### What's Changed - chore(deps): update typescript-go digest to [`5ac8497`](https://github.com/oxc-project/tsgolint/commit/5ac8497) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;500](https://github.com/oxc-project/tsgolint/pull/500) - chore(deps): update typescript-go digest to [`dcebe53`](https://github.com/oxc-project/tsgolint/commit/dcebe53) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;503](https://github.com/oxc-project/tsgolint/pull/503) - fix: nil interface type crash in getBaseTypes ([#&oxc-project#8203;502](https://github.com/oxc-project/tsgolint/issues/502)) by [@&oxc-project#8203;camc314](https://github.com/camc314) in [#&oxc-project#8203;504](https://github.com/oxc-project/tsgolint/pull/504) - chore(deps): update typescript-go digest to [`cac2644`](https://github.com/oxc-project/tsgolint/commit/cac2644) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;505](https://github.com/oxc-project/tsgolint/pull/505) **Full Changelog**: <oxc-project/tsgolint@v0.8.5...v0.8.6> </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [tsdown](http://tsdown.dev/) ([source](https://github.com/rolldown/tsdown)) | [`0.17.2` -> `0.17.3`](https://renovatebot.com/diffs/npm/tsdown/0.17.2/0.17.3) |  |  |  |  | --- ### Release Notes <details> <summary>rolldown/tsdown (tsdown)</summary> ### [`v0.17.3`](https://github.com/rolldown/tsdown/releases/tag/v0.17.3) [Compare Source](https://github.com/rolldown/tsdown/compare/v0.17.2...v0.17.3) ##### 🚀 Features - **copy**: Support glob in `copy` - by [@&oxc-project#8203;kricsleo](https://github.com/kricsleo) and [@&oxc-project#8203;sxzz](https://github.com/sxzz) in [#&oxc-project#8203;637](https://github.com/rolldown/tsdown/issues/637) [<samp>(c1fd4)</samp>](https://github.com/rolldown/tsdown/commit/c1fd4f3) ##### 🐞 Bug Fixes - Print error stack - by [@&oxc-project#8203;sxzz](https://github.com/sxzz) [<samp>(1c5b6)</samp>](https://github.com/rolldown/tsdown/commit/1c5b650) - Add config name to rebuilt message - by [@&oxc-project#8203;sxzz](https://github.com/sxzz) [<samp>(bfdc6)</samp>](https://github.com/rolldown/tsdown/commit/bfdc67a) - Call hooks for each format - by [@&oxc-project#8203;sxzz](https://github.com/sxzz) [<samp>(19bdd)</samp>](https://github.com/rolldown/tsdown/commit/19bdd0e) - Merge input & output options - by [@&oxc-project#8203;sxzz](https://github.com/sxzz) [<samp>(14181)</samp>](https://github.com/rolldown/tsdown/commit/1418122) ##### [View changes on GitHub](https://github.com/rolldown/tsdown/compare/v0.17.2...v0.17.3) </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
When adding new features primarily using JS via napi, to reduce future maintenance burdens such as missed implementations.
After giving it some thought, I decided to lean these features toward JS after all. The reasons are: - I want Rust side to focus on complex formatting tasks, while moving infrequently used commands elsewhere - Code defining `napi` callbacks tends to become verbose While `--init` itself worked on the Rust side, it will share some lines with upcoming `--migrate`, so I'm extracting this beforehand too. Also, We were generating `.jsonc`, but I've switched to creating `.json` for now. This is because running `oxfmt` immediately after generating `.jsonc` causes trailing comma diffs. 😨 I was hesitant to introduce a jsonc writer, so `.json` will enough for now.
Previously, - `oxfmt(napi) not-exists.js` returns `exitCode: 1` - `oxfmt(rust) not-exists.js` returns `exitCode: 2` = Expected To fix this, I updated napi callback return value from `bool`. Additionally, I realized that by returning the processing `Mode` simultaneously, Rust can handle the CLI arguments in better way. Like `--init --help`, which now shows help. And finally changed napi binding name from `format()` to `runCli()` for the future Node API.
…meta.bar` (oxc-project#16636) This commit fix issue oxc-project#16623 Fixes oxc-project#16623
Simple addition but unfortunately untested: I locally fail tsgo related tests, probably for reasons related to not having tsgo installed? --------- Co-authored-by: Boshen <boshenc@gmail.com>
…roject#16772) Eliminate one more reference that was still lingering.
…age directly in conformance build (oxc-project#16774) In build for conformance tests, use `@typescript-eslint/typescript-estree` package directly instead of the `ts_eslint.cjs`, to get stack traces for parsing failures. Only affects conformance build - release and debug (tests) builds both use `ts_eslint.cjs` bundle, same as before.
There was a problem hiding this comment.
Wow this gets a bit complicated. I hadn't clocked that BYTE_HANDLERS would be impacted by the change.
A few broad comments on top of the smaller notes below:
No-op impl first
I suggest splitting this change into 2 parts:
- Introduce
ParserConfigas an empty trait (no tokens code). Only addStandardParserConfig. - Add all the tokens-related code.
Step (1) is pure refactor. After that PR alone, we can check that the changes to BYTE_HANDLERS aren't bloating the binary.
Once that's done, we'll be able to concentrate on the tokens-related code in isolation.
Shorten code
We have to add the generic in a lot of places. To make it less intrusive, I suggest:
// Before
use crate::ParserConfig;
impl<'a, Config: ParserConfig> ParserImpl<'a, Config> {
// ...
}// After
use crate::ParserConfig as Config;
impl<'a, C: Config> ParserImpl<'a, C> {
// ...
}ParserConfig future
FYI: I have other plans for ParserConfig aside from tokens.
I'd like to add more methods to ParserConfig, and make ParseOptions implement ParserConfig. Then you'll be able to set parser options at compile time instead of dynamically if you want. For instance:
trait ParserConfig {
fn preserve_parens(&self) -> bool;
}
struct ParseOptions {
preserve_parens: bool;
}
impl ParserConfig for ParseOptions {
fn preserve_parens(&self) -> bool {
self.preserve_parens
}
}This behaves exactly the same as at present, but for example in linter where preserve_parens is always true, we can implement a specialized ParserConfig which returns true from preserve_parens unconditionally. This will remove branches from the version of parser used in linter.
(I'm not suggesting we do that now - just to put you in the picture of what I have in mind)
| fn $id($lex: &mut Lexer) -> Kind { | ||
| fn $id<Config: ParserConfig>($lex: &mut Lexer<'_, Config>) -> Kind { |
There was a problem hiding this comment.
Please also update the comment above this showing what the macro expands to.
crates/oxc_parser/src/lexer/mod.rs
Outdated
| // TODO: better name | ||
| #[derive(Debug)] | ||
| pub struct RealTokenStore<'a> { | ||
| tokens: oxc_allocator::Vec<'a, Token>, |
There was a problem hiding this comment.
Please add use oxc_allocator::Vec as ArenaVec; at top of file and use ArenaVec here. Ditto other places where we have oxc_allocator::Vec.
crates/oxc_parser/src/lexer/mod.rs
Outdated
| // TODO: better name | ||
| pub struct RealParserConfig; |
There was a problem hiding this comment.
Ha yes! How about:
RealParserConfig->ParserConfigWithTokensStandardParserConfig->ParserConfigWithoutTokensRealTokenStore->ActiveTokenStore
?
crates/oxc_parser/src/lexer/mod.rs
Outdated
| Full(Vec<OxcDiagnostic>), | ||
| Full(std::vec::Vec<OxcDiagnostic>), |
There was a problem hiding this comment.
Can leave this as it was (as we'll use ArenaVec for the other Vec).
crates/oxc_parser/src/lexer/mod.rs
Outdated
| @@ -99,6 +185,24 @@ impl<'a> Lexer<'a> { | |||
| source_type: SourceType, | |||
| unique: UniquePromise, | |||
| ) -> Self { | |||
| Self::new_with_config(allocator, source_text, source_type, unique) | |||
| } | |||
| } | |||
There was a problem hiding this comment.
I think this is only used in 1 test. Could:
- Convert that test to use
new_with_configinstead. - Remove
new. - Rename
new_with_configtonew.
crates/oxc_parser/src/lexer/mod.rs
Outdated
| unique: UniquePromise, | ||
| ) -> Self | ||
| where | ||
| Config: ParserConfig, |
There was a problem hiding this comment.
Is this where clause required? You already require this bound in impl<'a, Config: ParserConfig> Lexer<'a, Config> above.
crates/oxc_parser/src/lib.rs
Outdated
| pub mod lexer; | ||
|
|
||
| use oxc_allocator::{Allocator, Box as ArenaBox, Dummy}; | ||
| use crate::lexer::{ParserConfig, StandardParserConfig}; |
There was a problem hiding this comment.
Nit: Move this import down into the existing use crate::{ ... } block.
…xc-project#16780) Pure refactor. In conformance tester, remove bunch of repeated `lines.push` calls to make report creation code easier to read.
…summary (oxc-project#16781) Include percentages in summaries at top of conformance tester report.
f9d1ad2 to
5f97c54
Compare
…t#16779) ## Summary Implements ESLint's `no-promise-executor-return` rule which disallows returning values from Promise executor functions. The return value of a Promise executor is ignored, so returning a value is likely a mistake. This rule detects: - Implicit returns from arrow function expression bodies (`new Promise(r => r(1))`) - Explicit return statements with values (`return 1`, `return resolve(1)`) - Returns inside control flow (if, switch, loops, try-catch, etc.) ### Configuration - `allowVoid` (default: `false`): When `true`, allows `return void expr` pattern (both explicit and implicit returns) ### Design Notes This rule only checks executors passed directly as arrow/function expressions. It does not track references like `new Promise(executor)` where `executor` is defined elsewhere — this is an intentional design choice consistent with ESLint's implementation. ## Test Plan - [x] Added 12 pass cases (empty return, no return, nested functions, allowVoid, non-Promise) - [x] Added 16 fail cases (implicit return, explicit return, control flow, return resolve/reject) - [x] `cargo test -p oxc_linter no_promise_executor_return` passes - [x] `cargo clippy -p oxc_linter` passes
…ct#16790) Update a few rules to use backticks in their diagnostic messages.
…16791) Only one test case is a bit... slim.
…hMap` (oxc-project#16666) The language server wants to lint in the future multiple files at the same time. These files could still be in memory, so extend `IsolatedLintHandlerFileSystem` to be a `FxHashMap`
…urce text (oxc-project#16667) In the future, the language server wants to lint multiple files at the same time. Refactored the code to accept the RuntimeFS (like the CLI tool) for reading the source text.
This test case is defunct since oxc-project#14401. It doesn't test what it's meant to. From the comment in the plugin used in this test fixture: ```js // Purpose of this test fixture is to ensure that source text and AST are available in `after` hook // via `context.sourceCode` when the AST is not traversed ``` But the visitor returned by `createOnce` contains a visit function for `Program`, so the AST *is* traversed. On the other hand, if we remove that `Program` visit method, then it's an empty visitor, so the AST won't be traversed, but the `after` hook won't run either. Therefore, the case which this fixture is designed to test is impossible. Delete the fixture.
…ct#16795) Remove `// @ts-expect-error` and `// @ts-ignore` comments from test fixtures, and use assertions instead. This makes sure that the input to methods under test is what we intend it to be. Note: Using `assert` rather than `expect`, because `assert` gives TypeScript information about types, which `expect` doesn't seem to be capable of.
…hich need fixing (oxc-project#16796) Only alters comments. * Make sure all `// @ts-expect-error` comments include "TODO" where it's an issue we need to fix. * Format all the "TODO" comments consistently.
…#16798) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | [`0.8.6` -> `0.9.0`](https://renovatebot.com/diffs/npm/oxlint-tsgolint/0.8.6/0.9.0) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/tsgolint (oxlint-tsgolint)</summary> ### [`v0.9.0`](https://github.com/oxc-project/tsgolint/compare/v0.8.6...3e44cb980edb576ad8b258ca939e14c3e29d84ce) [Compare Source](https://github.com/oxc-project/tsgolint/compare/v0.8.6...3e44cb980edb576ad8b258ca939e14c3e29d84ce) </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…roject#16783) > This PR refactors the LSP tool diagnostic API to allow tools to report diagnostics for multiple URIs, not just the file being analyzed. This enables cross-file analysis features like ts config deprecation detection.
…ysis (oxc-project#16706) The constructor-super rule's DFS path analysis had exponential O(2^n) complexity, causing oxlint to hang indefinitely on files with complex control flow graphs (e.g., next.js compiled bundles with 59+ classes). ## Root Cause The previous algorithm explored all possible paths through the CFG, removing blocks from the visited set after each path to allow re-exploration. With k branch points, this creates 2^k paths. ## Solution Replaced the DFS path enumeration with iterative dataflow analysis: - New `SuperCallState` enum tracks abstract states: Unreached, Never, Once, Multiple, Mixed - Merge operation at CFG join points combines states from different paths - Transfer function computes state after executing super() calls - Worklist algorithm propagates states until fixpoint ## Complexity - Before: O(2^n) where n = number of branch points - After: O(n × m) where n = blocks, m = edges The state lattice has finite height (5 states), guaranteeing termination. ## Performance | File | Before | After | |------|--------|-------| | fetch.js (796KB, 59 classes) | hung indefinitely | 16ms | | load.js (800KB, 59 classes) | hung indefinitely | 50ms | | tar/index.js (98KB, 30 classes) | hung indefinitely | 6ms | | Full next.js (19,535 files) | hung indefinitely | 1.2s | 🤖 generated with help from Claude Opus 4.5
This corrects the implementation of the empty-tags rule to not care about the `ignorePrivate` setting. Fixes oxc-project#16871. The doc comment for this setting actually explicitly says it should not be considered: https://github.com/oxc-project/oxc/blob/230e34c6eb36c4265fa8ae421375884182fd6926/crates/oxc_linter/src/config/settings/jsdoc.rs#L12-L14 This was missed when porting the tests because the settings shape was incorrect and silently failed. I only noticed it while attempting to enforce a stricter config schema.
## Summary
Implements the `eslint/no-sequences` rule that disallows the use of the comma operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand. However, this frequently obscures side effects, and its use is often an accident.
### Features
- Allows comma operator in for loop init and update expressions (standard JS pattern)
- `allowInParentheses` option (default: `true`) allows sequences wrapped in parentheses
- For grammar positions requiring parentheses (if, while, do-while, switch, with), double parentheses are needed to indicate intentionality
### Examples
**Incorrect:**
```javascript
foo = doSomething(), val;
0, eval("doSomething();");
do {} while ((doSomething(), !!test)); // needs double parens
```
**Correct:**
```javascript
foo = (doSomething(), val);
(0, eval)("doSomething();");
do {} while (((doSomething(), !!test))); // double parens
for (i = 0, j = 10; i < j; i++, j--) {} // for loop allowed
```
Closes oxc-project#481 (partially - adds
no-sequences)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
…rride struct. (oxc-project#16870) Ensure that extra fields are not allowed inside overrides, to make it clear to users when they have added an invalid field. Pulled out of oxc-project#16822 since I think it's worth including regardless of that PR.
…xc-project#16858) Just ensuring they work correctly by checking that they get mapped to the correct enum variants. - `readonly`/`readable`/`false` should map to Readonly - `writable`/`writeable`/`true` should map to Writable - `off` should map to Off
) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxc-parser](https://oxc.rs) ([source](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser)) | [`^0.102.0` -> `^0.103.0`](https://renovatebot.com/diffs/npm/oxc-parser/0.102.0/0.103.0) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/oxc (oxc-parser)</summary> ### [`v0.103.0`](https://github.com/oxc-project/oxc/blob/HEAD/napi/parser/CHANGELOG.md#01030---2025-12-15) ##### 🚀 Features - [`30a9076`](https://github.com/oxc-project/oxc/commit/30a9076) ast, parser, codegen: Add `CommentKind::MultilineBlock` ([#&oxc-project#8203;16479](https://github.com/oxc-project/oxc/issues/16479)) (Dunqing) </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
## Summary
Implement the ESLint `no-inline-comments` rule which disallows comments
on the same line as code.
### Features
- Detects inline comments (both line and block comments)
- Supports `ignorePattern` option for regex-based exceptions
- Exempts ESLint/OxLint directive comments (`eslint-disable`, `eslint `,
etc.)
- Exempts `global`, `globals`, and `exported` directive comments (block
comments only, matching ESLint behavior)
- Handles JSX empty expression comments (`{/* comment */}`)
### Performance optimizations
- Pre-collects `JSXEmptyExpression` spans to avoid repeated AST
traversal
- Returns `&str` slices instead of `String` allocations in comment
context detection
Closes oxc-project#2987
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
Fixes oxc-project#16742 🤖 generated with help from Claude Opus 4.5
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxlint](https://oxc.rs) ([source](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`1.32.0` -> `1.33.0`](https://renovatebot.com/diffs/npm/oxlint/1.32.0/1.33.0) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/oxc (oxlint)</summary> ### [`v1.33.0`](https://github.com/oxc-project/oxc/compare/oxlint_v1.32.0...oxlint_v1.33.0) [Compare Source](https://github.com/oxc-project/oxc/compare/oxlint_v1.32.0...oxlint_v1.33.0) </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Toml formatting is missing but there's only a few toml files, can wait for oxfmt plugins. - [x] Something is slow, can't figure out why, need oxc-project#16607
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [tsdown](http://tsdown.dev/) ([source](https://github.com/rolldown/tsdown)) | [`0.17.4` -> `0.18.0`](https://renovatebot.com/diffs/npm/tsdown/0.17.4/0.18.0) |  |  |  |  | --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNDIuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
… to internal language server (oxc-project#16784) This PR refactors the VSCode extension to prefer using oxlint and oxfmt binaries from node_modules/.bin by default, with a fallback to the internal oxc_language_server when no binaries are found.
…xc-project#16898) Add configuration support for the typescript/no-base-to-string rule with an ignoredTypeNames option that allows specifying types that are safe to call toString() on, even if they don't provide a custom implementation. Default ignored types: Error, RegExp, URL, URLSearchParams 🤖 generated with help from Claude Opus 4.5 closes oxc-project/tsgolint#517
Just correct a typo.
….ts` (oxc-project#16901) Pure refactor. Move the type def for `RuleOptionsSchema` into `options.ts`, which is a more appropriate place for it.
…ble jest rule (oxc-project#16880) Related to oxc-project#4656 # Sumarry Add `require hook` as a vitest compatible rule. Oxlint-migrate PR: oxc-project/oxlint-migrate#288 # Changes made I had to sligthy modify the implementation to be compatible with both frameworks. The modifications were for 2 specific tests: ```javascripts ("describe.for([])('%s', (value) => {})", None), ( "describe('scoped', () => { test.scoped({ example: 'value' }); });", None, ), ``` Both test cases must pass. The first test case failed because `describe.for` [only exists in vitest](https://vitest.dev/api/#describe-for) and this exact part of `parse_jest_fn.rs` failed in the jest branch. https://github.com/oxc-project/oxc/blob/eba07e6fd945069e7694e3880b36e4f0f8f78d6d/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs#L109C1-L115C10 Second test failed because `test.scoped` doesn't exist neither frameworks, `scoped` is a custom property made by a user. Reviewing the Jest and Vitest eslint code they are only interested in the beginning, not in the full chain.
Small improvement to type safety. Here we need to remove `Readonly` from a type, but do only that, rather than full type-casting.
…16899) Ref: oxc-project#492 This PR adds support for [jest/prefer-to-have-been-called](https://github.com/jest-community/eslint-plugin-jest/blob/v29.5.0/docs/rules/prefer-to-have-been-called.md) --------- Signed-off-by: Cameron <cameron.clark@hey.com> Co-authored-by: Cameron <cameron.clark@hey.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…#16906) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | [`0.9.0` -> `0.9.1`](https://renovatebot.com/diffs/npm/oxlint-tsgolint/0.9.0/0.9.1) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/tsgolint (oxlint-tsgolint)</summary> ### [`v0.9.1`](https://github.com/oxc-project/tsgolint/releases/tag/v0.9.1) [Compare Source](https://github.com/oxc-project/tsgolint/compare/v0.9.0...v0.9.1) #### What's Changed - chore(deps): update typescript-go digest to [`92c21db`](https://github.com/oxc-project/tsgolint/commit/92c21db) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;509](https://github.com/oxc-project/tsgolint/pull/509) - chore(deps): update typescript-go digest to [`2046634`](https://github.com/oxc-project/tsgolint/commit/2046634) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;510](https://github.com/oxc-project/tsgolint/pull/510) - chore(deps): update npm packages by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;512](https://github.com/oxc-project/tsgolint/pull/512) - chore(deps): update taiki-e/install-action action to v2.63.1 by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;513](https://github.com/oxc-project/tsgolint/pull/513) - chore(deps): update typescript-go digest to [`6c175a0`](https://github.com/oxc-project/tsgolint/commit/6c175a0) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;511](https://github.com/oxc-project/tsgolint/pull/511) - chore(deps): update github-actions (major) by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;515](https://github.com/oxc-project/tsgolint/pull/515) - chore(deps): update taiki-e/install-action action to v2.63.2 by [@&oxc-project#8203;renovate](https://github.com/renovate)\[bot] in [#&oxc-project#8203;516](https://github.com/oxc-project/tsgolint/pull/516) - fix(no-base-to-string): remove leading trivia from error message text by [@&oxc-project#8203;camc314](https://github.com/camc314) in [#&oxc-project#8203;519](https://github.com/oxc-project/tsgolint/pull/519) **Full Changelog**: <oxc-project/tsgolint@v0.9.0...v0.9.1> </details> --- ### Configuration 📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi41NC4yIiwidXBkYXRlZEluVmVyIjoiNDIuNTQuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
…ct#16909) Updates submodule dependencies to their latest commits. ## Changes - test262: `c0cf527` → `947fee3` - babel: `3591b24` → `84d21e4` - TypeScript: `c21f73f` → `0a07132` - estree-conformance: `1f2816b` → `e0aa1b4` This PR is automatically generated by the [update_submodules workflow](https://github.com/oxc-project/oxc/blob/main/.github/workflows/update_submodules.yml).
…c-project#16801) Avoid heap allocation for the common case (0-2 default exports) by using SmallVec with inline capacity of 2 instead of Vec. 🤖 generated with help from Claude Opus 4.5
… with single empty line (oxc-project#16012) fixes oxc-project#15990 --------- Co-authored-by: Cameron <cameron.clark@hey.com>
…y as default options (oxc-project#16913) Small optimization to options merging. Treat rules which define `defaultOptions` as an empty array the same as those which provide no `defaultOptions` at all. This makes options merging take a faster path for these rules.
5f97c54 to
545d09b
Compare
ParserConfig, a generic interface that concretely does nothing for most compilation targets and will store tokens in an arena when compiled for oxlint.