From b3dceae895be0f0d2cf576e2397f8bf2057ac41f Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 1 Mar 2026 17:40:04 +0000 Subject: [PATCH] feat(data_structures): add `fieldless_enum!` macro (#19876) Add a `fieldless_enum!` macro. This macro generates a `VARIANTS` constant on the type that lists all the enum's variants. ```rs fieldless_enum! { enum Foo { A, B, C, } } assert_eq!(Foo::VARIANTS, [Foo::A, Foo::B, Foo::C]); ``` This is useful for code which interacts with the enum, and needs to take some action on each variant. --- crates/oxc_data_structures/Cargo.toml | 2 + crates/oxc_data_structures/README.md | 1 + .../oxc_data_structures/src/fieldless_enum.rs | 147 ++++++++++++++++++ crates/oxc_data_structures/src/lib.rs | 3 + 4 files changed, 153 insertions(+) create mode 100644 crates/oxc_data_structures/src/fieldless_enum.rs diff --git a/crates/oxc_data_structures/Cargo.toml b/crates/oxc_data_structures/Cargo.toml index c7ca56ee249c7..522d940702da2 100644 --- a/crates/oxc_data_structures/Cargo.toml +++ b/crates/oxc_data_structures/Cargo.toml @@ -30,6 +30,7 @@ all = [ "assert_unchecked", "box_macros", "code_buffer", + "fieldless_enum", "inline_string", "rope", "slice_iter", @@ -38,6 +39,7 @@ all = [ assert_unchecked = [] box_macros = [] code_buffer = ["assert_unchecked"] +fieldless_enum = [] inline_string = ["assert_unchecked"] rope = ["dep:ropey"] slice_iter = ["assert_unchecked"] diff --git a/crates/oxc_data_structures/README.md b/crates/oxc_data_structures/README.md index a62af755cd4c5..5fe237c1cf666 100644 --- a/crates/oxc_data_structures/README.md +++ b/crates/oxc_data_structures/README.md @@ -14,6 +14,7 @@ This crate provides specialized data structures and utilities that are used thro - **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) +- **Fieldless enums macro**: Macro for creating enums with a `VARIANTS` constant listing all variants ## Architecture diff --git a/crates/oxc_data_structures/src/fieldless_enum.rs b/crates/oxc_data_structures/src/fieldless_enum.rs new file mode 100644 index 0000000000000..d6f52b7f2c232 --- /dev/null +++ b/crates/oxc_data_structures/src/fieldless_enum.rs @@ -0,0 +1,147 @@ +/// Macro to define a fieldless enum with a `VARIANTS` constant listing all variants in declaration order. +/// +/// Wraps the enum definition and adds: +/// +/// ```ignore +/// impl EnumName { +/// pub const VARIANTS: [EnumName; N] = [EnumName::A, EnumName::B, ...]; +/// } +/// ``` +#[macro_export] +macro_rules! fieldless_enum { + ( + $(#[$meta:meta])* + $vis:vis enum $name:ident { + $( + $(#[$variant_meta:meta])* + $variant:ident $(= $discriminant:expr)? + ),* $(,)? + } + ) => { + $(#[$meta])* + $vis enum $name { + $( + $(#[$variant_meta])* + $variant $(= $discriminant)? + ),* + } + + impl $name { + /// All variants in declaration order. + $vis const VARIANTS: [$name; <[&str]>::len(&[$(stringify!($variant)),*])] = [ + $($name::$variant),* + ]; + } + }; +} + +pub use fieldless_enum; + +#[cfg(test)] +mod tests { + #[test] + fn basic() { + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Color { + Red, + Green, + Blue, + } + } + + assert_eq!(Color::VARIANTS.len(), 3); + assert_eq!(Color::VARIANTS, [Color::Red, Color::Green, Color::Blue]); + } + + #[test] + fn explicit_discriminants() { + const PENDING: u8 = 10; + + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u8)] + enum Status { + Active = 1, + Inactive = 5, + Pending = PENDING, + } + } + + assert_eq!(Status::VARIANTS.len(), 3); + assert_eq!(Status::VARIANTS, [Status::Active, Status::Inactive, Status::Pending]); + assert_eq!(Status::Active as u8, 1); + assert_eq!(Status::Inactive as u8, 5); + assert_eq!(Status::Pending as u8, 10); + } + + #[test] + fn variant_attributes() { + fieldless_enum! { + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] + enum WithDefault { + #[default] + First, + Second, + } + } + + assert_eq!(WithDefault::default(), WithDefault::First); + assert_eq!(WithDefault::VARIANTS, [WithDefault::First, WithDefault::Second]); + } + + #[test] + fn single_variant() { + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Unit { + Only, + } + } + + assert_eq!(Unit::VARIANTS.len(), 1); + assert_eq!(Unit::VARIANTS, [Unit::Only]); + } + + #[test] + fn zero_variants() { + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Never {} + } + + assert_eq!(Never::VARIANTS.len(), 0); + assert_eq!(Never::VARIANTS, []); + } + + #[test] + fn declaration_order() { + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u8)] + enum Shuffled { + C = 2, + A = 0, + B = 1, + } + } + + // `VARIANTS` follows declaration order, not discriminant order + assert_eq!(Shuffled::VARIANTS, [Shuffled::C, Shuffled::A, Shuffled::B]); + } + + #[test] + fn visibility() { + mod inner { + fieldless_enum! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Visible { + A, + B, + } + } + } + + assert_eq!(inner::Visible::VARIANTS.len(), 2); + } +} diff --git a/crates/oxc_data_structures/src/lib.rs b/crates/oxc_data_structures/src/lib.rs index eff5d04ac887e..20dc608a850b1 100644 --- a/crates/oxc_data_structures/src/lib.rs +++ b/crates/oxc_data_structures/src/lib.rs @@ -9,6 +9,9 @@ pub mod box_macros; #[cfg(feature = "code_buffer")] pub mod code_buffer; +#[cfg(feature = "fieldless_enum")] +pub mod fieldless_enum; + #[cfg(feature = "inline_string")] pub mod inline_string;