diff --git a/crates/oxc_traverse/src/context/bound_identifier.rs b/crates/oxc_traverse/src/context/bound_identifier.rs index dcf1beb8ff979..2761a89c80b09 100644 --- a/crates/oxc_traverse/src/context/bound_identifier.rs +++ b/crates/oxc_traverse/src/context/bound_identifier.rs @@ -10,6 +10,8 @@ use oxc_syntax::{reference::ReferenceFlags, symbol::SymbolId}; use crate::TraverseCtx; +use super::MaybeBoundIdentifier; + /// Info about a binding, from which one can create a `BindingIdentifier` or `IdentifierReference`s. /// /// Typical usage: @@ -54,6 +56,11 @@ impl<'a> BoundIdentifier<'a> { Self { name: ident.name.clone(), symbol_id: ident.symbol_id() } } + /// Convert `BoundIdentifier` to `MaybeBoundIdentifier` + pub fn to_maybe_bound_identifier(&self) -> MaybeBoundIdentifier<'a> { + MaybeBoundIdentifier::new(self.name.clone(), Some(self.symbol_id)) + } + /// Create `BindingIdentifier` for this binding pub fn create_binding_identifier(&self, ctx: &TraverseCtx<'a>) -> BindingIdentifier<'a> { ctx.ast.binding_identifier_with_symbol_id(SPAN, self.name.clone(), self.symbol_id) diff --git a/crates/oxc_traverse/src/context/maybe_bound_identifier.rs b/crates/oxc_traverse/src/context/maybe_bound_identifier.rs new file mode 100644 index 0000000000000..c604503217b92 --- /dev/null +++ b/crates/oxc_traverse/src/context/maybe_bound_identifier.rs @@ -0,0 +1,247 @@ +use oxc_ast::ast::{AssignmentTarget, Expression, IdentifierReference}; +use oxc_span::{Atom, Span, SPAN}; +use oxc_syntax::{reference::ReferenceFlags, symbol::SymbolId}; + +use crate::TraverseCtx; + +use super::BoundIdentifier; + +/// A factory for generating `IdentifierReference`s. +/// +/// Typical usage: +/// +/// ```rs +/// // Create `MaybeBoundIdentifier` from an existing `IdentifierReference` +/// let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx); +/// +/// // Generate `IdentifierReference`s and insert them into AST +/// assign_expr.left = binding.create_write_target(ctx); +/// assign_expr.right = binding.create_read_expression(ctx); +/// ``` +/// +/// Notes: +/// +/// * The original `IdentifierReference` must also be used in the AST, or it'll be a dangling reference. +/// * `MaybeBoundIdentifier` is smaller than `IdentifierReference`, so takes less memory when you store +/// it for later use. +/// * `MaybeBoundIdentifier` re-uses the same `Atom` for all `BindingIdentifier` / `IdentifierReference`s +/// created from it. +/// * `MaybeBoundIdentifier` looks up the `SymbolId` for the reference only once, +/// rather than `TraverseCtx::clone_identifier_reference` which looks it up every time you create +/// an `IdentifierReference`. +#[derive(Debug, Clone)] +pub struct MaybeBoundIdentifier<'a> { + pub name: Atom<'a>, + pub symbol_id: Option, +} + +impl<'a> MaybeBoundIdentifier<'a> { + /// Create `MaybeBoundIdentifier` for `name` and `Option` + pub fn new(name: Atom<'a>, symbol_id: Option) -> Self { + Self { name, symbol_id } + } + + /// Create `MaybeBoundIdentifier` from an `IdentifierReference` + pub fn from_identifier_reference( + ident: &IdentifierReference<'a>, + ctx: &TraverseCtx<'a>, + ) -> Self { + let symbol_id = ctx.symbols().get_reference(ident.reference_id()).symbol_id(); + Self { name: ident.name.clone(), symbol_id } + } + + /// Convert `MaybeBoundIdentifier` to `BoundIdentifier`. + /// + /// Returns `None` if symbol is not bound. + pub fn to_bound_identifier(&self) -> Option> { + self.symbol_id.map(|symbol_id| BoundIdentifier::new(self.name.clone(), symbol_id)) + } + + // --- Read only --- + + /// Create `IdentifierReference` referencing this binding, which is read from, with dummy `Span` + pub fn create_read_reference(&self, ctx: &mut TraverseCtx<'a>) -> IdentifierReference<'a> { + self.create_spanned_read_reference(SPAN, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is read from, with dummy `Span` + pub fn create_read_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { + self.create_spanned_read_expression(SPAN, ctx) + } + + /// Create `IdentifierReference` referencing this binding, which is read from, with specified `Span` + pub fn create_spanned_read_reference( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + self.create_spanned_reference(span, ReferenceFlags::Read, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is read from, with specified `Span` + pub fn create_spanned_read_expression( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.create_spanned_expression(span, ReferenceFlags::Read, ctx) + } + + // --- Write only --- + + /// Create `IdentifierReference` referencing this binding, which is written to, with dummy `Span` + pub fn create_write_reference(&self, ctx: &mut TraverseCtx<'a>) -> IdentifierReference<'a> { + self.create_spanned_write_reference(SPAN, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is written to, with dummy `Span` + pub fn create_write_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { + self.create_spanned_write_expression(SPAN, ctx) + } + + /// Create `AssignmentTarget` referencing this binding, which is written to, with dummy `Span` + pub fn create_write_target(&self, ctx: &mut TraverseCtx<'a>) -> AssignmentTarget<'a> { + self.create_spanned_write_target(SPAN, ctx) + } + + /// Create `IdentifierReference` referencing this binding, which is written to, with specified `Span` + pub fn create_spanned_write_reference( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + self.create_spanned_reference(span, ReferenceFlags::Write, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is written to, with specified `Span` + pub fn create_spanned_write_expression( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.create_spanned_expression(span, ReferenceFlags::Write, ctx) + } + + /// Create `AssignmentTarget` referencing this binding, which is written to, with specified `Span` + pub fn create_spanned_write_target( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> AssignmentTarget<'a> { + self.create_spanned_target(span, ReferenceFlags::Write, ctx) + } + + // --- Read and write --- + + /// Create `IdentifierReference` referencing this binding, which is read from + written to, + /// with dummy `Span` + pub fn create_read_write_reference( + &self, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + self.create_spanned_read_write_reference(SPAN, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is read from + written to, + /// with dummy `Span` + pub fn create_read_write_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { + self.create_spanned_read_write_expression(SPAN, ctx) + } + + /// Create `AssignmentTarget` referencing this binding, which is read from + written to, + /// with dummy `Span` + pub fn create_read_write_target(&self, ctx: &mut TraverseCtx<'a>) -> AssignmentTarget<'a> { + self.create_spanned_read_write_target(SPAN, ctx) + } + + /// Create `IdentifierReference` referencing this binding, which is read from + written to, + /// with specified `Span` + pub fn create_spanned_read_write_reference( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + self.create_spanned_reference(span, ReferenceFlags::Read | ReferenceFlags::Write, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, which is read from + written to, + /// with specified `Span` + pub fn create_spanned_read_write_expression( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.create_spanned_expression(span, ReferenceFlags::Read | ReferenceFlags::Write, ctx) + } + + /// Create `AssignmentTarget` referencing this binding, which is read from + written to, + /// with specified `Span` + pub fn create_spanned_read_write_target( + &self, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> AssignmentTarget<'a> { + self.create_spanned_target(span, ReferenceFlags::Read | ReferenceFlags::Write, ctx) + } + + // --- Specified ReferenceFlags --- + + /// Create `IdentifierReference` referencing this binding, with specified `ReferenceFlags` + pub fn create_reference( + &self, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + self.create_spanned_reference(SPAN, flags, ctx) + } + + /// Create `Expression::Identifier` referencing this binding, with specified `ReferenceFlags` + pub fn create_expression( + &self, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.create_spanned_expression(SPAN, flags, ctx) + } + + /// Create `AssignmentTarget` referencing this binding, with specified `ReferenceFlags` + pub fn create_target( + &self, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> AssignmentTarget<'a> { + self.create_spanned_target(SPAN, flags, ctx) + } + + /// Create `IdentifierReference` referencing this binding, with specified `Span` and `ReferenceFlags` + pub fn create_spanned_reference( + &self, + span: Span, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> IdentifierReference<'a> { + ctx.create_reference_id(span, self.name.clone(), self.symbol_id, flags) + } + + /// Create `Expression::Identifier` referencing this binding, with specified `Span` and `ReferenceFlags` + pub fn create_spanned_expression( + &self, + span: Span, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let ident = self.create_spanned_reference(span, flags, ctx); + Expression::Identifier(ctx.alloc(ident)) + } + + /// Create `Expression::Identifier` referencing this binding, with specified `Span` and `ReferenceFlags` + pub fn create_spanned_target( + &self, + span: Span, + flags: ReferenceFlags, + ctx: &mut TraverseCtx<'a>, + ) -> AssignmentTarget<'a> { + let ident = self.create_spanned_reference(span, flags, ctx); + AssignmentTarget::AssignmentTargetIdentifier(ctx.alloc(ident)) + } +} diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 5a06285a0723c..e0ae80738f3ea 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -18,10 +18,12 @@ use crate::{ mod ancestry; mod bound_identifier; +mod maybe_bound_identifier; mod scoping; use ancestry::PopToken; pub use ancestry::TraverseAncestry; pub use bound_identifier::BoundIdentifier; +pub use maybe_bound_identifier::MaybeBoundIdentifier; pub use scoping::TraverseScoping; /// Traverse context. diff --git a/crates/oxc_traverse/src/lib.rs b/crates/oxc_traverse/src/lib.rs index ade032ca9fcf5..b5fc9d4ce9650 100644 --- a/crates/oxc_traverse/src/lib.rs +++ b/crates/oxc_traverse/src/lib.rs @@ -66,7 +66,9 @@ use oxc_semantic::{ScopeTree, SymbolTable}; pub mod ast_operations; mod context; -pub use context::{BoundIdentifier, TraverseAncestry, TraverseCtx, TraverseScoping}; +pub use context::{ + BoundIdentifier, MaybeBoundIdentifier, TraverseAncestry, TraverseCtx, TraverseScoping, +}; mod generated { pub mod ancestor;