mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(traverse): mutable access to scopes tree + symbol table (#3314)
Allow mutable access to scopes tree and symbol table. Closes #3189. This completes the v1 scopes-in-traverse implementation, and provides all the primitives required to implement the missing APIs listed in https://github.com/oxc-project/oxc/discussions/3251. Performance is abysmal, as noted in #3304, but we can fix that later on by taking `Semantic` out of the picture, or optimizing it.
This commit is contained in:
parent
421107aa82
commit
0c09047111
1 changed files with 85 additions and 2 deletions
|
|
@ -13,11 +13,13 @@ const INITIAL_STACK_CAPACITY: usize = 64; // 64 entries = 1 KiB
|
|||
///
|
||||
/// Provides ability to:
|
||||
/// * Query parent/ancestor of current node via [`parent`], [`ancestor`], [`find_ancestor`].
|
||||
/// * Get scopes tree and symbols table via [`scopes`], [`symbols`], [`find_scope`],
|
||||
/// [`find_scope_by_flags`].
|
||||
/// * Get scopes tree and symbols table via [`scopes`], [`symbols`], [`scopes_mut`], [`symbols_mut`],
|
||||
/// [`find_scope`], [`find_scope_by_flags`].
|
||||
/// * Create AST nodes via AST builder [`ast`].
|
||||
/// * Allocate into arena via [`alloc`].
|
||||
///
|
||||
/// # Namespaced APIs
|
||||
///
|
||||
/// All APIs are provided via 2 routes:
|
||||
///
|
||||
/// 1. Directly on `TraverseCtx`.
|
||||
|
|
@ -29,11 +31,64 @@ const INITIAL_STACK_CAPACITY: usize = 64; // 64 entries = 1 KiB
|
|||
/// | `ctx.current_scope_id()` | `ctx.scoping.current_scope_id()` |
|
||||
/// | `ctx.alloc(thing)` | `ctx.ast.alloc(thing)` |
|
||||
///
|
||||
/// Purpose of the "namespaces" is to support if you want to mutate scope tree or symbol table
|
||||
/// while holding an `&Ancestor`, or AST nodes obtained from an `&Ancestor`.
|
||||
///
|
||||
/// For example, this will not compile because it attempts to borrow `ctx`
|
||||
/// immutably and mutably at same time:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// use oxc_ast::ast::*;
|
||||
/// use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
///
|
||||
/// struct MyTransform;
|
||||
/// impl<'a> Traverse<'a> for MyTransform {
|
||||
/// fn enter_unary_expression(&mut self, unary_expr: &mut UnaryExpression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
/// // `right` is ultimately borrowed from `ctx`
|
||||
/// let right = match ctx.parent() {
|
||||
/// Ancestor::BinaryExpressionLeft(bin_expr) => bin_expr.right(),
|
||||
/// _ => return,
|
||||
/// };
|
||||
///
|
||||
/// // Won't compile! `ctx.scopes_mut()` attempts to mut borrow `ctx`
|
||||
/// // while it's already borrowed by `right`.
|
||||
/// let scope_tree_mut = ctx.scopes_mut();
|
||||
///
|
||||
/// // Use `right` later on
|
||||
/// dbg!(right);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You can fix this by using the "namespaced" methods instead.
|
||||
/// This works because you can borrow `ctx.ancestry` and `ctx.scoping` simultaneously:
|
||||
///
|
||||
/// ```
|
||||
/// use oxc_ast::ast::*;
|
||||
/// use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
///
|
||||
/// struct MyTransform;
|
||||
/// impl<'a> Traverse<'a> for MyTransform {
|
||||
/// fn enter_unary_expression(&mut self, unary_expr: &mut UnaryExpression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
/// let right = match ctx.ancestry.parent() {
|
||||
/// Ancestor::BinaryExpressionLeft(bin_expr) => bin_expr.right(),
|
||||
/// _ => return,
|
||||
/// };
|
||||
///
|
||||
/// let scope_tree_mut = ctx.scoping.scopes_mut();
|
||||
///
|
||||
/// dbg!(right);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`parent`]: `TraverseCtx::parent`
|
||||
/// [`ancestor`]: `TraverseCtx::ancestor`
|
||||
/// [`find_ancestor`]: `TraverseCtx::find_ancestor`
|
||||
/// [`scopes`]: `TraverseCtx::scopes`
|
||||
/// [`symbols`]: `TraverseCtx::symbols`
|
||||
/// [`scopes_mut`]: `TraverseCtx::scopes_mut`
|
||||
/// [`symbols_mut`]: `TraverseCtx::symbols_mut`
|
||||
/// [`find_scope`]: `TraverseCtx::find_scope`
|
||||
/// [`find_scope_by_flags`]: `TraverseCtx::find_scope_by_flags`
|
||||
/// [`ast`]: `TraverseCtx::ast`
|
||||
|
|
@ -191,6 +246,14 @@ impl<'a> TraverseCtx<'a> {
|
|||
self.scoping.scopes()
|
||||
}
|
||||
|
||||
/// Get mutable scopes tree.
|
||||
///
|
||||
/// Shortcut for `ctx.scoping.scopes_mut`.
|
||||
#[inline]
|
||||
pub fn scopes_mut(&mut self) -> &mut ScopeTree {
|
||||
self.scoping.scopes_mut()
|
||||
}
|
||||
|
||||
/// Get symbols table.
|
||||
///
|
||||
/// Shortcut for `ctx.scoping.symbols`.
|
||||
|
|
@ -199,6 +262,14 @@ impl<'a> TraverseCtx<'a> {
|
|||
self.scoping.symbols()
|
||||
}
|
||||
|
||||
/// Get mutable symbols table.
|
||||
///
|
||||
/// Shortcut for `ctx.scoping.symbols_mut`.
|
||||
#[inline]
|
||||
pub fn symbols_mut(&mut self) -> &mut SymbolTable {
|
||||
self.scoping.symbols_mut()
|
||||
}
|
||||
|
||||
/// Walk up trail of scopes to find a scope.
|
||||
///
|
||||
/// `finder` is called with `ScopeId`.
|
||||
|
|
@ -423,12 +494,24 @@ impl TraverseScoping {
|
|||
&self.scopes
|
||||
}
|
||||
|
||||
/// Get mutable scopes tree
|
||||
#[inline]
|
||||
pub fn scopes_mut(&mut self) -> &mut ScopeTree {
|
||||
&mut self.scopes
|
||||
}
|
||||
|
||||
/// Get symbols table
|
||||
#[inline]
|
||||
pub fn symbols(&self) -> &SymbolTable {
|
||||
&self.symbols
|
||||
}
|
||||
|
||||
/// Get mutable symbols table
|
||||
#[inline]
|
||||
pub fn symbols_mut(&mut self) -> &mut SymbolTable {
|
||||
&mut self.symbols
|
||||
}
|
||||
|
||||
/// Walk up trail of scopes to find a scope.
|
||||
///
|
||||
/// `finder` is called with `ScopeId`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue