diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 3172ef702..c8a6161ff 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -69,7 +69,9 @@ impl<'a> Program<'a> { inherit_variants! { /// Expression /// -/// Inherits variants from [`MemberExpression`]. +/// Inherits variants from [`MemberExpression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -505,7 +507,9 @@ pub struct ArrayExpression<'a> { inherit_variants! { /// Array Expression Element /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -583,7 +587,9 @@ pub struct ObjectProperty<'a> { inherit_variants! { /// Property Key /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -973,7 +979,9 @@ pub struct SpreadElement<'a> { inherit_variants! { /// Argument /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1086,6 +1094,9 @@ inherit_variants! { /// Destructuring Assignment /// /// Inherits variants from [`SimpleAssignmentTarget`] and [`AssignmentTargetPattern`]. +/// See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1121,7 +1132,9 @@ pub use match_assignment_target; inherit_variants! { /// Simple Assignment Target /// -/// Inherits variants from [`MemberExpression`]. +/// Inherits variants from [`MemberExpression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1262,7 +1275,9 @@ pub struct AssignmentTargetRest<'a> { inherit_variants! { /// Assignment Target Maybe Default /// -/// Inherits variants from [`AssignmentTarget`]. +/// Inherits variants from [`AssignmentTarget`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1379,7 +1394,9 @@ pub struct ChainExpression<'a> { inherit_variants! { /// Chain Element /// -/// Inherits variants from [`MemberExpression`]. +/// Inherits variants from [`MemberExpression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1407,6 +1424,9 @@ inherit_variants! { /// Statement /// /// Inherits variants from [`Declaration`] and [`ModuleDeclaration`]. +/// See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1722,7 +1742,9 @@ pub struct ForStatement<'a> { inherit_variants! { /// For Statement Init /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1774,7 +1796,9 @@ pub struct ForOfStatement<'a> { inherit_variants! { /// For Statement Left /// -/// Inherits variants from [`AssignmentTarget`]. +/// Inherits variants from [`AssignmentTarget`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -2854,7 +2878,9 @@ impl<'a> ExportSpecifier<'a> { inherit_variants! { /// Export Default Declaration Kind /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index 8722901b7..9ef96ce2f 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -162,7 +162,9 @@ pub struct JSXExpressionContainer<'a> { inherit_variants! { /// JSX Expression /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] diff --git a/crates/oxc_ast/src/ast/macros.rs b/crates/oxc_ast/src/ast/macros.rs index 49c7422c8..04f517572 100644 --- a/crates/oxc_ast/src/ast/macros.rs +++ b/crates/oxc_ast/src/ast/macros.rs @@ -1,6 +1,11 @@ /// Macro to inherit enum variants from another enum. /// +/// (for further details see ) +/// +/// # Types which can be inherited +/// /// The following types' variants can be inherited: +/// /// * `Expression` /// * `MemberExpression` /// * `AssignmentTarget` @@ -11,6 +16,8 @@ /// * `TSType` /// * `TSTypeName` /// +/// # Expansion +/// /// ``` /// inherit_variants! { /// #[repr(C, u8)] @@ -240,12 +247,15 @@ macro_rules! inherit_variants { $($(#[$variant_attr])* $variant_name($variant_type) = $variant_discrim,)* /// Inherited from [`MemberExpression`]. + /// /// `MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]` ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, /// Inherited from [`MemberExpression`]. + /// /// `MemberExpression[?Yield, ?Await] . IdentifierName` StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, /// Inherited from [`MemberExpression`]. + /// /// `MemberExpression[?Yield, ?Await] . PrivateIdentifier` PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, @@ -712,7 +722,8 @@ pub(crate) use inherit_variants; /// # SAFETY /// Both enums must be `#[repr(C, u8)]` or using this macro is unsound. /// -/// # Example +/// # Expansion +/// /// NB: For illustration only - `Statement` and `Declaration` in reality share 9 variants, not 2. /// /// ``` diff --git a/crates/oxc_ast/src/ast/mod.rs b/crates/oxc_ast/src/ast/mod.rs index e0954106c..f88231d37 100644 --- a/crates/oxc_ast/src/ast/mod.rs +++ b/crates/oxc_ast/src/ast/mod.rs @@ -1,4 +1,176 @@ //! AST Definitions +//! +//! # Enum inheritance +//! +//! Some enum AST types inherit variants from other enums using the `inherit_variants!` macro. +//! +//! "Inherit" means: If `enum Y` inherits the variants of `enum X`, +//! then all `X`'s variants are duplicated as variants of `Y`. +//! +//! This is mainly an explanation of the consumer-facing API. For further details on implementation, +//! see comments in `src/ast/macros.rs`. +//! +//! ## Defining enum inheritance +//! +//! Instead of nested enums: +//! +//! ``` +//! pub enum Expression<'a> { +//! BooleanLiteral(Box<'a, BooleanLiteral>), +//! NullLiteral(Box<'a, NullLiteral>), +//! // ...more variants +//! MemberExpression(MemberExpression<'a>), +//! } +//! +//! pub enum MemberExpression<'a> { +//! ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>), +//! StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>), +//! PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>), +//! } +//! ``` +//! +//! We define the types using `inherit_variants!` macro: +//! +//! ``` +//! inherit_variants! { +//! #[repr(C, u8)] +//! pub enum Expression<'a> { +//! BooleanLiteral(Box<'a, BooleanLiteral>) = 0, +//! NullLiteral(Box<'a, NullLiteral>) = 1, +//! // ...more variants +//! @inherit MemberExpression, +//! } +//! } +//! +//! #[repr(C, u8)] +//! pub enum MemberExpression<'a> { +//! ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, +//! StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, +//! PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, +//! } +//! ``` +//! +//! `inherit_variants!` macro expands `Expression` to: +//! +//! ``` +//! #[repr(C, u8)] +//! pub enum Expression<'a> { +//! BooleanLiteral(Box<'a, BooleanLiteral>) = 0, +//! NullLiteral(Box<'a, NullLiteral>) = 1, +//! // ...more variants +//! +//! // Inherited from `MemberExpression` +//! ComputedMemberExpression(Box<'a, ComputedMemberExpression<'a>>) = 48, +//! StaticMemberExpression(Box<'a, StaticMemberExpression<'a>>) = 49, +//! PrivateFieldExpression(Box<'a, PrivateFieldExpression<'a>>) = 50, +//! } +//! +//! shared_enum_variants!( +//! Expression, MemberExpression, +//! is_member_expression, +//! as_member_expression, as_member_expression_mut, +//! to_member_expression, to_member_expression_mut, +//! [ComputedMemberExpression, StaticMemberExpression, PrivateFieldExpression] +//! ) +//! ``` +//! +//! See `src/ast/macros.rs` for what `shared_enum_variants!` macro expands to. +//! It provides the APIs listed below. +//! +//! ## Using inherited variants +//! +//! #### Creation +//! +//! ``` +//! // Old +//! let expr = Expression::MemberExpression( +//! MemberExpression::ComputedMemberExpression(computed_member_expr) +//! ); +//! +//! // New +//! let expr = Expression::ComputedMemberExpression(computed_member_expr); +//! ``` +//! +//! #### Conversion +//! +//! ``` +//! // Old +//! let expr = Expression::MemberExpression(member_expr); +//! +//! // New +//! let expr = Expression::from(member_expr); +//! ``` +//! +//! ``` +//! // Old +//! let maybe_member_expr = match expr { +//! Expression::MemberExpression(member_expr) => Some(member_expr), +//! _ => None, +//! }; +//! +//! // New +//! let maybe_member_expr = MemberExpression::try_from(expr).ok(); +//! ``` +//! +//! #### Testing +//! +//! ``` +//! // Old +//! if matches!(expr, Expression::MemberExpression(_)) { } +//! +//! // New +//! if expr.is_member_expression() { } +//! // or +//! if matches!(expr, match_member_expression!(Expression)) { } +//! ``` +//! +//! #### Branching +//! +//! ``` +//! // Old +//! if let Expression::MemberExpression(member_expr) = &expr { } +//! +//! // New +//! if let Some(member_expr) = expr.as_member_expression() { } +//! ``` +//! +//! #### Matching +//! +//! ``` +//! // Old +//! match get_expression() { +//! Expression::MemberExpression(member_expr) => visitor.visit(member_expr), +//! } +//! +//! // New (exhaustive match) +//! match get_expression() { +//! expr @ match_member_expression!(Expression) => visitor.visit(expr.to_member_expression()), +//! } +//! +//! // New (alternative) +//! match get_expression() { +//! expr if expr.is_member_expression() => visitor.visit(expr.to_member_expression()), +//! } +//! ``` +//! +//! ## Why `#[repr(C, u8)]` on enums? +//! +//! `#[repr(C, u8)]` allows us to define the discriminants for variants in both the "inherited" +//! and "inheritee" enums. +//! +//! The discriminants and "payloads" match between the 2 types for the inherited variants. +//! Therefore `MemberExpression::ComputedMemberExpression` and `Expression::ComputedMemberExpression` +//! have identical representations in memory, and a `MemberExpression` can be converted to an +//! `Expression` with a zero-cost transmute. +//! +//! The APIs listed above use this property. +//! +//! It is **essential** that the discriminants and "payload" types match between the "inherited" +//! and "inheritee" types, or using the APIs below would be instant UB. +//! The `shared_enum_variants!` macro generates const assertions to ensure +//! these invariants are upheld, and it will be caught at compile time if they don't. +//! +//! If you are seeing compile-time errors in `src/ast/macros.rs`, this will be the cause. mod js; mod jsx; diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 3096882fd..a181c0276 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -70,7 +70,9 @@ pub struct TSEnumMember<'a> { inherit_variants! { /// TS Enum Member Name /// -/// Inherits variants from [`Expression`]. +/// Inherits variants from [`Expression`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -368,7 +370,9 @@ pub struct TSRestType<'a> { inherit_variants! { /// TS Tuple Element /// -/// Inherits variants from [`TSType`]. +/// Inherits variants from [`TSType`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -918,7 +922,9 @@ pub struct TSTypeQuery<'a> { inherit_variants! { /// TS Type Query Expr Name /// -/// Inherits variants from [`TSTypeName`]. +/// Inherits variants from [`TSTypeName`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)] @@ -1085,7 +1091,9 @@ pub struct TSImportEqualsDeclaration<'a> { inherit_variants! { /// TS Module Reference /// -/// Inherits variants from [`TSTypeName`]. +/// Inherits variants from [`TSTypeName`]. See [`ast` module docs] for explanation of inheritance. +/// +/// [`ast` module docs]: `super` #[visited_node] #[repr(C, u8)] #[derive(Debug, Hash)]