Skip to content

Commit

Permalink
docs(ast): document enum inheritance (#3192)
Browse files Browse the repository at this point in the history
Add more docs for AST type enum inheritance and the `inherit_variants!`
macro.

This covers the changes made in #3115.
  • Loading branch information
overlookmotel authored May 7, 2024
1 parent 82bd97d commit c6bd616
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 16 deletions.
46 changes: 36 additions & 10 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_ast/src/ast/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
13 changes: 12 additions & 1 deletion crates/oxc_ast/src/ast/macros.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/// Macro to inherit enum variants from another enum.
///
/// (for further details see <https://github.com/oxc-project/oxc/pull/3115>)
///
/// # Types which can be inherited
///
/// The following types' variants can be inherited:
///
/// * `Expression`
/// * `MemberExpression`
/// * `AssignmentTarget`
Expand All @@ -11,6 +16,8 @@
/// * `TSType`
/// * `TSTypeName`
///
/// # Expansion
///
/// ```
/// inherit_variants! {
/// #[repr(C, u8)]
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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.
///
/// ```
Expand Down
172 changes: 172 additions & 0 deletions crates/oxc_ast/src/ast/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading

0 comments on commit c6bd616

Please sign in to comment.