Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/oxc_data_structures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ ropey = { workspace = true, optional = true }

[features]
default = []
all = ["assert_unchecked", "code_buffer", "inline_string", "rope", "slice_iter", "stack"]
all = ["assert_unchecked", "box_macros", "code_buffer", "inline_string", "rope", "slice_iter", "stack"]
assert_unchecked = []
box_macros = []
code_buffer = ["assert_unchecked"]
inline_string = ["assert_unchecked"]
rope = ["dep:ropey"]
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_data_structures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This crate provides specialized data structures and utilities that are used thro
- **Inline strings**: Memory-efficient string storage for short strings
- **Slice iterators**: Enhanced iteration capabilities for slices
- **Rope data structure**: Efficient text manipulation for large documents
- **Box macros**: Macros for creating boxed arrays / slices (similar to `vec!` macro)

## Architecture

Expand Down
58 changes: 58 additions & 0 deletions crates/oxc_data_structures/src/box_macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Macros for creating boxed slices and arrays.

// TODO: We might be able to make these more performant by using `MaybeUninit`.

/// Macro to create a boxed slice (`Box<[T]>`).
///
/// Similar to standard library's `vec!` macro.
///
/// If length is a const (known at compile time), you would likely be better off using a boxed array instead
/// (`boxed_array!` macro).
///
/// # Example
/// ```
/// # fn get_length_somehow() -> usize { 5 }
///
/// use oxc_data_structures::box_macros::boxed_slice;
///
/// let len = get_length_somehow();
/// // Creates a `Box<[u64]>`
/// let boxed = boxed_slice![0u64; len];
/// ```
#[macro_export]
macro_rules! boxed_slice {
($value:expr; $len:expr) => {
::std::vec![$value; $len].into_boxed_slice()
};
}

pub use boxed_slice;

/// Macro to create a boxed array (`Box<[T; N]>`).
///
/// Similar to standard library's `vec!` macro.
///
/// `$len` must be a const expression.
///
/// # Example
/// ```
/// use oxc_data_structures::box_macros::boxed_array;
///
/// const LENGTH: usize = 5;
/// // Creates a `Box<[u64; LENGTH]>`
/// let boxed = boxed_array![0u64; LENGTH];
/// ```
#[macro_export]
macro_rules! boxed_array {
($value:expr; $len:expr) => {{
// Make sure `$len` is const
const LEN: usize = $len;
let boxed_slice = ::std::vec![$value; LEN].into_boxed_slice();
// `.ok()` is to support types which are not `Debug`
#[allow(clippy::allow_attributes)]
#[allow(clippy::missing_panics_doc, reason = "infallible")]
::std::boxed::Box::<[_; LEN]>::try_from(boxed_slice).ok().unwrap()
}};
}

pub use boxed_array;
3 changes: 3 additions & 0 deletions crates/oxc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#[cfg(feature = "assert_unchecked")]
mod assert_unchecked;

#[cfg(feature = "box_macros")]
pub mod box_macros;

#[cfg(feature = "code_buffer")]
pub mod code_buffer;

Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ oxc_ast_macros = { workspace = true, optional = true }
oxc_ast_visit = { workspace = true }
oxc_cfg = { workspace = true }
oxc_codegen = { workspace = true }
oxc_data_structures = { workspace = true, optional = true }
oxc_data_structures = { workspace = true, features = ["box_macros"] }
oxc_diagnostics = { workspace = true }
oxc_ecmascript = { workspace = true }
oxc_index = { workspace = true, features = ["serde"] }
Expand Down
14 changes: 4 additions & 10 deletions crates/oxc_linter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{path::Path, rc::Rc};

use oxc_allocator::Allocator;
use oxc_ast::ast_kind::AST_TYPE_MAX;
use oxc_data_structures::box_macros::boxed_array;
use oxc_semantic::AstNode;

#[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))]
Expand Down Expand Up @@ -173,23 +174,16 @@ impl Linter {
//
// See https://github.com/oxc-project/oxc/pull/6600 for more context.
if semantic.nodes().len() > 200_000 {
const AST_TYPES_LEN: usize = AST_TYPE_MAX as usize + 1;

// Collect rules into a Vec so that we can iterate over the rules multiple times
let rules = rules.collect::<Vec<_>>();

// TODO: It seems like there is probably a more intelligent way to preallocate space here. This will
// likely incur quite a few unnecessary reallocs currently. We theoretically could compute this at
// compile-time since we know all of the rules and their AST node type information ahead of time.
//
// Convert to boxed array to help compiler see that indexing into it with an `AstType`
// cannot go out of bounds, and remove bounds checks. The `unwrap` is infallible and should be optimized out.
let rules_by_ast_type = vec![Vec::new(); AST_TYPES_LEN];
#[expect(clippy::missing_panics_doc, reason = "infallible")]
let mut rules_by_ast_type =
Box::<[_; AST_TYPES_LEN]>::try_from(rules_by_ast_type.into_boxed_slice())
.ok()
.unwrap();
// Use boxed array to help compiler see that indexing into it with an `AstType`
// cannot go out of bounds, and remove bounds checks.
let mut rules_by_ast_type = boxed_array![Vec::new(); AST_TYPE_MAX as usize + 1];
// TODO: Compute needed capacity. This is a slight overestimate as not 100% of rules will need to run on all
// node types, but it at least guarantees we won't need to realloc.
let mut rules_any_ast_type = Vec::with_capacity(rules.len());
Expand Down
Loading