mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 12:51:57 +00:00
refactor(traverse): Traverse produce scopes tree using Semantic (#3304)
`Traverse` use `Semantic` to construct scopes tree and expose it to visitors via `TraverseCtx`. Currently scopes tree is immutable. Will expose it as a mutable in a follow-on. This is extremely inefficient. Semantic does all kinds of stuff (control flow graph etc) which `Traverse` doesn't need, and `Traverse` just throws away all that work after semantic has done it. Intent here is to get a working implementation first, and then to do another pass later on to improve performance.
This commit is contained in:
parent
6f3b1c8724
commit
05c71d20b1
8 changed files with 369 additions and 188 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1707,6 +1707,7 @@ dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
|
"oxc_semantic",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
"oxc_syntax",
|
"oxc_syntax",
|
||||||
"trybuild",
|
"trybuild",
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use std::{path::Path, rc::Rc};
|
||||||
|
|
||||||
use es2015::ES2015;
|
use es2015::ES2015;
|
||||||
use oxc_allocator::{Allocator, Vec};
|
use oxc_allocator::{Allocator, Vec};
|
||||||
use oxc_ast::{ast::*, Trivias};
|
use oxc_ast::{ast::*, AstBuilder, Trivias};
|
||||||
use oxc_diagnostics::Error;
|
use oxc_diagnostics::Error;
|
||||||
use oxc_span::SourceType;
|
use oxc_span::SourceType;
|
||||||
use oxc_traverse::{traverse_mut, Traverse, TraverseCtx};
|
use oxc_traverse::{traverse_mut, Traverse, TraverseCtx};
|
||||||
|
|
@ -78,8 +78,9 @@ impl<'a> Transformer<'a> {
|
||||||
///
|
///
|
||||||
/// Returns `Vec<Error>` if any errors were collected during the transformation.
|
/// Returns `Vec<Error>` if any errors were collected during the transformation.
|
||||||
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), std::vec::Vec<Error>> {
|
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), std::vec::Vec<Error>> {
|
||||||
let allocator = self.ctx.ast.allocator;
|
let TransformCtx { ast: AstBuilder { allocator }, source_text, source_type, .. } =
|
||||||
traverse_mut(&mut self, program, allocator);
|
*self.ctx;
|
||||||
|
traverse_mut(&mut self, program, source_text, source_type, allocator);
|
||||||
|
|
||||||
let errors = self.ctx.take_errors();
|
let errors = self.ctx.take_errors();
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ impl<'a> ReactJsxSelf<'a> {
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn is_inside_constructor(&self, ctx: &TraverseCtx<'a>) -> bool {
|
fn is_inside_constructor(&self, ctx: &TraverseCtx<'a>) -> bool {
|
||||||
ctx.find_scope(|scope| {
|
ctx.find_scope_by_flags(|flags| {
|
||||||
if scope.is_block() || scope.is_arrow() {
|
if flags.is_block() || flags.is_arrow() {
|
||||||
return FinderRet::Continue;
|
return FinderRet::Continue;
|
||||||
}
|
}
|
||||||
FinderRet::Found(scope.is_constructor())
|
FinderRet::Found(flags.is_constructor())
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ doctest = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
oxc_allocator = { workspace = true }
|
oxc_allocator = { workspace = true }
|
||||||
oxc_ast = { workspace = true }
|
oxc_ast = { workspace = true }
|
||||||
|
oxc_semantic = { workspace = true }
|
||||||
oxc_span = { workspace = true }
|
oxc_span = { workspace = true }
|
||||||
oxc_syntax = { workspace = true }
|
oxc_syntax = { workspace = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,15 @@ export default function generateWalkFunctionsCode(types) {
|
||||||
clippy::semicolon_if_nothing_returned,
|
clippy::semicolon_if_nothing_returned,
|
||||||
clippy::ptr_as_ptr,
|
clippy::ptr_as_ptr,
|
||||||
clippy::borrow_as_ptr,
|
clippy::borrow_as_ptr,
|
||||||
clippy::cast_ptr_alignment,
|
clippy::cast_ptr_alignment
|
||||||
clippy::needless_borrow
|
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
use oxc_allocator::Vec;
|
use oxc_allocator::Vec;
|
||||||
#[allow(clippy::wildcard_imports)]
|
#[allow(clippy::wildcard_imports)]
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
use oxc_span::SourceType;
|
use oxc_syntax::scope::ScopeId;
|
||||||
use oxc_syntax::scope::ScopeFlags;
|
|
||||||
|
|
||||||
use crate::{ancestor::{self, AncestorType}, Ancestor, Traverse, TraverseCtx};
|
use crate::{ancestor::{self, AncestorType}, Ancestor, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
|
@ -49,11 +49,17 @@ export default function generateWalkFunctionsCode(types) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateWalkForStruct(type, types) {
|
function generateWalkForStruct(type, types) {
|
||||||
const visitedFields = type.fields.filter(field => field.innerTypeName in types);
|
let scopeIdField;
|
||||||
|
const visitedFields = type.fields.filter(field => {
|
||||||
|
if (field.name === 'scope_id' && field.typeName === `Cell<Option<ScopeId>>`) {
|
||||||
|
scopeIdField = field;
|
||||||
|
}
|
||||||
|
return field.innerTypeName in types;
|
||||||
|
});
|
||||||
|
|
||||||
const {scopeArgs} = type;
|
const {scopeArgs} = type;
|
||||||
let scopeEnterField, enterScopeCode, exitScopeCode;
|
let scopeEnterField, enterScopeCode = '', exitScopeCode = '';
|
||||||
if (scopeArgs) {
|
if (scopeArgs && scopeIdField) {
|
||||||
// Get field to enter scope before
|
// Get field to enter scope before
|
||||||
const enterFieldName = scopeArgs.enter_scope_before;
|
const enterFieldName = scopeArgs.enter_scope_before;
|
||||||
if (enterFieldName) {
|
if (enterFieldName) {
|
||||||
|
|
@ -63,33 +69,26 @@ function generateWalkForStruct(type, types) {
|
||||||
`\`visited_node\` attr says to enter scope before field '${enterFieldName}' `
|
`\`visited_node\` attr says to enter scope before field '${enterFieldName}' `
|
||||||
+ `in '${type.name}', but that field is not visited`
|
+ `in '${type.name}', but that field is not visited`
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
scopeEnterField = visitedFields[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertExpressionToUsePointers = arg => arg.replace(
|
// TODO: Maybe this isn't quite right. `scope_id` fields are `Cell<Option<ScopeId>>`,
|
||||||
/(^|[^a-zA-Z0-9_])self\.(?:r#)?([A-Za-z0-9_]+)/g,
|
// so visitor is able to alter the `scope_id` of a node higher up the tree,
|
||||||
(_, before, fieldName) => {
|
// but we don't take that into account.
|
||||||
const field = type.fields.find(field => field.name === fieldName);
|
// Visitor should not do that though, so maybe it's OK.
|
||||||
assert(`Cannot parse conditional in visited_node args: '${arg}' for ${type.name}`);
|
// In final version, we should not make `scope_id` fields `Cell`s to prevent this.
|
||||||
return `${before}(&*(${makeFieldCode(field)}))`;
|
enterScopeCode = `
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*(${makeFieldCode(scopeIdField)})).get() {
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
}
|
}
|
||||||
);
|
`;
|
||||||
|
|
||||||
let scopeType = convertExpressionToUsePointers(scopeArgs.scope);
|
exitScopeCode = `
|
||||||
if (scopeArgs.strict_if) {
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
scopeType += `.with_strict_mode(${convertExpressionToUsePointers(scopeArgs.strict_if)})`;
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
enterScopeCode = `ctx.push_scope_stack(${scopeType});`;
|
|
||||||
exitScopeCode = `ctx.pop_scope_stack();`;
|
|
||||||
if (scopeArgs.scope_if) {
|
|
||||||
enterScopeCode = `
|
|
||||||
let has_scope = ${convertExpressionToUsePointers(scopeArgs.scope_if)};
|
|
||||||
if has_scope { ${enterScopeCode} }
|
|
||||||
`;
|
|
||||||
exitScopeCode = `if has_scope { ${exitScopeCode} }`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldsCodes = visitedFields.map((field, index) => {
|
const fieldsCodes = visitedFields.map((field, index) => {
|
||||||
|
|
@ -99,7 +98,11 @@ function generateWalkForStruct(type, types) {
|
||||||
? ''
|
? ''
|
||||||
: `ctx.retag_stack(AncestorType::${type.name}${snakeToCamel(field.name)});`;
|
: `ctx.retag_stack(AncestorType::${type.name}${snakeToCamel(field.name)});`;
|
||||||
const fieldCode = makeFieldCode(field);
|
const fieldCode = makeFieldCode(field);
|
||||||
const scopeCode = field === scopeEnterField ? enterScopeCode : '';
|
let scopeCode = '';
|
||||||
|
if (field === scopeEnterField) {
|
||||||
|
scopeCode = enterScopeCode;
|
||||||
|
enterScopeCode = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (field.wrappers[0] === 'Option') {
|
if (field.wrappers[0] === 'Option') {
|
||||||
let walkCode;
|
let walkCode;
|
||||||
|
|
@ -187,7 +190,6 @@ function generateWalkForStruct(type, types) {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
if (exitScopeCode) fieldsCodes.push(exitScopeCode);
|
|
||||||
fieldsCodes.push('ctx.pop_stack();');
|
fieldsCodes.push('ctx.pop_stack();');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,9 +200,11 @@ function generateWalkForStruct(type, types) {
|
||||||
node: *mut ${type.rawName},
|
node: *mut ${type.rawName},
|
||||||
ctx: &mut TraverseCtx<'a>
|
ctx: &mut TraverseCtx<'a>
|
||||||
) {
|
) {
|
||||||
|
${enterScopeCode}
|
||||||
traverser.enter_${typeSnakeName}(&mut *node, ctx);
|
traverser.enter_${typeSnakeName}(&mut *node, ctx);
|
||||||
${fieldsCodes.join('\n')}
|
${fieldsCodes.join('\n')}
|
||||||
traverser.exit_${typeSnakeName}(&mut *node, ctx);
|
traverser.exit_${typeSnakeName}(&mut *node, ctx);
|
||||||
|
${exitScopeCode}
|
||||||
}
|
}
|
||||||
`.replace(/\n\s*\n+/g, '\n');
|
`.replace(/\n\s*\n+/g, '\n');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use oxc_allocator::{Allocator, Box};
|
use oxc_allocator::{Allocator, Box};
|
||||||
use oxc_ast::AstBuilder;
|
use oxc_ast::AstBuilder;
|
||||||
use oxc_syntax::scope::ScopeFlags;
|
use oxc_semantic::{ScopeTree, SymbolTable};
|
||||||
|
use oxc_syntax::scope::{ScopeFlags, ScopeId};
|
||||||
|
|
||||||
use crate::ancestor::{Ancestor, AncestorType};
|
use crate::ancestor::{Ancestor, AncestorType};
|
||||||
|
|
||||||
const INITIAL_STACK_CAPACITY: usize = 64; // 64 entries = 1 KiB
|
const INITIAL_STACK_CAPACITY: usize = 64; // 64 entries = 1 KiB
|
||||||
const INITIAL_SCOPE_STACK_CAPACITY: usize = 32; // 32 entries = 64 bytes
|
|
||||||
|
|
||||||
/// Traverse context.
|
/// Traverse context.
|
||||||
///
|
///
|
||||||
|
|
@ -13,24 +13,38 @@ const INITIAL_SCOPE_STACK_CAPACITY: usize = 32; // 32 entries = 64 bytes
|
||||||
///
|
///
|
||||||
/// Provides ability to:
|
/// Provides ability to:
|
||||||
/// * Query parent/ancestor of current node via [`parent`], [`ancestor`], [`find_ancestor`].
|
/// * Query parent/ancestor of current node via [`parent`], [`ancestor`], [`find_ancestor`].
|
||||||
/// * Get type of current scope via [`scope`], [`ancestor_scope`], [`find_scope`].
|
/// * Get scopes tree and symbols table via [`scopes`], [`symbols`], [`find_scope`],
|
||||||
|
/// [`find_scope_by_flags`].
|
||||||
/// * Create AST nodes via AST builder [`ast`].
|
/// * Create AST nodes via AST builder [`ast`].
|
||||||
/// * Allocate into arena via [`alloc`].
|
/// * Allocate into arena via [`alloc`].
|
||||||
///
|
///
|
||||||
/// [`parent`]: `TraverseCtx::parent`
|
/// [`parent`]: `TraverseCtx::parent`
|
||||||
/// [`ancestor`]: `TraverseCtx::ancestor`
|
/// [`ancestor`]: `TraverseCtx::ancestor`
|
||||||
/// [`find_ancestor`]: `TraverseCtx::find_ancestor`
|
/// [`find_ancestor`]: `TraverseCtx::find_ancestor`
|
||||||
/// [`scope`]: `TraverseCtx::scope`
|
/// [`scopes`]: `TraverseCtx::scopes`
|
||||||
/// [`ancestor_scope`]: `TraverseCtx::ancestor_scope`
|
/// [`symbols`]: `TraverseCtx::symbols`
|
||||||
/// [`find_scope`]: `TraverseCtx::find_scope`
|
/// [`find_scope`]: `TraverseCtx::find_scope`
|
||||||
|
/// [`find_scope_by_flags`]: `TraverseCtx::find_scope_by_flags`
|
||||||
/// [`ast`]: `TraverseCtx::ast`
|
/// [`ast`]: `TraverseCtx::ast`
|
||||||
/// [`alloc`]: `TraverseCtx::alloc`
|
/// [`alloc`]: `TraverseCtx::alloc`
|
||||||
pub struct TraverseCtx<'a> {
|
pub struct TraverseCtx<'a> {
|
||||||
stack: Vec<Ancestor<'a>>,
|
stack: Vec<Ancestor<'a>>,
|
||||||
scope_stack: Vec<ScopeFlags>,
|
pub scoping: TraverseScoping,
|
||||||
pub ast: AstBuilder<'a>,
|
pub ast: AstBuilder<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Traverse scope context.
|
||||||
|
///
|
||||||
|
/// Contains the scope tree and symbols table, and provides methods to access them.
|
||||||
|
///
|
||||||
|
/// `current_scope_id` is the ID of current scope during traversal.
|
||||||
|
/// `walk_*` functions update this field when entering/exiting a scope.
|
||||||
|
pub struct TraverseScoping {
|
||||||
|
scopes: ScopeTree,
|
||||||
|
symbols: SymbolTable,
|
||||||
|
current_scope_id: ScopeId,
|
||||||
|
}
|
||||||
|
|
||||||
/// Return value when using [`TraverseCtx::find_ancestor`].
|
/// Return value when using [`TraverseCtx::find_ancestor`].
|
||||||
pub enum FinderRet<T> {
|
pub enum FinderRet<T> {
|
||||||
Found(T),
|
Found(T),
|
||||||
|
|
@ -41,14 +55,14 @@ pub enum FinderRet<T> {
|
||||||
// Public methods
|
// Public methods
|
||||||
impl<'a> TraverseCtx<'a> {
|
impl<'a> TraverseCtx<'a> {
|
||||||
/// Create new traversal context.
|
/// Create new traversal context.
|
||||||
pub(crate) fn new(allocator: &'a Allocator) -> Self {
|
pub(crate) fn new(scopes: ScopeTree, symbols: SymbolTable, allocator: &'a Allocator) -> Self {
|
||||||
let mut stack = Vec::with_capacity(INITIAL_STACK_CAPACITY);
|
let mut stack = Vec::with_capacity(INITIAL_STACK_CAPACITY);
|
||||||
stack.push(Ancestor::None);
|
stack.push(Ancestor::None);
|
||||||
|
|
||||||
let mut scope_stack = Vec::with_capacity(INITIAL_SCOPE_STACK_CAPACITY);
|
let scoping = TraverseScoping::new(scopes, symbols);
|
||||||
scope_stack.push(ScopeFlags::empty());
|
let ast = AstBuilder::new(allocator);
|
||||||
|
|
||||||
Self { stack, scope_stack, ast: AstBuilder::new(allocator) }
|
Self { stack, scoping, ast }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a node in the arena.
|
/// Allocate a node in the arena.
|
||||||
|
|
@ -127,50 +141,62 @@ impl<'a> TraverseCtx<'a> {
|
||||||
self.stack.len()
|
self.stack.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current scope info.
|
/// Get current scope ID.
|
||||||
|
///
|
||||||
|
/// Shortcut for `ctx.scoping.current_scope_id`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
pub fn current_scope_id(&self) -> ScopeId {
|
||||||
pub fn scope(&self) -> ScopeFlags {
|
self.scoping.current_scope_id()
|
||||||
// SAFETY: Scope stack contains 1 entry initially. Entries are pushed as traverse down the AST,
|
|
||||||
// and popped as go back up. So even when visiting `Program`, the initial entry is in the stack.
|
|
||||||
unsafe { *self.scope_stack.last().unwrap_unchecked() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get scope ancestor.
|
/// Get scopes tree.
|
||||||
/// `level` is number of scopes above.
|
///
|
||||||
/// `ancestor_scope(1).unwrap()` is equivalent to `scope()`.
|
/// Shortcut for `ctx.scoping.scopes`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestor_scope(&self, level: usize) -> Option<ScopeFlags> {
|
pub fn scopes(&self) -> &ScopeTree {
|
||||||
self.scope_stack.get(self.stack.len() - level).copied()
|
self.scoping.scopes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get symbols table.
|
||||||
|
///
|
||||||
|
/// Shortcut for `ctx.scoping.symbols`.
|
||||||
|
#[inline]
|
||||||
|
pub fn symbols(&self) -> &SymbolTable {
|
||||||
|
self.scoping.symbols()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walk up trail of scopes to find a scope.
|
/// Walk up trail of scopes to find a scope.
|
||||||
///
|
///
|
||||||
|
/// `finder` is called with `ScopeId`.
|
||||||
|
///
|
||||||
/// `finder` should return:
|
/// `finder` should return:
|
||||||
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
|
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
|
||||||
/// * `FinderRet::Stop` to stop walking and return `None`.
|
/// * `FinderRet::Stop` to stop walking and return `None`.
|
||||||
/// * `FinderRet::Continue` to continue walking up.
|
/// * `FinderRet::Continue` to continue walking up.
|
||||||
|
///
|
||||||
|
/// This is a shortcut for `ctx.scoping.find_scope`.
|
||||||
pub fn find_scope<F, O>(&self, finder: F) -> Option<O>
|
pub fn find_scope<F, O>(&self, finder: F) -> Option<O>
|
||||||
|
where
|
||||||
|
F: Fn(ScopeId) -> FinderRet<O>,
|
||||||
|
{
|
||||||
|
self.scoping.find_scope(finder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk up trail of scopes to find a scope by checking `ScopeFlags`.
|
||||||
|
///
|
||||||
|
/// `finder` is called with `ScopeFlags`.
|
||||||
|
///
|
||||||
|
/// `finder` should return:
|
||||||
|
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
|
||||||
|
/// * `FinderRet::Stop` to stop walking and return `None`.
|
||||||
|
/// * `FinderRet::Continue` to continue walking up.
|
||||||
|
///
|
||||||
|
/// This is a shortcut for `ctx.scoping.find_scope_by_flags`.
|
||||||
|
pub fn find_scope_by_flags<F, O>(&self, finder: F) -> Option<O>
|
||||||
where
|
where
|
||||||
F: Fn(ScopeFlags) -> FinderRet<O>,
|
F: Fn(ScopeFlags) -> FinderRet<O>,
|
||||||
{
|
{
|
||||||
for flags in self.scope_stack.iter().rev().copied() {
|
self.scoping.find_scope_by_flags(finder)
|
||||||
match finder(flags) {
|
|
||||||
FinderRet::Found(res) => return Some(res),
|
|
||||||
FinderRet::Stop => return None,
|
|
||||||
FinderRet::Continue => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get depth of scopes.
|
|
||||||
///
|
|
||||||
/// Count includes global scope.
|
|
||||||
/// i.e. in `Program`, depth is 2 (global scope + program top level scope).
|
|
||||||
#[inline]
|
|
||||||
pub fn scopes_depth(&self) -> usize {
|
|
||||||
self.scope_stack.len()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,21 +242,95 @@ impl<'a> TraverseCtx<'a> {
|
||||||
*(self.stack.last_mut().unwrap_unchecked() as *mut _ as *mut AncestorType) = ty;
|
*(self.stack.last_mut().unwrap_unchecked() as *mut _ as *mut AncestorType) = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push scope flags onto scope stack.
|
/// Shortcut for `ctx.scoping.set_current_scope_id`, to make `walk_*` methods less verbose.
|
||||||
///
|
|
||||||
/// `StrictMode` flag is inherited from parent.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_scope_stack(&mut self, flags: ScopeFlags) {
|
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
|
||||||
self.scope_stack.push(flags | (self.scope() & ScopeFlags::StrictMode));
|
self.scoping.set_current_scope_id(scope_id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// Pop last item off scope stack.
|
|
||||||
/// # SAFETY
|
// Public methods
|
||||||
/// * Stack must not be empty.
|
impl TraverseScoping {
|
||||||
/// * Each `pop_scope_stack` call must correspond to an earlier `push_scope_stack` call.
|
/// Get current scope ID
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
pub fn current_scope_id(&self) -> ScopeId {
|
||||||
pub(crate) unsafe fn pop_scope_stack(&mut self) {
|
self.current_scope_id
|
||||||
self.scope_stack.pop().unwrap_unchecked();
|
}
|
||||||
|
|
||||||
|
/// Get scopes tree
|
||||||
|
#[inline]
|
||||||
|
pub fn scopes(&self) -> &ScopeTree {
|
||||||
|
&self.scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get symbols table
|
||||||
|
#[inline]
|
||||||
|
pub fn symbols(&self) -> &SymbolTable {
|
||||||
|
&self.symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk up trail of scopes to find a scope.
|
||||||
|
///
|
||||||
|
/// `finder` is called with `ScopeId`.
|
||||||
|
///
|
||||||
|
/// `finder` should return:
|
||||||
|
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
|
||||||
|
/// * `FinderRet::Stop` to stop walking and return `None`.
|
||||||
|
/// * `FinderRet::Continue` to continue walking up.
|
||||||
|
pub fn find_scope<F, O>(&self, finder: F) -> Option<O>
|
||||||
|
where
|
||||||
|
F: Fn(ScopeId) -> FinderRet<O>,
|
||||||
|
{
|
||||||
|
let mut scope_id = self.current_scope_id;
|
||||||
|
loop {
|
||||||
|
match finder(scope_id) {
|
||||||
|
FinderRet::Found(res) => return Some(res),
|
||||||
|
FinderRet::Stop => return None,
|
||||||
|
FinderRet::Continue => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent_scope_id) = self.scopes.get_parent_id(scope_id) {
|
||||||
|
scope_id = parent_scope_id;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk up trail of scopes to find a scope by checking `ScopeFlags`.
|
||||||
|
///
|
||||||
|
/// `finder` is called with `ScopeFlags`.
|
||||||
|
///
|
||||||
|
/// `finder` should return:
|
||||||
|
/// * `FinderRet::Found(value)` to stop walking and return `Some(value)`.
|
||||||
|
/// * `FinderRet::Stop` to stop walking and return `None`.
|
||||||
|
/// * `FinderRet::Continue` to continue walking up.
|
||||||
|
pub fn find_scope_by_flags<F, O>(&self, finder: F) -> Option<O>
|
||||||
|
where
|
||||||
|
F: Fn(ScopeFlags) -> FinderRet<O>,
|
||||||
|
{
|
||||||
|
self.find_scope(|scope_id| {
|
||||||
|
let flags = self.scopes.get_flags(scope_id);
|
||||||
|
finder(flags)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods used internally within crate
|
||||||
|
impl TraverseScoping {
|
||||||
|
/// Create new `TraverseScoping`
|
||||||
|
fn new(scopes: ScopeTree, symbols: SymbolTable) -> Self {
|
||||||
|
Self {
|
||||||
|
scopes,
|
||||||
|
symbols,
|
||||||
|
// Dummy value. Immediately overwritten in `walk_program`.
|
||||||
|
current_scope_id: ScopeId::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set current scope ID
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
|
||||||
|
self.current_scope_id = scope_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,14 @@
|
||||||
//! to edit by hand.
|
//! to edit by hand.
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
|
|
||||||
use oxc_ast::ast::Program;
|
use oxc_ast::ast::Program;
|
||||||
|
use oxc_semantic::SemanticBuilder;
|
||||||
|
use oxc_span::SourceType;
|
||||||
|
|
||||||
pub mod ancestor;
|
pub mod ancestor;
|
||||||
pub use ancestor::Ancestor;
|
pub use ancestor::Ancestor;
|
||||||
mod context;
|
mod context;
|
||||||
pub use context::{FinderRet, TraverseCtx};
|
pub use context::{FinderRet, TraverseCtx, TraverseScoping};
|
||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
mod traverse;
|
mod traverse;
|
||||||
pub use traverse::Traverse;
|
pub use traverse::Traverse;
|
||||||
|
|
@ -140,11 +141,18 @@ mod walk;
|
||||||
pub fn traverse_mut<'a, Tr: Traverse<'a>>(
|
pub fn traverse_mut<'a, Tr: Traverse<'a>>(
|
||||||
traverser: &mut Tr,
|
traverser: &mut Tr,
|
||||||
program: &mut Program<'a>,
|
program: &mut Program<'a>,
|
||||||
|
source_text: &'a str,
|
||||||
|
source_type: SourceType,
|
||||||
allocator: &'a Allocator,
|
allocator: &'a Allocator,
|
||||||
) {
|
) {
|
||||||
let mut ctx = TraverseCtx::new(allocator);
|
let semantic = SemanticBuilder::new(source_text, source_type)
|
||||||
|
.with_check_syntax_error(true)
|
||||||
|
.build(program)
|
||||||
|
.semantic;
|
||||||
|
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||||
|
|
||||||
|
let mut ctx = TraverseCtx::new(scopes, symbols, allocator);
|
||||||
// SAFETY: Walk functions are constructed to avoid unsoundness
|
// SAFETY: Walk functions are constructed to avoid unsoundness
|
||||||
unsafe { walk::walk_program(traverser, program as *mut Program, &mut ctx) };
|
unsafe { walk::walk_program(traverser, program as *mut Program, &mut ctx) };
|
||||||
debug_assert!(ctx.ancestors_depth() == 1);
|
debug_assert!(ctx.ancestors_depth() == 1);
|
||||||
debug_assert!(ctx.scopes_depth() == 1);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@
|
||||||
clippy::semicolon_if_nothing_returned,
|
clippy::semicolon_if_nothing_returned,
|
||||||
clippy::ptr_as_ptr,
|
clippy::ptr_as_ptr,
|
||||||
clippy::borrow_as_ptr,
|
clippy::borrow_as_ptr,
|
||||||
clippy::cast_ptr_alignment,
|
clippy::cast_ptr_alignment
|
||||||
clippy::needless_borrow
|
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
use oxc_allocator::Vec;
|
use oxc_allocator::Vec;
|
||||||
#[allow(clippy::wildcard_imports)]
|
#[allow(clippy::wildcard_imports)]
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
use oxc_span::SourceType;
|
use oxc_syntax::scope::ScopeId;
|
||||||
use oxc_syntax::scope::ScopeFlags;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ancestor::{self, AncestorType},
|
ancestor::{self, AncestorType},
|
||||||
|
|
@ -28,18 +28,16 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut Program<'a>,
|
node: *mut Program<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_PROGRAM_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_program(&mut *node, ctx);
|
traverser.enter_program(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::ProgramDirectives(ancestor::ProgramWithoutDirectives(node)));
|
ctx.push_stack(Ancestor::ProgramDirectives(ancestor::ProgramWithoutDirectives(node)));
|
||||||
ctx.push_scope_stack(
|
|
||||||
ScopeFlags::Top.with_strict_mode(
|
|
||||||
(&*((node as *mut u8).add(ancestor::OFFSET_PROGRAM_SOURCE_TYPE) as *mut SourceType))
|
|
||||||
.is_strict()
|
|
||||||
|| (&*((node as *mut u8).add(ancestor::OFFSET_PROGRAM_DIRECTIVES)
|
|
||||||
as *mut Vec<Directive>))
|
|
||||||
.iter()
|
|
||||||
.any(Directive::is_use_strict),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for item in (*((node as *mut u8).add(ancestor::OFFSET_PROGRAM_DIRECTIVES)
|
for item in (*((node as *mut u8).add(ancestor::OFFSET_PROGRAM_DIRECTIVES)
|
||||||
as *mut Vec<Directive>))
|
as *mut Vec<Directive>))
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|
@ -58,9 +56,11 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_PROGRAM_BODY) as *mut Vec<Statement>,
|
(node as *mut u8).add(ancestor::OFFSET_PROGRAM_BODY) as *mut Vec<Statement>,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_program(&mut *node, ctx);
|
traverser.exit_program(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_expression<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_expression<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -1401,17 +1401,26 @@ pub(crate) unsafe fn walk_block_statement<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut BlockStatement<'a>,
|
node: *mut BlockStatement<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_BLOCK_STATEMENT_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_block_statement(&mut *node, ctx);
|
traverser.enter_block_statement(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::BlockStatementBody(ancestor::BlockStatementWithoutBody(node)));
|
ctx.push_stack(Ancestor::BlockStatementBody(ancestor::BlockStatementWithoutBody(node)));
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
walk_statements(
|
walk_statements(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_BLOCK_STATEMENT_BODY) as *mut Vec<Statement>,
|
(node as *mut u8).add(ancestor::OFFSET_BLOCK_STATEMENT_BODY) as *mut Vec<Statement>,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_block_statement(&mut *node, ctx);
|
traverser.exit_block_statement(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_declaration<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_declaration<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -1614,15 +1623,16 @@ pub(crate) unsafe fn walk_for_statement<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut ForStatement<'a>,
|
node: *mut ForStatement<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_for_statement(&mut *node, ctx);
|
traverser.enter_for_statement(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::ForStatementInit(ancestor::ForStatementWithoutInit(node)));
|
ctx.push_stack(Ancestor::ForStatementInit(ancestor::ForStatementWithoutInit(node)));
|
||||||
let has_scope = (&*((node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_INIT)
|
|
||||||
as *mut Option<ForStatementInit>))
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(ForStatementInit::is_lexical_declaration);
|
|
||||||
if has_scope {
|
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
}
|
|
||||||
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_INIT)
|
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_INIT)
|
||||||
as *mut Option<ForStatementInit>)
|
as *mut Option<ForStatementInit>)
|
||||||
{
|
{
|
||||||
|
|
@ -1646,11 +1656,11 @@ pub(crate) unsafe fn walk_for_statement<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_BODY) as *mut Statement,
|
(node as *mut u8).add(ancestor::OFFSET_FOR_STATEMENT_BODY) as *mut Statement,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
if has_scope {
|
|
||||||
ctx.pop_scope_stack();
|
|
||||||
}
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_for_statement(&mut *node, ctx);
|
traverser.exit_for_statement(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_for_statement_init<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_for_statement_init<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -1719,14 +1729,16 @@ pub(crate) unsafe fn walk_for_in_statement<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut ForInStatement<'a>,
|
node: *mut ForInStatement<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_for_in_statement(&mut *node, ctx);
|
traverser.enter_for_in_statement(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::ForInStatementLeft(ancestor::ForInStatementWithoutLeft(node)));
|
ctx.push_stack(Ancestor::ForInStatementLeft(ancestor::ForInStatementWithoutLeft(node)));
|
||||||
let has_scope = (&*((node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_LEFT)
|
|
||||||
as *mut ForStatementLeft))
|
|
||||||
.is_lexical_declaration();
|
|
||||||
if has_scope {
|
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
}
|
|
||||||
walk_for_statement_left(
|
walk_for_statement_left(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_LEFT) as *mut ForStatementLeft,
|
(node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_LEFT) as *mut ForStatementLeft,
|
||||||
|
|
@ -1744,11 +1756,11 @@ pub(crate) unsafe fn walk_for_in_statement<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_BODY) as *mut Statement,
|
(node as *mut u8).add(ancestor::OFFSET_FOR_IN_STATEMENT_BODY) as *mut Statement,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
if has_scope {
|
|
||||||
ctx.pop_scope_stack();
|
|
||||||
}
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_for_in_statement(&mut *node, ctx);
|
traverser.exit_for_in_statement(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_for_of_statement<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_for_of_statement<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -1756,14 +1768,16 @@ pub(crate) unsafe fn walk_for_of_statement<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut ForOfStatement<'a>,
|
node: *mut ForOfStatement<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_for_of_statement(&mut *node, ctx);
|
traverser.enter_for_of_statement(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::ForOfStatementLeft(ancestor::ForOfStatementWithoutLeft(node)));
|
ctx.push_stack(Ancestor::ForOfStatementLeft(ancestor::ForOfStatementWithoutLeft(node)));
|
||||||
let has_scope = (&*((node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_LEFT)
|
|
||||||
as *mut ForStatementLeft))
|
|
||||||
.is_lexical_declaration();
|
|
||||||
if has_scope {
|
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
}
|
|
||||||
walk_for_statement_left(
|
walk_for_statement_left(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_LEFT) as *mut ForStatementLeft,
|
(node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_LEFT) as *mut ForStatementLeft,
|
||||||
|
|
@ -1781,11 +1795,11 @@ pub(crate) unsafe fn walk_for_of_statement<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_BODY) as *mut Statement,
|
(node as *mut u8).add(ancestor::OFFSET_FOR_OF_STATEMENT_BODY) as *mut Statement,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
if has_scope {
|
|
||||||
ctx.pop_scope_stack();
|
|
||||||
}
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_for_of_statement(&mut *node, ctx);
|
traverser.exit_for_of_statement(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_for_statement_left<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_for_statement_left<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -1904,7 +1918,14 @@ pub(crate) unsafe fn walk_switch_statement<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_SWITCH_STATEMENT_DISCRIMINANT) as *mut Expression,
|
(node as *mut u8).add(ancestor::OFFSET_SWITCH_STATEMENT_DISCRIMINANT) as *mut Expression,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_SWITCH_STATEMENT_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
ctx.retag_stack(AncestorType::SwitchStatementCases);
|
ctx.retag_stack(AncestorType::SwitchStatementCases);
|
||||||
for item in (*((node as *mut u8).add(ancestor::OFFSET_SWITCH_STATEMENT_CASES)
|
for item in (*((node as *mut u8).add(ancestor::OFFSET_SWITCH_STATEMENT_CASES)
|
||||||
as *mut Vec<SwitchCase>))
|
as *mut Vec<SwitchCase>))
|
||||||
|
|
@ -1912,9 +1933,11 @@ pub(crate) unsafe fn walk_switch_statement<'a, Tr: Traverse<'a>>(
|
||||||
{
|
{
|
||||||
walk_switch_case(traverser, item as *mut _, ctx);
|
walk_switch_case(traverser, item as *mut _, ctx);
|
||||||
}
|
}
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_switch_statement(&mut *node, ctx);
|
traverser.exit_switch_statement(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_switch_case<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_switch_case<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -2011,14 +2034,16 @@ pub(crate) unsafe fn walk_catch_clause<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut CatchClause<'a>,
|
node: *mut CatchClause<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_CATCH_CLAUSE_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_catch_clause(&mut *node, ctx);
|
traverser.enter_catch_clause(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::CatchClauseParam(ancestor::CatchClauseWithoutParam(node)));
|
ctx.push_stack(Ancestor::CatchClauseParam(ancestor::CatchClauseWithoutParam(node)));
|
||||||
let has_scope = (&*((node as *mut u8).add(ancestor::OFFSET_CATCH_CLAUSE_PARAM)
|
|
||||||
as *mut Option<CatchParameter>))
|
|
||||||
.is_some();
|
|
||||||
if has_scope {
|
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
}
|
|
||||||
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CATCH_CLAUSE_PARAM)
|
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_CATCH_CLAUSE_PARAM)
|
||||||
as *mut Option<CatchParameter>)
|
as *mut Option<CatchParameter>)
|
||||||
{
|
{
|
||||||
|
|
@ -2031,11 +2056,11 @@ pub(crate) unsafe fn walk_catch_clause<'a, Tr: Traverse<'a>>(
|
||||||
as *mut Box<BlockStatement>)) as *mut _,
|
as *mut Box<BlockStatement>)) as *mut _,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
if has_scope {
|
|
||||||
ctx.pop_scope_stack();
|
|
||||||
}
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_catch_clause(&mut *node, ctx);
|
traverser.exit_catch_clause(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_catch_parameter<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_catch_parameter<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -2226,19 +2251,16 @@ pub(crate) unsafe fn walk_function<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut Function<'a>,
|
node: *mut Function<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_FUNCTION_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_function(&mut *node, ctx);
|
traverser.enter_function(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::FunctionId(ancestor::FunctionWithoutId(node)));
|
ctx.push_stack(Ancestor::FunctionId(ancestor::FunctionWithoutId(node)));
|
||||||
let has_scope = !matches!(ctx.ancestor(2).unwrap(), Ancestor::MethodDefinitionValue(_));
|
|
||||||
if has_scope {
|
|
||||||
ctx.push_scope_stack(
|
|
||||||
ScopeFlags::Function.with_strict_mode(
|
|
||||||
(&*((node as *mut u8).add(ancestor::OFFSET_FUNCTION_BODY)
|
|
||||||
as *mut Option<Box<FunctionBody>>))
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|body| body.has_use_strict_directive()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_FUNCTION_ID)
|
if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_FUNCTION_ID)
|
||||||
as *mut Option<BindingIdentifier>)
|
as *mut Option<BindingIdentifier>)
|
||||||
{
|
{
|
||||||
|
|
@ -2275,11 +2297,11 @@ pub(crate) unsafe fn walk_function<'a, Tr: Traverse<'a>>(
|
||||||
ctx.retag_stack(AncestorType::FunctionReturnType);
|
ctx.retag_stack(AncestorType::FunctionReturnType);
|
||||||
walk_ts_type_annotation(traverser, (&mut **field) as *mut _, ctx);
|
walk_ts_type_annotation(traverser, (&mut **field) as *mut _, ctx);
|
||||||
}
|
}
|
||||||
if has_scope {
|
|
||||||
ctx.pop_scope_stack();
|
|
||||||
}
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_function(&mut *node, ctx);
|
traverser.exit_function(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_formal_parameters<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_formal_parameters<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -2356,11 +2378,19 @@ pub(crate) unsafe fn walk_arrow_function_expression<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut ArrowFunctionExpression<'a>,
|
node: *mut ArrowFunctionExpression<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8)
|
||||||
|
.add(ancestor::OFFSET_ARROW_FUNCTION_EXPRESSION_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_arrow_function_expression(&mut *node, ctx);
|
traverser.enter_arrow_function_expression(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::ArrowFunctionExpressionParams(
|
ctx.push_stack(Ancestor::ArrowFunctionExpressionParams(
|
||||||
ancestor::ArrowFunctionExpressionWithoutParams(node),
|
ancestor::ArrowFunctionExpressionWithoutParams(node),
|
||||||
));
|
));
|
||||||
ctx.push_scope_stack(ScopeFlags::Function | ScopeFlags::Arrow);
|
|
||||||
walk_formal_parameters(
|
walk_formal_parameters(
|
||||||
traverser,
|
traverser,
|
||||||
(&mut **((node as *mut u8).add(ancestor::OFFSET_ARROW_FUNCTION_EXPRESSION_PARAMS)
|
(&mut **((node as *mut u8).add(ancestor::OFFSET_ARROW_FUNCTION_EXPRESSION_PARAMS)
|
||||||
|
|
@ -2388,9 +2418,11 @@ pub(crate) unsafe fn walk_arrow_function_expression<'a, Tr: Traverse<'a>>(
|
||||||
ctx.retag_stack(AncestorType::ArrowFunctionExpressionReturnType);
|
ctx.retag_stack(AncestorType::ArrowFunctionExpressionReturnType);
|
||||||
walk_ts_type_annotation(traverser, (&mut **field) as *mut _, ctx);
|
walk_ts_type_annotation(traverser, (&mut **field) as *mut _, ctx);
|
||||||
}
|
}
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_arrow_function_expression(&mut *node, ctx);
|
traverser.exit_arrow_function_expression(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_yield_expression<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_yield_expression<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -2423,7 +2455,14 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>(
|
||||||
{
|
{
|
||||||
walk_decorator(traverser, item as *mut _, ctx);
|
walk_decorator(traverser, item as *mut _, ctx);
|
||||||
}
|
}
|
||||||
ctx.push_scope_stack(ScopeFlags::StrictMode);
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_CLASS_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
if let Some(field) =
|
if let Some(field) =
|
||||||
&mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_ID) as *mut Option<BindingIdentifier>)
|
&mut *((node as *mut u8).add(ancestor::OFFSET_CLASS_ID) as *mut Option<BindingIdentifier>)
|
||||||
{
|
{
|
||||||
|
|
@ -2463,9 +2502,11 @@ pub(crate) unsafe fn walk_class<'a, Tr: Traverse<'a>>(
|
||||||
walk_ts_class_implements(traverser, item as *mut _, ctx);
|
walk_ts_class_implements(traverser, item as *mut _, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_class(&mut *node, ctx);
|
traverser.exit_class(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_class_body<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_class_body<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -2532,16 +2573,6 @@ pub(crate) unsafe fn walk_method_definition<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_METHOD_DEFINITION_KEY) as *mut PropertyKey,
|
(node as *mut u8).add(ancestor::OFFSET_METHOD_DEFINITION_KEY) as *mut PropertyKey,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.push_scope_stack(
|
|
||||||
(&*((node as *mut u8).add(ancestor::OFFSET_METHOD_DEFINITION_KIND)
|
|
||||||
as *mut MethodDefinitionKind))
|
|
||||||
.scope_flags()
|
|
||||||
.with_strict_mode(
|
|
||||||
(&*((node as *mut u8).add(ancestor::OFFSET_METHOD_DEFINITION_VALUE)
|
|
||||||
as *mut Box<Function>))
|
|
||||||
.is_strict(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.retag_stack(AncestorType::MethodDefinitionValue);
|
ctx.retag_stack(AncestorType::MethodDefinitionValue);
|
||||||
walk_function(
|
walk_function(
|
||||||
traverser,
|
traverser,
|
||||||
|
|
@ -2549,7 +2580,6 @@ pub(crate) unsafe fn walk_method_definition<'a, Tr: Traverse<'a>>(
|
||||||
as *mut Box<Function>)) as *mut _,
|
as *mut Box<Function>)) as *mut _,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_method_definition(&mut *node, ctx);
|
traverser.exit_method_definition(&mut *node, ctx);
|
||||||
}
|
}
|
||||||
|
|
@ -2604,17 +2634,26 @@ pub(crate) unsafe fn walk_static_block<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut StaticBlock<'a>,
|
node: *mut StaticBlock<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_STATIC_BLOCK_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_static_block(&mut *node, ctx);
|
traverser.enter_static_block(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::StaticBlockBody(ancestor::StaticBlockWithoutBody(node)));
|
ctx.push_stack(Ancestor::StaticBlockBody(ancestor::StaticBlockWithoutBody(node)));
|
||||||
ctx.push_scope_stack(ScopeFlags::ClassStaticBlock);
|
|
||||||
walk_statements(
|
walk_statements(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_STATIC_BLOCK_BODY) as *mut Vec<Statement>,
|
(node as *mut u8).add(ancestor::OFFSET_STATIC_BLOCK_BODY) as *mut Vec<Statement>,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_static_block(&mut *node, ctx);
|
traverser.exit_static_block(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_module_declaration<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_module_declaration<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -3589,7 +3628,14 @@ pub(crate) unsafe fn walk_ts_enum_declaration<'a, Tr: Traverse<'a>>(
|
||||||
(node as *mut u8).add(ancestor::OFFSET_TS_ENUM_DECLARATION_ID) as *mut BindingIdentifier,
|
(node as *mut u8).add(ancestor::OFFSET_TS_ENUM_DECLARATION_ID) as *mut BindingIdentifier,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_TS_ENUM_DECLARATION_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
ctx.retag_stack(AncestorType::TSEnumDeclarationMembers);
|
ctx.retag_stack(AncestorType::TSEnumDeclarationMembers);
|
||||||
for item in (*((node as *mut u8).add(ancestor::OFFSET_TS_ENUM_DECLARATION_MEMBERS)
|
for item in (*((node as *mut u8).add(ancestor::OFFSET_TS_ENUM_DECLARATION_MEMBERS)
|
||||||
as *mut Vec<TSEnumMember>))
|
as *mut Vec<TSEnumMember>))
|
||||||
|
|
@ -3597,9 +3643,11 @@ pub(crate) unsafe fn walk_ts_enum_declaration<'a, Tr: Traverse<'a>>(
|
||||||
{
|
{
|
||||||
walk_ts_enum_member(traverser, item as *mut _, ctx);
|
walk_ts_enum_member(traverser, item as *mut _, ctx);
|
||||||
}
|
}
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_ts_enum_declaration(&mut *node, ctx);
|
traverser.exit_ts_enum_declaration(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_ts_enum_member<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_ts_enum_member<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -4318,9 +4366,16 @@ pub(crate) unsafe fn walk_ts_type_parameter<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut TSTypeParameter<'a>,
|
node: *mut TSTypeParameter<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_TS_TYPE_PARAMETER_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_ts_type_parameter(&mut *node, ctx);
|
traverser.enter_ts_type_parameter(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::TSTypeParameterName(ancestor::TSTypeParameterWithoutName(node)));
|
ctx.push_stack(Ancestor::TSTypeParameterName(ancestor::TSTypeParameterWithoutName(node)));
|
||||||
ctx.push_scope_stack(ScopeFlags::empty());
|
|
||||||
walk_binding_identifier(
|
walk_binding_identifier(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_TS_TYPE_PARAMETER_NAME) as *mut BindingIdentifier,
|
(node as *mut u8).add(ancestor::OFFSET_TS_TYPE_PARAMETER_NAME) as *mut BindingIdentifier,
|
||||||
|
|
@ -4338,9 +4393,11 @@ pub(crate) unsafe fn walk_ts_type_parameter<'a, Tr: Traverse<'a>>(
|
||||||
ctx.retag_stack(AncestorType::TSTypeParameterDefault);
|
ctx.retag_stack(AncestorType::TSTypeParameterDefault);
|
||||||
walk_ts_type(traverser, field as *mut _, ctx);
|
walk_ts_type(traverser, field as *mut _, ctx);
|
||||||
}
|
}
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_ts_type_parameter(&mut *node, ctx);
|
traverser.exit_ts_type_parameter(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_ts_type_parameter_declaration<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_ts_type_parameter_declaration<'a, Tr: Traverse<'a>>(
|
||||||
|
|
@ -4818,17 +4875,26 @@ pub(crate) unsafe fn walk_ts_module_block<'a, Tr: Traverse<'a>>(
|
||||||
node: *mut TSModuleBlock<'a>,
|
node: *mut TSModuleBlock<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
|
let mut previous_scope_id = None;
|
||||||
|
if let Some(scope_id) = (*((node as *mut u8).add(ancestor::OFFSET_TS_MODULE_BLOCK_SCOPE_ID)
|
||||||
|
as *mut Cell<Option<ScopeId>>))
|
||||||
|
.get()
|
||||||
|
{
|
||||||
|
previous_scope_id = Some(ctx.current_scope_id());
|
||||||
|
ctx.set_current_scope_id(scope_id);
|
||||||
|
}
|
||||||
traverser.enter_ts_module_block(&mut *node, ctx);
|
traverser.enter_ts_module_block(&mut *node, ctx);
|
||||||
ctx.push_stack(Ancestor::TSModuleBlockBody(ancestor::TSModuleBlockWithoutBody(node)));
|
ctx.push_stack(Ancestor::TSModuleBlockBody(ancestor::TSModuleBlockWithoutBody(node)));
|
||||||
ctx.push_scope_stack(ScopeFlags::TsModuleBlock);
|
|
||||||
walk_statements(
|
walk_statements(
|
||||||
traverser,
|
traverser,
|
||||||
(node as *mut u8).add(ancestor::OFFSET_TS_MODULE_BLOCK_BODY) as *mut Vec<Statement>,
|
(node as *mut u8).add(ancestor::OFFSET_TS_MODULE_BLOCK_BODY) as *mut Vec<Statement>,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
ctx.pop_scope_stack();
|
|
||||||
ctx.pop_stack();
|
ctx.pop_stack();
|
||||||
traverser.exit_ts_module_block(&mut *node, ctx);
|
traverser.exit_ts_module_block(&mut *node, ctx);
|
||||||
|
if let Some(previous_scope_id) = previous_scope_id {
|
||||||
|
ctx.set_current_scope_id(previous_scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn walk_ts_type_literal<'a, Tr: Traverse<'a>>(
|
pub(crate) unsafe fn walk_ts_type_literal<'a, Tr: Traverse<'a>>(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue