Skip to content

Commit b3dceae

Browse files
committed
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.
1 parent 37e7ca6 commit b3dceae

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

crates/oxc_data_structures/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ all = [
3030
"assert_unchecked",
3131
"box_macros",
3232
"code_buffer",
33+
"fieldless_enum",
3334
"inline_string",
3435
"rope",
3536
"slice_iter",
@@ -38,6 +39,7 @@ all = [
3839
assert_unchecked = []
3940
box_macros = []
4041
code_buffer = ["assert_unchecked"]
42+
fieldless_enum = []
4143
inline_string = ["assert_unchecked"]
4244
rope = ["dep:ropey"]
4345
slice_iter = ["assert_unchecked"]

crates/oxc_data_structures/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This crate provides specialized data structures and utilities that are used thro
1414
- **Slice iterators**: Enhanced iteration capabilities for slices
1515
- **Rope data structure**: Efficient text manipulation for large documents
1616
- **Box macros**: Macros for creating boxed arrays / slices (similar to `vec!` macro)
17+
- **Fieldless enums macro**: Macro for creating enums with a `VARIANTS` constant listing all variants
1718

1819
## Architecture
1920

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/// Macro to define a fieldless enum with a `VARIANTS` constant listing all variants in declaration order.
2+
///
3+
/// Wraps the enum definition and adds:
4+
///
5+
/// ```ignore
6+
/// impl EnumName {
7+
/// pub const VARIANTS: [EnumName; N] = [EnumName::A, EnumName::B, ...];
8+
/// }
9+
/// ```
10+
#[macro_export]
11+
macro_rules! fieldless_enum {
12+
(
13+
$(#[$meta:meta])*
14+
$vis:vis enum $name:ident {
15+
$(
16+
$(#[$variant_meta:meta])*
17+
$variant:ident $(= $discriminant:expr)?
18+
),* $(,)?
19+
}
20+
) => {
21+
$(#[$meta])*
22+
$vis enum $name {
23+
$(
24+
$(#[$variant_meta])*
25+
$variant $(= $discriminant)?
26+
),*
27+
}
28+
29+
impl $name {
30+
/// All variants in declaration order.
31+
$vis const VARIANTS: [$name; <[&str]>::len(&[$(stringify!($variant)),*])] = [
32+
$($name::$variant),*
33+
];
34+
}
35+
};
36+
}
37+
38+
pub use fieldless_enum;
39+
40+
#[cfg(test)]
41+
mod tests {
42+
#[test]
43+
fn basic() {
44+
fieldless_enum! {
45+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46+
enum Color {
47+
Red,
48+
Green,
49+
Blue,
50+
}
51+
}
52+
53+
assert_eq!(Color::VARIANTS.len(), 3);
54+
assert_eq!(Color::VARIANTS, [Color::Red, Color::Green, Color::Blue]);
55+
}
56+
57+
#[test]
58+
fn explicit_discriminants() {
59+
const PENDING: u8 = 10;
60+
61+
fieldless_enum! {
62+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63+
#[repr(u8)]
64+
enum Status {
65+
Active = 1,
66+
Inactive = 5,
67+
Pending = PENDING,
68+
}
69+
}
70+
71+
assert_eq!(Status::VARIANTS.len(), 3);
72+
assert_eq!(Status::VARIANTS, [Status::Active, Status::Inactive, Status::Pending]);
73+
assert_eq!(Status::Active as u8, 1);
74+
assert_eq!(Status::Inactive as u8, 5);
75+
assert_eq!(Status::Pending as u8, 10);
76+
}
77+
78+
#[test]
79+
fn variant_attributes() {
80+
fieldless_enum! {
81+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
82+
enum WithDefault {
83+
#[default]
84+
First,
85+
Second,
86+
}
87+
}
88+
89+
assert_eq!(WithDefault::default(), WithDefault::First);
90+
assert_eq!(WithDefault::VARIANTS, [WithDefault::First, WithDefault::Second]);
91+
}
92+
93+
#[test]
94+
fn single_variant() {
95+
fieldless_enum! {
96+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97+
enum Unit {
98+
Only,
99+
}
100+
}
101+
102+
assert_eq!(Unit::VARIANTS.len(), 1);
103+
assert_eq!(Unit::VARIANTS, [Unit::Only]);
104+
}
105+
106+
#[test]
107+
fn zero_variants() {
108+
fieldless_enum! {
109+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110+
enum Never {}
111+
}
112+
113+
assert_eq!(Never::VARIANTS.len(), 0);
114+
assert_eq!(Never::VARIANTS, []);
115+
}
116+
117+
#[test]
118+
fn declaration_order() {
119+
fieldless_enum! {
120+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121+
#[repr(u8)]
122+
enum Shuffled {
123+
C = 2,
124+
A = 0,
125+
B = 1,
126+
}
127+
}
128+
129+
// `VARIANTS` follows declaration order, not discriminant order
130+
assert_eq!(Shuffled::VARIANTS, [Shuffled::C, Shuffled::A, Shuffled::B]);
131+
}
132+
133+
#[test]
134+
fn visibility() {
135+
mod inner {
136+
fieldless_enum! {
137+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138+
pub enum Visible {
139+
A,
140+
B,
141+
}
142+
}
143+
}
144+
145+
assert_eq!(inner::Visible::VARIANTS.len(), 2);
146+
}
147+
}

crates/oxc_data_structures/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ pub mod box_macros;
99
#[cfg(feature = "code_buffer")]
1010
pub mod code_buffer;
1111

12+
#[cfg(feature = "fieldless_enum")]
13+
pub mod fieldless_enum;
14+
1215
#[cfg(feature = "inline_string")]
1316
pub mod inline_string;
1417

0 commit comments

Comments
 (0)