Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
refactor(traverse): enter node before entering scope (#4684)
Closes #4200.

Align `Traverse`'s behavior with `Visit` and `VisitMut`. For types with scopes, call `enter_*` before entering scope, and call `exit_*` after exiting scope.
  • Loading branch information
overlookmotel committed Aug 6, 2024
commit 83546d32309c815aa1019b1b99a587aa53b90220
22 changes: 15 additions & 7 deletions crates/oxc_transformer/src/typescript/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use oxc_ast::ast::*;
use oxc_semantic::SymbolFlags;
use oxc_span::{Atom, GetSpan, Span, SPAN};
use oxc_syntax::{
operator::AssignmentOperator, reference::ReferenceFlag, scope::ScopeFlags, symbol::SymbolId,
operator::AssignmentOperator,
reference::ReferenceFlag,
scope::{ScopeFlags, ScopeId},
symbol::SymbolId,
};
use oxc_traverse::TraverseCtx;
use rustc_hash::FxHashSet;
Expand Down Expand Up @@ -419,7 +422,7 @@ impl<'a> TypeScriptAnnotations<'a> {
}
}

Self::replace_with_empty_block_if_ts(&mut stmt.consequent, ctx);
Self::replace_with_empty_block_if_ts(&mut stmt.consequent, ctx.current_scope_id(), ctx);

if stmt.alternate.as_ref().is_some_and(Statement::is_typescript_syntax) {
stmt.alternate = None;
Expand All @@ -442,28 +445,33 @@ impl<'a> TypeScriptAnnotations<'a> {
stmt: &mut ForStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
let scope_id = stmt.scope_id.get().unwrap_or(ctx.current_scope_id());
Self::replace_with_empty_block_if_ts(&mut stmt.body, scope_id, ctx);
}

pub fn transform_while_statement(
&mut self,
stmt: &mut WhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx.current_scope_id(), ctx);
}

pub fn transform_do_while_statement(
&mut self,
stmt: &mut DoWhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx.current_scope_id(), ctx);
}

fn replace_with_empty_block_if_ts(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
fn replace_with_empty_block_if_ts(
stmt: &mut Statement<'a>,
parent_scope_id: ScopeId,
ctx: &mut TraverseCtx<'a>,
) {
if stmt.is_typescript_syntax() {
let scope_id = ctx.create_scope_child_of_current(ScopeFlags::empty());
let scope_id = ctx.create_child_scope(parent_scope_id, ScopeFlags::empty());
let block = BlockStatement {
span: stmt.span(),
body: ctx.ast.vec(),
Expand Down
5 changes: 2 additions & 3 deletions crates/oxc_transformer/src/typescript/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,9 @@ impl<'a> TypeScript<'a> {
}

if new_stmts.is_empty() {
// Delete the scope binding that `ctx.generate_uid_in_current_scope` created above,
// Delete the scope binding that `ctx.generate_uid` created above,
// as no binding is actually being created
let current_scope_id = ctx.current_scope_id();
ctx.scopes_mut().remove_binding(current_scope_id, &CompactStr::from(name.as_str()));
ctx.scopes_mut().remove_binding(scope_id, &CompactStr::from(name.as_str()));

return None;
}
Expand Down
52 changes: 24 additions & 28 deletions crates/oxc_traverse/scripts/lib/walk.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ function generateWalkForStruct(type, types) {
`\`ast\` attr says to enter scope before field '${enterFieldName}' `
+ `in '${type.name}', but that field is not visited`
);
if (scopeEnterField === visitedFields[0]) scopeEnterField = undefined;
} else {
scopeEnterField = visitedFields[0];
}

// TODO: Maybe this isn't quite right. `scope_id` fields are `Cell<Option<ScopeId>>`,
Expand All @@ -93,18 +94,25 @@ function generateWalkForStruct(type, types) {
}

const fieldsCodes = visitedFields.map((field, index) => {
const fieldWalkName = `walk_${camelToSnake(field.innerTypeName)}`;
const fieldWalkName = `walk_${camelToSnake(field.innerTypeName)}`,
fieldCamelName = snakeToCamel(field.name);
const scopeCode = field === scopeEnterField ? enterScopeCode : '';

const retagCode = index === 0
? ''
: `ctx.retag_stack(AncestorType::${type.name}${snakeToCamel(field.name)});`;
const fieldCode = makeFieldCode(field);
let scopeCode = '';
if (field === scopeEnterField) {
scopeCode = enterScopeCode;
enterScopeCode = '';
let tagCode = '', retagCode = '';
if (index === 0) {
tagCode = `
ctx.push_stack(
Ancestor::${type.name}${fieldCamelName}(
ancestor::${type.name}Without${fieldCamelName}(node)
)
);
`;
} else {
retagCode = `ctx.retag_stack(AncestorType::${type.name}${fieldCamelName});`;
}

const fieldCode = makeFieldCode(field);

if (field.wrappers[0] === 'Option') {
let walkCode;
if (field.wrappers.length === 2 && field.wrappers[1] === 'Vec') {
Expand All @@ -127,6 +135,7 @@ function generateWalkForStruct(type, types) {

return `
${scopeCode}
${tagCode}
if let Some(field) = &mut *(${fieldCode}) {
${retagCode}
${walkCode}
Expand Down Expand Up @@ -159,15 +168,15 @@ function generateWalkForStruct(type, types) {

return `
${scopeCode}
${retagCode}
${tagCode || retagCode}
${walkVecCode}
`;
}

if (field.wrappers.length === 1 && field.wrappers[0] === 'Box') {
return `
${scopeCode}
${retagCode}
${tagCode || retagCode}
${fieldWalkName}(traverser, (&mut **(${fieldCode})) as *mut _, ctx);
`;
}
Expand All @@ -176,23 +185,12 @@ function generateWalkForStruct(type, types) {

return `
${scopeCode}
${retagCode}
${tagCode || retagCode}
${fieldWalkName}(traverser, ${fieldCode}, ctx);
`;
});

if (visitedFields.length > 0) {
const field = visitedFields[0],
fieldCamelName = snakeToCamel(field.name);
fieldsCodes.unshift(`
ctx.push_stack(
Ancestor::${type.name}${fieldCamelName}(
ancestor::${type.name}Without${fieldCamelName}(node)
)
);
`);
fieldsCodes.push('ctx.pop_stack();');
}
if (visitedFields.length > 0) fieldsCodes.push('ctx.pop_stack();');

const typeSnakeName = camelToSnake(type.name);
return `
Expand All @@ -201,12 +199,10 @@ function generateWalkForStruct(type, types) {
node: *mut ${type.rawName},
ctx: &mut TraverseCtx<'a>
) {
${enterScopeCode}
traverser.enter_${typeSnakeName}(&mut *node, ctx);
${fieldsCodes.join('\n')}
${enterScopeCode ? '' : exitScopeCode}
${exitScopeCode}
traverser.exit_${typeSnakeName}(&mut *node, ctx);
${enterScopeCode ? exitScopeCode : ''}
}
`.replace(/\n\s*\n+/g, '\n');
}
Expand Down
15 changes: 12 additions & 3 deletions crates/oxc_traverse/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,22 @@ impl<'a> TraverseCtx<'a> {
self.scoping.find_scope_by_flags(finder)
}

/// Create new scope as child of provided scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
///
/// This is a shortcut for `ctx.scoping.create_child_scope`.
pub fn create_child_scope(&mut self, parent_id: ScopeId, flags: ScopeFlags) -> ScopeId {
self.scoping.create_child_scope(parent_id, flags)
}

/// Create new scope as child of current scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
///
/// This is a shortcut for `ctx.scoping.create_scope_child_of_current`.
pub fn create_scope_child_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
self.scoping.create_scope_child_of_current(flags)
/// This is a shortcut for `ctx.scoping.create_child_scope_of_current`.
pub fn create_child_scope_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
self.scoping.create_child_scope_of_current(flags)
}

/// Insert a scope into scope tree below a statement.
Expand Down
15 changes: 11 additions & 4 deletions crates/oxc_traverse/src/context/scoping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,19 @@ impl TraverseScoping {
})
}

/// Create new scope as child of provided scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
pub fn create_child_scope(&mut self, parent_id: ScopeId, flags: ScopeFlags) -> ScopeId {
let flags = self.scopes.get_new_scope_flags(flags, parent_id);
self.scopes.add_scope(parent_id, AstNodeId::DUMMY, flags)
}

/// Create new scope as child of current scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
pub fn create_scope_child_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
let flags = self.scopes.get_new_scope_flags(flags, self.current_scope_id);
self.scopes.add_scope(self.current_scope_id, AstNodeId::DUMMY, flags)
pub fn create_child_scope_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
self.create_child_scope(self.current_scope_id, flags)
}

/// Insert a scope into scope tree below a statement.
Expand Down Expand Up @@ -162,7 +169,7 @@ impl TraverseScoping {
}

// Create new scope as child of parent
let new_scope_id = self.create_scope_child_of_current(flags);
let new_scope_id = self.create_child_scope_of_current(flags);

// Set scopes as children of new scope instead
for &child_id in child_scope_ids {
Expand Down
Loading