diff --git a/.github/generated/ast_changes_watch_list.yml b/.github/generated/ast_changes_watch_list.yml index 4fa31c451b262..43b3dd3b93d8f 100644 --- a/.github/generated/ast_changes_watch_list.yml +++ b/.github/generated/ast_changes_watch_list.yml @@ -65,6 +65,8 @@ src: - 'crates/oxc_syntax/src/serialize.rs' - 'crates/oxc_syntax/src/symbol.rs' - 'crates/oxc_traverse/src/generated/scopes_collector.rs' + - 'napi/oxlint2/src/generated/constants.cjs' + - 'napi/oxlint2/src/generated/raw_transfer_constants.rs' - 'napi/parser/generated/constants.js' - 'napi/parser/generated/deserialize/js.js' - 'napi/parser/generated/deserialize/ts.js' diff --git a/Cargo.lock b/Cargo.lock index 731197308d020..80ecec5cb1801 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1994,6 +1994,7 @@ dependencies = [ "napi", "napi-build", "napi-derive", + "oxc_allocator", "oxlint", "serde_json", ] diff --git a/crates/oxc_allocator/src/lib.rs b/crates/oxc_allocator/src/lib.rs index 69cd3ef61f696..fea2e1e4494f3 100644 --- a/crates/oxc_allocator/src/lib.rs +++ b/crates/oxc_allocator/src/lib.rs @@ -88,9 +88,58 @@ use pool_fixed_size as pool; target_endian = "little" ))] use pool_fixed_size::FixedSizeAllocatorMetadata; +// Export so can be used in `napi/oxlint2` +#[cfg(all( + feature = "fixed_size", + not(feature = "disable_fixed_size"), + target_pointer_width = "64", + target_endian = "little" +))] +pub use pool_fixed_size::free_fixed_size_allocator; pub use pool::{AllocatorGuard, AllocatorPool}; +// Dummy implementations of interfaces from `pool_fixed_size`, just to stop clippy complaining. +// Seems to be necessary due to feature unification. +#[cfg(not(all( + feature = "fixed_size", + not(feature = "disable_fixed_size"), + target_pointer_width = "64", + target_endian = "little" +)))] +#[allow(missing_docs, clippy::missing_safety_doc, clippy::unused_self, clippy::allow_attributes)] +mod dummies { + use std::{ptr::NonNull, sync::atomic::AtomicBool}; + + use super::Allocator; + + #[doc(hidden)] + pub struct FixedSizeAllocatorMetadata { + pub id: u32, + pub alloc_ptr: NonNull, + pub is_double_owned: AtomicBool, + } + + #[doc(hidden)] + pub unsafe fn free_fixed_size_allocator(_metadata_ptr: NonNull) { + unreachable!(); + } + + #[doc(hidden)] + impl Allocator { + pub unsafe fn fixed_size_metadata_ptr(&self) -> NonNull { + unreachable!(); + } + } +} +#[cfg(not(all( + feature = "fixed_size", + not(feature = "disable_fixed_size"), + target_pointer_width = "64", + target_endian = "little" +)))] +pub use dummies::*; + #[cfg(all( feature = "fixed_size", not(feature = "disable_fixed_size"), diff --git a/crates/oxc_allocator/src/pool_fixed_size.rs b/crates/oxc_allocator/src/pool_fixed_size.rs index 10000fb9dd4af..09c4b141b1043 100644 --- a/crates/oxc_allocator/src/pool_fixed_size.rs +++ b/crates/oxc_allocator/src/pool_fixed_size.rs @@ -142,7 +142,8 @@ pub struct FixedSizeAllocatorMetadata { const ALLOC_SIZE: usize = BLOCK_SIZE + TWO_GIB; const ALLOC_ALIGN: usize = TWO_GIB; -const ALLOC_LAYOUT: Layout = match Layout::from_size_align(ALLOC_SIZE, ALLOC_ALIGN) { +/// Layout of backing allocations for fixed-size allocators. +pub const ALLOC_LAYOUT: Layout = match Layout::from_size_align(ALLOC_SIZE, ALLOC_ALIGN) { Ok(layout) => layout, Err(_) => unreachable!(), }; @@ -287,7 +288,7 @@ impl Drop for FixedSizeAllocator { } } -/// Deallocate memory backing a [`FixedSizeAllocator`] if it's not double-owned +/// Deallocate memory backing a `FixedSizeAllocator` if it's not double-owned /// (both owned by a `FixedSizeAllocator` on Rust side *and* held as a buffer on JS side). /// /// If it is double-owned, don't deallocate the memory but set the flag that it's no longer double-owned @@ -302,7 +303,7 @@ impl Drop for FixedSizeAllocator { /// Calling this function in any other circumstances would result in a double-free. /// /// `metadata_ptr` must point to a valid `FixedSizeAllocatorMetadata`. -unsafe fn free_fixed_size_allocator(metadata_ptr: NonNull) { +pub unsafe fn free_fixed_size_allocator(metadata_ptr: NonNull) { // Get pointer to start of original allocation from `FixedSizeAllocatorMetadata` let alloc_ptr = { // SAFETY: This `Allocator` was created by the `FixedSizeAllocator`. @@ -341,13 +342,13 @@ unsafe fn free_fixed_size_allocator(metadata_ptr: NonNull NonNull { + pub unsafe fn fixed_size_metadata_ptr(&self) -> NonNull { // SAFETY: Caller guarantees this `Allocator` was created by a `FixedSizeAllocator`. // // `FixedSizeAllocator::new` writes `FixedSizeAllocatorMetadata` after the end of diff --git a/crates/oxc_linter/src/external_linter.rs b/crates/oxc_linter/src/external_linter.rs index 1f2b5ab57be4e..05e750b67b281 100644 --- a/crates/oxc_linter/src/external_linter.rs +++ b/crates/oxc_linter/src/external_linter.rs @@ -2,6 +2,8 @@ use std::{fmt::Debug, pin::Pin, sync::Arc}; use serde::{Deserialize, Serialize}; +use oxc_allocator::Allocator; + pub type ExternalLinterLoadPluginCb = Arc< dyn Fn( String, @@ -17,7 +19,11 @@ pub type ExternalLinterLoadPluginCb = Arc< >; pub type ExternalLinterCb = Arc< - dyn Fn(String, Vec) -> Result, Box> + dyn Fn( + String, + Vec, + &Allocator, + ) -> Result, Box> + Sync + Send, >; diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 603c8e15b84f8..e5e320aeff1c5 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -213,7 +213,7 @@ impl Linter { } #[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))] - self.run_external_rules(&external_rules, path, &ctx_host, allocator); + self.run_external_rules(&external_rules, path, semantic, &ctx_host, allocator); // Stop clippy complaining about unused vars #[cfg(not(all(feature = "oxlint2", not(feature = "disable_oxlint2"))))] @@ -233,9 +233,12 @@ impl Linter { &self, external_rules: &[(ExternalRuleId, AllowWarnDeny)], path: &Path, + semantic: &Semantic<'_>, ctx_host: &ContextHost, - _allocator: &Allocator, + allocator: &Allocator, ) { + use std::ptr; + use oxc_diagnostics::OxcDiagnostic; use oxc_span::Span; @@ -248,9 +251,23 @@ impl Linter { // `external_linter` always exists when `oxlint2` feature is enabled let external_linter = self.external_linter.as_ref().unwrap(); + // Write offset of `Program` and source text length in metadata at end of buffer + let program = semantic.nodes().program().unwrap(); + let program_offset = ptr::from_ref(program) as u32; + #[expect(clippy::cast_possible_truncation)] + let source_len = program.source_text.len() as u32; + + let metadata = RawTransferMetadata::new(program_offset, source_len); + let metadata_ptr = allocator.end_ptr().cast::(); + // SAFETY: `Allocator` was created by `FixedSizeAllocator` which reserved space after `end_ptr` + // for a `RawTransferMetadata`. `end_ptr` is aligned for `FixedSizeAllocator`. + unsafe { metadata_ptr.write(metadata) }; + + // Pass AST and rule IDs to JS let result = (external_linter.run)( path.to_str().unwrap().to_string(), external_rules.iter().map(|(rule_id, _)| rule_id.raw()).collect(), + allocator, ); match result { Ok(diagnostics) => { @@ -304,10 +321,9 @@ struct RawTransferMetadata2 { use RawTransferMetadata2 as RawTransferMetadata; #[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))] -#[expect(dead_code)] impl RawTransferMetadata { - pub fn new(data_offset: u32, is_ts: bool) -> Self { - Self { data_offset, is_ts, source_len: 0, _padding: 0 } + pub fn new(data_offset: u32, source_len: u32) -> Self { + Self { data_offset, is_ts: false, source_len, _padding: 0 } } } diff --git a/napi/oxlint2/Cargo.toml b/napi/oxlint2/Cargo.toml index 144da3d4abcbf..ef39781f47f12 100644 --- a/napi/oxlint2/Cargo.toml +++ b/napi/oxlint2/Cargo.toml @@ -22,6 +22,7 @@ test = false doctest = false [dependencies] +oxc_allocator = { workspace = true, features = ["fixed_size"] } oxlint = { workspace = true, features = ["oxlint2", "allocator"] } napi = { workspace = true, features = ["async"] } diff --git a/napi/oxlint2/src/bindings.d.ts b/napi/oxlint2/src/bindings.d.ts index 197f6c2453119..4f28ad077e43b 100644 --- a/napi/oxlint2/src/bindings.d.ts +++ b/napi/oxlint2/src/bindings.d.ts @@ -4,6 +4,6 @@ export type JsLoadPluginCb = ((arg: string) => Promise) export type JsRunCb = - ((arg0: string, arg1: Array) => string) + ((arg0: string, arg1: number, arg2: Uint8Array | undefined | null, arg3: Array) => string) export declare function lint(loadPlugin: JsLoadPluginCb, run: JsRunCb): Promise diff --git a/napi/oxlint2/src/generated/constants.cjs b/napi/oxlint2/src/generated/constants.cjs new file mode 100644 index 0000000000000..3e3821fed92ee --- /dev/null +++ b/napi/oxlint2/src/generated/constants.cjs @@ -0,0 +1,18 @@ +// Auto-generated code, DO NOT EDIT DIRECTLY! +// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`. + +const BUFFER_SIZE = 2147483616, + BUFFER_ALIGN = 4294967296, + DATA_POINTER_POS_32 = 536870900, + IS_TS_FLAG_POS = 2147483612, + SOURCE_LEN_POS_32 = 536870901, + PROGRAM_OFFSET = 0; + +module.exports = { + BUFFER_SIZE, + BUFFER_ALIGN, + DATA_POINTER_POS_32, + IS_TS_FLAG_POS, + SOURCE_LEN_POS_32, + PROGRAM_OFFSET, +}; diff --git a/napi/oxlint2/src/generated/raw_transfer_constants.rs b/napi/oxlint2/src/generated/raw_transfer_constants.rs new file mode 100644 index 0000000000000..fa55a998a0f3b --- /dev/null +++ b/napi/oxlint2/src/generated/raw_transfer_constants.rs @@ -0,0 +1,10 @@ +// Auto-generated code, DO NOT EDIT DIRECTLY! +// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`. + +#![expect(clippy::unreadable_literal)] +#![allow(dead_code)] + +pub const BLOCK_SIZE: usize = 2147483632; +pub const BLOCK_ALIGN: usize = 4294967296; +pub const BUFFER_SIZE: usize = 2147483616; +pub const RAW_METADATA_SIZE: usize = 16; diff --git a/napi/oxlint2/src/index.js b/napi/oxlint2/src/index.js index 7bf613aece02d..deb8d59e93ece 100644 --- a/napi/oxlint2/src/index.js +++ b/napi/oxlint2/src/index.js @@ -1,4 +1,17 @@ +import { createRequire } from 'node:module'; import { lint } from './bindings.js'; +import { DATA_POINTER_POS_32, SOURCE_LEN_POS_32 } from './generated/constants.cjs'; + +// Import lazy visitor from `oxc-parser`. +// Use `require` not `import` as `oxc-parser` uses `require` internally, +// and need to make sure get same instance of modules as it uses internally, +// otherwise `TOKEN` here won't be same `TOKEN` as used within `oxc-parser`. +const require = createRequire(import.meta.url); +const { TOKEN } = require('../../parser/raw-transfer/lazy-common.js'), + { Visitor, getVisitorsArr } = require('../../parser/raw-transfer/visitor.js'), + walkProgram = require('../../parser/generated/lazy/walk.js'); + +const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true }); class PluginRegistry { registeredPluginPaths = new Set(); @@ -33,6 +46,9 @@ class PluginRegistry { } } +// Buffers cache +const buffers = []; + class Linter { pluginRegistry = new PluginRegistry(); @@ -63,8 +79,26 @@ class Linter { } // TODO(camc314): why do we have to destructure here? - // In `./bindings.d.ts`, it doesn't indicate that we have to (typed as `(filePath: string, ruleIds: number[]))` - lint([filePath, ruleIds]) { + // In `./bindings.d.ts`, it doesn't indicate that we have to + // (typed as `(filePath: string, bufferId: number, buffer: Uint8Array | undefined | null, ruleIds: number[])`). + lint([filePath, bufferId, buffer, ruleIds]) { + // If new buffer, add it to `buffers` array. Otherwise, get existing buffer from array. + // Do this before checks below, to make sure buffer doesn't get garbage collected when not expected + // if there's an error. + // TODO: Is this enough to guarantee soundness? + if (buffer !== null) { + const { buffer: arrayBuffer, byteOffset } = buffer; + buffer.uint32 = new Uint32Array(arrayBuffer, byteOffset); + buffer.float64 = new Float64Array(arrayBuffer, byteOffset); + + while (buffers.length <= bufferId) { + buffers.push(null); + } + buffers[bufferId] = buffer; + } else { + buffer = buffers[bufferId]; + } + if (typeof filePath !== 'string' || filePath.length === 0) { throw new Error('expected filePath to be a non-zero length string'); } @@ -72,6 +106,7 @@ class Linter { throw new Error('Expected `ruleIds` to be a non-zero len array'); } + // Get visitors for this file from all rules const diagnostics = []; const createContext = (ruleId) => ({ @@ -85,13 +120,25 @@ class Linter { }, }); - const rules = []; + const visitors = []; for (const { rule, ruleId } of this.pluginRegistry.getRules(ruleIds)) { - rules.push(rule.create(createContext(ruleId))); + visitors.push(rule.create(createContext(ruleId))); } - // TODO: walk the AST + // TODO: Combine visitors for multiple rules + const visitor = new Visitor(visitors[0]); + + // Visit AST + const programPos = buffer.uint32[DATA_POINTER_POS_32], + sourceByteLen = buffer.uint32[SOURCE_LEN_POS_32]; + + const sourceText = textDecoder.decode(buffer.subarray(0, sourceByteLen)); + const sourceIsAscii = sourceText.length === sourceByteLen; + const ast = { buffer, sourceText, sourceByteLen, sourceIsAscii, nodes: new Map(), token: TOKEN }; + + walkProgram(programPos, ast, getVisitorsArr(visitor)); + // Send diagnostics back to Rust return JSON.stringify(diagnostics); } } diff --git a/napi/oxlint2/src/lib.rs b/napi/oxlint2/src/lib.rs index 2e46b86f15fe1..ea08541bf65be 100644 --- a/napi/oxlint2/src/lib.rs +++ b/napi/oxlint2/src/lib.rs @@ -1,25 +1,31 @@ use std::{ process::{ExitCode, Termination}, - sync::{Arc, mpsc::channel}, + sync::{Arc, atomic::Ordering, mpsc::channel}, }; use napi::{ Status, - bindgen_prelude::Promise, + bindgen_prelude::{Promise, Uint8Array}, threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, }; use napi_derive::napi; +use oxc_allocator::{Allocator, free_fixed_size_allocator}; use oxlint::{ ExternalLinter, ExternalLinterCb, ExternalLinterLoadPluginCb, LintResult, PluginLoadResult, lint as oxlint_lint, }; +mod generated { + pub mod raw_transfer_constants; +} +use generated::raw_transfer_constants::{BLOCK_ALIGN, BUFFER_SIZE}; + #[napi] pub type JsRunCb = ThreadsafeFunction< - (String, Vec), + (String, u32, Option, Vec), String, /* Vec */ - (String, Vec), + (String, u32, Option, Vec), Status, false, >; @@ -35,13 +41,79 @@ pub type JsLoadPluginCb = ThreadsafeFunction< fn wrap_run(cb: JsRunCb) -> ExternalLinterCb { let cb = Arc::new(cb); - Arc::new(move |file_path: String, rule_ids: Vec| { + Arc::new(move |file_path: String, rule_ids: Vec, allocator: &Allocator| { let cb = Arc::clone(&cb); let (tx, rx) = channel(); + // Each buffer is sent over to JS only once. + // JS side stores them in an array, and holds them until process ends. + // A flag in `FixedSizeAllocatorMetadata` records whether the buffer has already been transferred + // to JS or not. If it hasn't, send it. Otherwise, just send the ID of the buffer which is the + // index of that buffer in the array on JS side, and JS side will get the buffer from the array. + // This means there's only even 1 instance of a buffer on Rust side, and 1 on JS side, + // which makes it simpler to avoid use-after-free or double-free problems. + + // SAFETY: This crate enables the `fixed_size` feature on `oxc_allocator`, so all AST `Allocator`s + // are created via `FixedSizeAllocator`. We only create an immutable ref from this pointer. + let metadata_ptr = unsafe { allocator.fixed_size_metadata_ptr() }; + let (buffer_id, already_sent_to_js) = { + // SAFETY: Fixed-size allocators always have a valid `FixedSizeAllocatorMetadata` + // stored at the pointer returned by `Allocator::fixed_size_metadata_ptr`. + let metadata = unsafe { metadata_ptr.as_ref() }; + // TODO: Is `Ordering::SeqCst` excessive here? + let already_sent_to_js = metadata.is_double_owned.fetch_or(true, Ordering::SeqCst); + + (metadata.id, already_sent_to_js) + }; + + let buffer = if already_sent_to_js { + // Buffer has already been sent to JS. Don't send it again. + None + } else { + // Buffer has not already been sent to JS. Send it. + + // Get pointer to start of allocator chunk. + // Note: `Allocator::data_ptr` would not provide the right pointer, because source text + // gets written to start of the allocator chunk, and `data_ptr` gets moved to after it. + // SAFETY: Fixed-size allocators have their chunk aligned on `BLOCK_ALIGN`, + // and size less than `BLOCK_ALIGN`. So we can get pointer to start of `Allocator` chunk + // by rounding down to next multiple of `BLOCK_ALIGN`. That can't go out of bounds of + // the backing allocation. + let chunk_ptr = unsafe { + let ptr = metadata_ptr.cast::(); + let offset = ptr.as_ptr() as usize % BLOCK_ALIGN; + ptr.sub(offset) + }; + + // SAFETY: + // Range of memory starting at `chunk_ptr` and encompassing `BUFFER_SIZE` is all within + // the allocation backing the `Allocator`. + // + // We can't prove that no mutable references to data in the buffer exist, + // but there shouldn't be any, because linter doesn't mutate the AST. + // Anyway, I (@overlookmotel) am not sure if the aliasing rules apply to code in another + // language. Probably not, as JS code is outside the domain of the "Rust abstract machine". + // As long as we don't mutate data in the buffer on JS side, it should be fine. + // + // On the other side, while many immutable references to data in the buffer exist + // (`AstKind`s for every AST node), JS side does not mutate the data in the buffer, + // so that shouldn't break the guarantees of `&` references. + // + // This is all a bit wavy, but such is the way with sharing memory outside of Rust. + let buffer = unsafe { + Uint8Array::with_external_data( + chunk_ptr.as_ptr(), + BUFFER_SIZE, + move |_ptr, _len| free_fixed_size_allocator(metadata_ptr), + ) + }; + Some(buffer) + }; + + // Send data to JS let status = cb.call_with_return_value( - (file_path, rule_ids), + (file_path, buffer_id, buffer, rule_ids), ThreadsafeFunctionCallMode::NonBlocking, move |result, _env| { let _ = match &result { diff --git a/napi/oxlint2/test/__snapshots__/e2e.test.ts.snap b/napi/oxlint2/test/__snapshots__/e2e.test.ts.snap index d03ec159a8942..a67081ada124b 100644 --- a/napi/oxlint2/test/__snapshots__/e2e.test.ts.snap +++ b/napi/oxlint2/test/__snapshots__/e2e.test.ts.snap @@ -30,7 +30,7 @@ exports[`cli options for bundling > should load a custom plugin 1`] = ` x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[index.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- Found 1 warning and 1 error. @@ -49,7 +49,7 @@ exports[`cli options for bundling > should load a custom plugin when configured x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[index.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- Found 1 warning and 1 error. @@ -68,7 +68,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/01.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -81,7 +81,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/02.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -94,7 +94,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/03.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -107,7 +107,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/04.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -120,7 +120,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/05.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -133,7 +133,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/06.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -146,7 +146,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/07.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -159,7 +159,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/08.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -172,7 +172,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/09.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -185,7 +185,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/10.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -198,7 +198,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/11.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -211,7 +211,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/12.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -224,7 +224,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/13.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -237,7 +237,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/14.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -250,7 +250,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/15.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -263,7 +263,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/16.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -276,7 +276,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/17.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -289,7 +289,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/18.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -302,7 +302,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/19.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\\eslint(no-debugger)]8;;\\: \`debugger\` statement is not allowed @@ -315,7 +315,7 @@ exports[`cli options for bundling > should load a custom plugin with multiple fi x basic-custom-plugin(no-debugger): Unexpected Debugger Statement ,-[files/20.js:1:1] 1 | debugger; - : ^ + : ^^^^^^^^^ \`---- Found 20 warnings and 20 errors. diff --git a/napi/oxlint2/test/fixtures/basic_custom_plugin/test_plugin/index.js b/napi/oxlint2/test/fixtures/basic_custom_plugin/test_plugin/index.js index b8cdb2bdb73fa..d4e46f0bc4d7b 100644 --- a/napi/oxlint2/test/fixtures/basic_custom_plugin/test_plugin/index.js +++ b/napi/oxlint2/test/fixtures/basic_custom_plugin/test_plugin/index.js @@ -5,13 +5,13 @@ export default { rules: { "no-debugger": { create(context) { - // TODO: move this call into `DebuggerStatement`, once we are walking the ast. - context.report({ - message: "Unexpected Debugger Statement", - node: { start: 0, end: 0 }, - }); return { - DebuggerStatement(_debuggerStatement) {}, + DebuggerStatement(debuggerStatement) { + context.report({ + message: "Unexpected Debugger Statement", + node: debuggerStatement, + }); + }, }; }, }, diff --git a/napi/oxlint2/test/fixtures/basic_custom_plugin_many_files/test_plugin/index.js b/napi/oxlint2/test/fixtures/basic_custom_plugin_many_files/test_plugin/index.js index b8cdb2bdb73fa..d4e46f0bc4d7b 100644 --- a/napi/oxlint2/test/fixtures/basic_custom_plugin_many_files/test_plugin/index.js +++ b/napi/oxlint2/test/fixtures/basic_custom_plugin_many_files/test_plugin/index.js @@ -5,13 +5,13 @@ export default { rules: { "no-debugger": { create(context) { - // TODO: move this call into `DebuggerStatement`, once we are walking the ast. - context.report({ - message: "Unexpected Debugger Statement", - node: { start: 0, end: 0 }, - }); return { - DebuggerStatement(_debuggerStatement) {}, + DebuggerStatement(debuggerStatement) { + context.report({ + message: "Unexpected Debugger Statement", + node: debuggerStatement, + }); + }, }; }, }, diff --git a/napi/oxlint2/test/fixtures/custom_plugin_via_overrides/test_plugin/index.js b/napi/oxlint2/test/fixtures/custom_plugin_via_overrides/test_plugin/index.js index b8cdb2bdb73fa..d4e46f0bc4d7b 100644 --- a/napi/oxlint2/test/fixtures/custom_plugin_via_overrides/test_plugin/index.js +++ b/napi/oxlint2/test/fixtures/custom_plugin_via_overrides/test_plugin/index.js @@ -5,13 +5,13 @@ export default { rules: { "no-debugger": { create(context) { - // TODO: move this call into `DebuggerStatement`, once we are walking the ast. - context.report({ - message: "Unexpected Debugger Statement", - node: { start: 0, end: 0 }, - }); return { - DebuggerStatement(_debuggerStatement) {}, + DebuggerStatement(debuggerStatement) { + context.report({ + message: "Unexpected Debugger Statement", + node: debuggerStatement, + }); + }, }; }, }, diff --git a/tasks/ast_tools/src/generators/raw_transfer.rs b/tasks/ast_tools/src/generators/raw_transfer.rs index fce17b9c232d1..0a25127a87764 100644 --- a/tasks/ast_tools/src/generators/raw_transfer.rs +++ b/tasks/ast_tools/src/generators/raw_transfer.rs @@ -9,7 +9,7 @@ use quote::quote; use rustc_hash::FxHashSet; use crate::{ - ALLOCATOR_CRATE_PATH, Generator, NAPI_PARSER_PACKAGE_PATH, + ALLOCATOR_CRATE_PATH, Generator, NAPI_OXLINT_PACKAGE_PATH, NAPI_PARSER_PACKAGE_PATH, codegen::{Codegen, DeriveId}, derives::estree::{ get_fieldless_variant_value, get_struct_field_name, should_flatten_field, @@ -71,12 +71,20 @@ impl Generator for RawTransferGenerator { }, Output::Javascript { path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/constants.js"), + code: constants_js.clone(), + }, + Output::Javascript { + path: format!("{NAPI_OXLINT_PACKAGE_PATH}/src/generated/constants.cjs"), code: constants_js, }, Output::Rust { path: format!("{NAPI_PARSER_PACKAGE_PATH}/src/generated/raw_transfer_constants.rs"), tokens: constants_rust.clone(), }, + Output::Rust { + path: format!("{NAPI_OXLINT_PACKAGE_PATH}/src/generated/raw_transfer_constants.rs"), + tokens: constants_rust.clone(), + }, Output::Rust { path: format!("{ALLOCATOR_CRATE_PATH}/src/generated/fixed_size_constants.rs"), tokens: constants_rust, diff --git a/tasks/ast_tools/src/main.rs b/tasks/ast_tools/src/main.rs index d3d37582daf74..d3bf8c751c11b 100644 --- a/tasks/ast_tools/src/main.rs +++ b/tasks/ast_tools/src/main.rs @@ -256,6 +256,9 @@ const TYPESCRIPT_DEFINITIONS_PATH: &str = "npm/oxc-types/types.d.ts"; /// Path to NAPI parser package const NAPI_PARSER_PACKAGE_PATH: &str = "napi/parser"; +/// Path to NAPI oxlint package +const NAPI_OXLINT_PACKAGE_PATH: &str = "napi/oxlint2"; + /// Path to write AST changes filter list to const AST_CHANGES_WATCH_LIST_PATH: &str = ".github/generated/ast_changes_watch_list.yml";