feat(ast, ast_codegen): pass the scope_id to the enter_scope event. (#4168)

This commit is contained in:
rzvxa 2024-07-10 15:19:23 +00:00
parent c8f5664e0a
commit 67fe75ec6c
7 changed files with 203 additions and 197 deletions

View file

@ -16,8 +16,10 @@
clippy::match_wildcard_for_single_variants
)]
use std::cell::Cell;
use oxc_allocator::Vec;
use oxc_syntax::scope::ScopeFlags;
use oxc_syntax::scope::{ScopeFlags, ScopeId};
use crate::{ast::*, ast_kind::AstKind};
@ -28,7 +30,7 @@ pub trait Visit<'a>: Sized {
fn enter_node(&mut self, kind: AstKind<'a>) {}
fn leave_node(&mut self, kind: AstKind<'a>) {}
fn enter_scope(&mut self, flags: ScopeFlags) {}
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {}
fn leave_scope(&mut self) {}
#[inline]
@ -1350,13 +1352,17 @@ pub mod walk {
#[inline]
pub fn walk_program<'a, V: Visit<'a>>(visitor: &mut V, it: &Program<'a>) {
visitor.enter_scope({
let mut flags = ScopeFlags::Top;
if it.source_type.is_strict() || it.directives.iter().any(Directive::is_use_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::Top;
if it.source_type.is_strict() || it.directives.iter().any(Directive::is_use_strict)
{
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstKind::Program(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_directives(&it.directives);
@ -1433,7 +1439,7 @@ pub mod walk {
#[inline]
pub fn walk_block_statement<'a, V: Visit<'a>>(visitor: &mut V, it: &BlockStatement<'a>) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstKind::BlockStatement(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_statements(&it.body);
@ -1699,13 +1705,16 @@ pub mod walk {
visitor: &mut V,
it: &ArrowFunctionExpression<'a>,
) {
visitor.enter_scope({
let mut flags = ScopeFlags::Function | ScopeFlags::Arrow;
if it.body.has_use_strict_directive() {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::Function | ScopeFlags::Arrow;
if it.body.has_use_strict_directive() {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstKind::ArrowFunctionExpression(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_formal_parameters(&it.params);
@ -2064,7 +2073,7 @@ pub mod walk {
#[inline]
pub fn walk_ts_type_parameter<'a, V: Visit<'a>>(visitor: &mut V, it: &TSTypeParameter<'a>) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstKind::TSTypeParameter(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_binding_identifier(&it.name);
@ -2916,7 +2925,7 @@ pub mod walk {
let kind = AstKind::Class(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_decorators(&it.decorators);
visitor.enter_scope(ScopeFlags::StrictMode);
visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id);
if let Some(id) = &it.id {
visitor.visit_binding_identifier(id);
}
@ -2972,7 +2981,7 @@ pub mod walk {
#[inline]
pub fn walk_static_block<'a, V: Visit<'a>>(visitor: &mut V, it: &StaticBlock<'a>) {
visitor.enter_scope(ScopeFlags::ClassStaticBlock);
visitor.enter_scope(ScopeFlags::ClassStaticBlock, &it.scope_id);
let kind = AstKind::StaticBlock(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_statements(&it.body);
@ -3003,13 +3012,16 @@ pub mod walk {
it: &Function<'a>,
flags: Option<ScopeFlags>,
) {
visitor.enter_scope({
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if it.body.as_ref().is_some_and(|body| body.has_use_strict_directive()) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if it.body.as_ref().is_some_and(|body| body.has_use_strict_directive()) {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstKind::Function(visitor.alloc(it));
visitor.enter_node(kind);
if let Some(id) = &it.id {
@ -3473,7 +3485,7 @@ pub mod walk {
pub fn walk_for_in_statement<'a, V: Visit<'a>>(visitor: &mut V, it: &ForInStatement<'a>) {
let scope_events_cond = it.left.is_lexical_declaration();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstKind::ForInStatement(visitor.alloc(it));
visitor.enter_node(kind);
@ -3544,7 +3556,7 @@ pub mod walk {
pub fn walk_for_of_statement<'a, V: Visit<'a>>(visitor: &mut V, it: &ForOfStatement<'a>) {
let scope_events_cond = it.left.is_lexical_declaration();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstKind::ForOfStatement(visitor.alloc(it));
visitor.enter_node(kind);
@ -3562,7 +3574,7 @@ pub mod walk {
let scope_events_cond =
it.init.as_ref().is_some_and(ForStatementInit::is_lexical_declaration);
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstKind::ForStatement(visitor.alloc(it));
visitor.enter_node(kind);
@ -3630,7 +3642,7 @@ pub mod walk {
let kind = AstKind::SwitchStatement(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_expression(&it.discriminant);
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_switch_cases(&it.cases);
visitor.leave_node(kind);
visitor.leave_scope();
@ -3680,7 +3692,7 @@ pub mod walk {
pub fn walk_catch_clause<'a, V: Visit<'a>>(visitor: &mut V, it: &CatchClause<'a>) {
let scope_events_cond = it.param.is_some();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstKind::CatchClause(visitor.alloc(it));
visitor.enter_node(kind);
@ -3704,7 +3716,7 @@ pub mod walk {
#[inline]
pub fn walk_finally_clause<'a, V: Visit<'a>>(visitor: &mut V, it: &BlockStatement<'a>) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstKind::FinallyClause(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_statements(&it.body);
@ -3817,7 +3829,7 @@ pub mod walk {
let kind = AstKind::TSEnumDeclaration(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_binding_identifier(&it.id);
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_enum_members(&it.members);
visitor.leave_node(kind);
visitor.leave_scope();
@ -3859,13 +3871,16 @@ pub mod walk {
let kind = AstKind::TSModuleDeclaration(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_ts_module_declaration_name(&it.id);
visitor.enter_scope({
let mut flags = ScopeFlags::TsModuleBlock;
if it.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::TsModuleBlock;
if it.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
if let Some(body) = &it.body {
visitor.visit_ts_module_declaration_body(body);
}

View file

@ -16,19 +16,21 @@
clippy::match_wildcard_for_single_variants
)]
use std::cell::Cell;
use oxc_allocator::Vec;
use oxc_syntax::scope::ScopeFlags;
use oxc_syntax::scope::{ScopeFlags, ScopeId};
use crate::{ast::*, ast_kind::AstType};
use walk_mut::*;
/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in place.
/// Syntax tree traversal
pub trait VisitMut<'a>: Sized {
fn enter_node(&mut self, ty: AstType) {}
fn leave_node(&mut self, ty: AstType) {}
fn enter_node(&mut self, kind: AstType) {}
fn leave_node(&mut self, kind: AstType) {}
fn enter_scope(&mut self, flags: ScopeFlags) {}
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {}
fn leave_scope(&mut self) {}
#[inline]
@ -1342,13 +1344,17 @@ pub mod walk_mut {
#[inline]
pub fn walk_program<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut Program<'a>) {
visitor.enter_scope({
let mut flags = ScopeFlags::Top;
if it.source_type.is_strict() || it.directives.iter().any(Directive::is_use_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::Top;
if it.source_type.is_strict() || it.directives.iter().any(Directive::is_use_strict)
{
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstType::Program;
visitor.enter_node(kind);
visitor.visit_directives(&mut it.directives);
@ -1425,7 +1431,7 @@ pub mod walk_mut {
#[inline]
pub fn walk_block_statement<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut BlockStatement<'a>) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstType::BlockStatement;
visitor.enter_node(kind);
visitor.visit_statements(&mut it.body);
@ -1718,13 +1724,16 @@ pub mod walk_mut {
visitor: &mut V,
it: &mut ArrowFunctionExpression<'a>,
) {
visitor.enter_scope({
let mut flags = ScopeFlags::Function | ScopeFlags::Arrow;
if it.body.has_use_strict_directive() {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::Function | ScopeFlags::Arrow;
if it.body.has_use_strict_directive() {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstType::ArrowFunctionExpression;
visitor.enter_node(kind);
visitor.visit_formal_parameters(&mut it.params);
@ -2125,7 +2134,7 @@ pub mod walk_mut {
visitor: &mut V,
it: &mut TSTypeParameter<'a>,
) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstType::TSTypeParameter;
visitor.enter_node(kind);
visitor.visit_binding_identifier(&mut it.name);
@ -3040,7 +3049,7 @@ pub mod walk_mut {
let kind = AstType::Class;
visitor.enter_node(kind);
visitor.visit_decorators(&mut it.decorators);
visitor.enter_scope(ScopeFlags::StrictMode);
visitor.enter_scope(ScopeFlags::StrictMode, &it.scope_id);
if let Some(id) = &mut it.id {
visitor.visit_binding_identifier(id);
}
@ -3099,7 +3108,7 @@ pub mod walk_mut {
#[inline]
pub fn walk_static_block<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut StaticBlock<'a>) {
visitor.enter_scope(ScopeFlags::ClassStaticBlock);
visitor.enter_scope(ScopeFlags::ClassStaticBlock, &it.scope_id);
let kind = AstType::StaticBlock;
visitor.enter_node(kind);
visitor.visit_statements(&mut it.body);
@ -3133,13 +3142,16 @@ pub mod walk_mut {
it: &mut Function<'a>,
flags: Option<ScopeFlags>,
) {
visitor.enter_scope({
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if it.body.as_ref().is_some_and(|body| body.has_use_strict_directive()) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if it.body.as_ref().is_some_and(|body| body.has_use_strict_directive()) {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
let kind = AstType::Function;
visitor.enter_node(kind);
if let Some(id) = &mut it.id {
@ -3654,7 +3666,7 @@ pub mod walk_mut {
) {
let scope_events_cond = it.left.is_lexical_declaration();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstType::ForInStatement;
visitor.enter_node(kind);
@ -3734,7 +3746,7 @@ pub mod walk_mut {
) {
let scope_events_cond = it.left.is_lexical_declaration();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstType::ForOfStatement;
visitor.enter_node(kind);
@ -3752,7 +3764,7 @@ pub mod walk_mut {
let scope_events_cond =
it.init.as_ref().is_some_and(ForStatementInit::is_lexical_declaration);
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstType::ForStatement;
visitor.enter_node(kind);
@ -3832,7 +3844,7 @@ pub mod walk_mut {
let kind = AstType::SwitchStatement;
visitor.enter_node(kind);
visitor.visit_expression(&mut it.discriminant);
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_switch_cases(&mut it.cases);
visitor.leave_node(kind);
visitor.leave_scope();
@ -3885,7 +3897,7 @@ pub mod walk_mut {
pub fn walk_catch_clause<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut CatchClause<'a>) {
let scope_events_cond = it.param.is_some();
if scope_events_cond {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
}
let kind = AstType::CatchClause;
visitor.enter_node(kind);
@ -3909,7 +3921,7 @@ pub mod walk_mut {
#[inline]
pub fn walk_finally_clause<'a, V: VisitMut<'a>>(visitor: &mut V, it: &mut BlockStatement<'a>) {
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
let kind = AstType::FinallyClause;
visitor.enter_node(kind);
visitor.visit_statements(&mut it.body);
@ -4028,7 +4040,7 @@ pub mod walk_mut {
let kind = AstType::TSEnumDeclaration;
visitor.enter_node(kind);
visitor.visit_binding_identifier(&mut it.id);
visitor.enter_scope(ScopeFlags::empty());
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_enum_members(&mut it.members);
visitor.leave_node(kind);
visitor.leave_scope();
@ -4076,13 +4088,16 @@ pub mod walk_mut {
let kind = AstType::TSModuleDeclaration;
visitor.enter_node(kind);
visitor.visit_ts_module_declaration_name(&mut it.id);
visitor.enter_scope({
let mut flags = ScopeFlags::TsModuleBlock;
if it.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
});
visitor.enter_scope(
{
let mut flags = ScopeFlags::TsModuleBlock;
if it.body.as_ref().is_some_and(TSModuleDeclarationBody::is_strict) {
flags |= ScopeFlags::StrictMode;
}
flags
},
&it.scope_id,
);
if let Some(body) = &mut it.body {
visitor.visit_ts_module_declaration_body(body);
}

View file

@ -1,3 +1,5 @@
use std::cell::Cell;
use oxc_allocator::Box;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
@ -135,7 +137,8 @@ impl<'a> IsolatedDeclarations<'a> {
block: &Box<'a, TSModuleBlock<'a>>,
) -> Box<'a, TSModuleBlock<'a>> {
// We need to enter a new scope for the module block, avoid add binding to the parent scope
self.scope.enter_scope(ScopeFlags::TsModuleBlock);
// TODO: doesn't have a scope_id!
self.scope.enter_scope(ScopeFlags::TsModuleBlock, &Cell::default());
let stmts = self.transform_statements_on_demand(&block.body);
self.scope.leave_scope();
self.ast.alloc_ts_module_block(SPAN, self.ast.vec(), stmts)

View file

@ -1,3 +1,5 @@
use std::cell::Cell;
use oxc_ast::{
ast::{
ArrowFunctionExpression, BindingIdentifier, Expression, Function, FunctionBody,
@ -6,7 +8,7 @@ use oxc_ast::{
AstBuilder, Visit,
};
use oxc_span::{Atom, GetSpan, SPAN};
use oxc_syntax::scope::ScopeFlags;
use oxc_syntax::scope::{ScopeFlags, ScopeId};
use crate::{diagnostics::type_containing_private_name, IsolatedDeclarations};
@ -114,7 +116,7 @@ impl<'a> FunctionReturnType<'a> {
}
impl<'a> Visit<'a> for FunctionReturnType<'a> {
fn enter_scope(&mut self, _flags: ScopeFlags) {
fn enter_scope(&mut self, _flags: ScopeFlags, _: &Cell<Option<ScopeId>>) {
self.scope_depth += 1;
}

View file

@ -1,3 +1,5 @@
use std::cell::Cell;
use oxc_allocator::{Allocator, Vec};
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
@ -5,7 +7,7 @@ use oxc_ast::AstBuilder;
#[allow(clippy::wildcard_imports)]
use oxc_ast::{visit::walk::*, Visit};
use oxc_span::Atom;
use oxc_syntax::scope::ScopeFlags;
use oxc_syntax::scope::{ScopeFlags, ScopeId};
use rustc_hash::FxHashSet;
/// Declaration scope.
@ -100,7 +102,7 @@ impl<'a> ScopeTree<'a> {
}
impl<'a> Visit<'a> for ScopeTree<'a> {
fn enter_scope(&mut self, flags: ScopeFlags) {
fn enter_scope(&mut self, flags: ScopeFlags, _: &Cell<Option<ScopeId>>) {
let scope = Scope::new(flags);
self.levels.push(scope);
}
@ -216,7 +218,8 @@ impl<'a> Visit<'a> for ScopeTree<'a> {
/// Because the type parameter is can be used in following nodes
/// until the end of the function. So we leave the scope in the parent node (Function)
fn visit_ts_type_parameter_declaration(&mut self, decl: &TSTypeParameterDeclaration<'a>) {
self.enter_scope(ScopeFlags::empty());
// TODO: doesn't have a scope_id!
self.enter_scope(ScopeFlags::empty(), &Cell::default());
decl.params.iter().for_each(|param| self.visit_ts_type_parameter(param));
// exit scope in parent AST node
}
@ -291,8 +294,9 @@ impl<'a> Visit<'a> for ScopeTree<'a> {
/// ^^^^^^^^^^^^^^^^^^^^
/// We need to add both `T` and `K` to the scope
fn visit_ts_mapped_type(&mut self, ty: &TSMappedType<'a>) {
// TODO: doesn't have a scope_id!
self.enter_scope(ScopeFlags::empty(), &Cell::default());
// copy from walk_ts_mapped_type
self.enter_scope(ScopeFlags::empty());
self.visit_ts_type_parameter(&ty.type_parameter);
if let Some(name) = &ty.name_type {
self.visit_ts_type(name);
@ -308,7 +312,8 @@ impl<'a> Visit<'a> for ScopeTree<'a> {
/// `Item` is a type parameter
/// We need to add `Item` to the scope
fn visit_conditional_expression(&mut self, expr: &ConditionalExpression<'a>) {
self.enter_scope(ScopeFlags::empty());
// TODO: doesn't have a scope_id!
self.enter_scope(ScopeFlags::empty(), &Cell::default());
walk_conditional_expression(self, expr);
self.leave_scope();
}

View file

@ -1,6 +1,10 @@
//! Semantic Builder
use std::{cell::RefCell, path::PathBuf, sync::Arc};
use std::{
cell::{Cell, RefCell},
path::PathBuf,
sync::Arc,
};
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, AstKind, Trivias, Visit};
@ -417,7 +421,7 @@ impl<'a> SemanticBuilder<'a> {
}
impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn enter_scope(&mut self, flags: ScopeFlags) {
fn enter_scope(&mut self, flags: ScopeFlags, _: &Cell<Option<ScopeId>>) {
let parent_scope_id =
if flags.contains(ScopeFlags::Top) { None } else { Some(self.current_scope_id) };
@ -456,13 +460,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_program(&mut self, program: &Program<'a>) {
let kind = AstKind::Program(self.alloc(program));
self.enter_scope({
let mut flags = ScopeFlags::Top;
if program.is_strict() {
flags |= ScopeFlags::StrictMode;
}
flags
});
self.enter_scope(
{
let mut flags = ScopeFlags::Top;
if program.is_strict() {
flags |= ScopeFlags::StrictMode;
}
flags
},
&program.scope_id,
);
program.scope_id.set(Some(self.current_scope_id));
/* cfg */
@ -491,7 +498,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_block_statement(&mut self, stmt: &BlockStatement<'a>) {
let kind = AstKind::BlockStatement(self.alloc(stmt));
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &stmt.scope_id);
stmt.scope_id.set(Some(self.current_scope_id));
self.enter_node(kind);
@ -759,7 +766,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let is_lexical_declaration =
stmt.init.as_ref().is_some_and(ForStatementInit::is_lexical_declaration);
if is_lexical_declaration {
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &stmt.scope_id);
stmt.scope_id.set(Some(self.current_scope_id));
}
self.enter_node(kind);
@ -845,7 +852,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::ForInStatement(self.alloc(stmt));
let is_lexical_declaration = stmt.left.is_lexical_declaration();
if is_lexical_declaration {
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &stmt.scope_id);
stmt.scope_id.set(Some(self.current_scope_id));
}
self.enter_node(kind);
@ -910,7 +917,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::ForOfStatement(self.alloc(stmt));
let is_lexical_declaration = stmt.left.is_lexical_declaration();
if is_lexical_declaration {
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &stmt.scope_id);
stmt.scope_id.set(Some(self.current_scope_id));
}
self.enter_node(kind);
@ -1096,7 +1103,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::SwitchStatement(self.alloc(stmt));
self.enter_node(kind);
self.visit_expression(&stmt.discriminant);
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &stmt.scope_id);
stmt.scope_id.set(Some(self.current_scope_id));
/* cfg */
@ -1343,7 +1350,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) {
let kind = AstKind::CatchClause(self.alloc(clause));
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &clause.scope_id);
clause.scope_id.set(Some(self.current_scope_id));
self.enter_node(kind);
if let Some(param) = &clause.param {
@ -1356,7 +1363,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_finally_clause(&mut self, clause: &BlockStatement<'a>) {
let kind = AstKind::FinallyClause(self.alloc(clause));
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &clause.scope_id);
clause.scope_id.set(Some(self.current_scope_id));
self.enter_node(kind);
self.visit_statements(&clause.body);
@ -1441,13 +1448,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_function(&mut self, func: &Function<'a>, flags: Option<ScopeFlags>) {
let kind = AstKind::Function(self.alloc(func));
self.enter_scope({
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if func.is_strict() {
flags |= ScopeFlags::StrictMode;
}
flags
});
self.enter_scope(
{
let mut flags = flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function;
if func.is_strict() {
flags |= ScopeFlags::StrictMode;
}
flags
},
&func.scope_id,
);
func.scope_id.set(Some(self.current_scope_id));
/* cfg */
@ -1516,7 +1526,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
if is_class_expr {
// Class expressions create a temporary scope with the class name as its only variable
// E.g., `let c = class A { foo() { console.log(A) } }`
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &class.scope_id);
class.scope_id.set(Some(self.current_scope_id));
}
@ -1545,7 +1555,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_static_block(&mut self, block: &StaticBlock<'a>) {
let kind = AstKind::StaticBlock(self.alloc(block));
self.enter_scope(ScopeFlags::ClassStaticBlock);
self.enter_scope(ScopeFlags::ClassStaticBlock, &block.scope_id);
block.scope_id.set(Some(self.current_scope_id));
self.enter_node(kind);
self.visit_statements(&block.body);
@ -1555,7 +1565,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_arrow_function_expression(&mut self, expr: &ArrowFunctionExpression<'a>) {
let kind = AstKind::ArrowFunctionExpression(self.alloc(expr));
self.enter_scope(ScopeFlags::Function | ScopeFlags::Arrow);
self.enter_scope(ScopeFlags::Function | ScopeFlags::Arrow, &expr.scope_id);
expr.scope_id.set(Some(self.current_scope_id));
/* cfg */
@ -1605,7 +1615,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::TSEnumDeclaration(self.alloc(decl));
self.enter_node(kind);
self.visit_binding_identifier(&decl.id);
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &decl.scope_id);
decl.scope_id.set(Some(self.current_scope_id));
for member in &decl.members {
self.visit_ts_enum_member(member);
@ -1621,7 +1631,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
TSModuleDeclarationName::Identifier(ident) => self.visit_identifier_name(ident),
TSModuleDeclarationName::StringLiteral(lit) => self.visit_string_literal(lit),
}
self.enter_scope(ScopeFlags::TsModuleBlock);
self.enter_scope(ScopeFlags::TsModuleBlock, &decl.scope_id);
decl.scope_id.set(Some(self.current_scope_id));
match &decl.body {
Some(TSModuleDeclarationBody::TSModuleDeclaration(decl)) => {
@ -1638,7 +1648,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn visit_ts_type_parameter(&mut self, ty: &TSTypeParameter<'a>) {
let kind = AstKind::TSTypeParameter(self.alloc(ty));
self.enter_scope(ScopeFlags::empty());
self.enter_scope(ScopeFlags::empty(), &ty.scope_id);
ty.scope_id.set(Some(self.current_scope_id));
self.enter_node(kind);
if let Some(constraint) = &ty.constraint {

View file

@ -37,7 +37,7 @@ impl Generator for VisitGenerator {
}
fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput {
GeneratorOutput::Stream(("visit", generate_visit(ctx)))
GeneratorOutput::Stream(("visit", generate_visit::<false>(ctx)))
}
}
@ -47,11 +47,11 @@ impl Generator for VisitMutGenerator {
}
fn generate(&mut self, ctx: &CodegenCtx) -> GeneratorOutput {
GeneratorOutput::Stream(("visit_mut", generate_visit_mut(ctx)))
GeneratorOutput::Stream(("visit_mut", generate_visit::<true>(ctx)))
}
}
static CLIPPY_ALLOW: &str = "\
const CLIPPY_ALLOW: &str = "\
unused_variables,\
clippy::extra_unused_type_parameters,\
clippy::explicit_iter_loop,\
@ -59,7 +59,7 @@ static CLIPPY_ALLOW: &str = "\
clippy::semicolon_if_nothing_returned,\
clippy::match_wildcard_for_single_variants";
fn generate_visit(ctx: &CodegenCtx) -> TokenStream {
fn generate_visit<const MUT: bool>(ctx: &CodegenCtx) -> TokenStream {
let header = generated_header!();
// we evaluate it outside of quote to take advantage of expression evaluation
// otherwise the `\n\` wouldn't work!
@ -71,41 +71,18 @@ fn generate_visit(ctx: &CodegenCtx) -> TokenStream {
//! * [rustc visitor](https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/visit.rs)\n\
"};
let (visits, walks) = VisitBuilder::new(ctx, false).build();
let (visits, walks) = VisitBuilder::new(ctx, MUT).build();
let clippy_attr = insert!("#![allow({})]", CLIPPY_ALLOW);
quote! {
#header
#file_docs
#clippy_attr
endl!();
use oxc_allocator::Vec;
use oxc_syntax::scope::ScopeFlags;
endl!();
use crate::{ast::*, ast_kind::AstKind};
endl!();
use walk::*;
endl!();
/// Syntax tree traversal
pub trait Visit<'a>: Sized {
fn enter_node(&mut self, kind: AstKind<'a>) {}
fn leave_node(&mut self, kind: AstKind<'a>) {}
endl!();
fn enter_scope(&mut self, flags: ScopeFlags) {}
fn leave_scope(&mut self) {}
endl!();
let walk_mod = if MUT { quote!(walk_mut) } else { quote!(walk) };
let trait_name = if MUT { quote!(VisitMut) } else { quote!(Visit) };
let ast_kind_type = if MUT { quote!(AstType) } else { quote!(AstKind) };
let ast_kind_life = if MUT { TokenStream::default() } else { quote!(<'a>) };
let may_alloc = if MUT {
TokenStream::default()
} else {
quote! {
#[inline]
fn alloc<T>(&self, t: &T) -> &'a T {
insert!("// SAFETY:");
@ -116,35 +93,8 @@ fn generate_visit(ctx: &CodegenCtx) -> TokenStream {
std::mem::transmute(t)
}
}
#(#visits)*
}
endl!();
pub mod walk {
use super::*;
#(#walks)*
}
}
}
fn generate_visit_mut(ctx: &CodegenCtx) -> TokenStream {
let header = generated_header!();
// we evaluate it outside of quote to take advantage of expression evaluation
// otherwise the `\n\` wouldn't work!
let file_docs = insert! {"\
//! Visitor Pattern\n\
//!\n\
//! See:\n\
//! * [visitor pattern](https://rust-unofficial.github.io/patterns/patterns/behavioural/visitor.html)\n\
//! * [rustc visitor](https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/visit.rs)\n\
"};
let (visits, walks) = VisitBuilder::new(ctx, true).build();
let clippy_attr = insert!("#![allow({})]", CLIPPY_ALLOW);
};
quote! {
#header
@ -153,37 +103,43 @@ fn generate_visit_mut(ctx: &CodegenCtx) -> TokenStream {
endl!();
use std::cell::Cell;
endl!();
use oxc_allocator::Vec;
use oxc_syntax::scope::ScopeFlags;
use oxc_syntax::scope::{ScopeFlags, ScopeId};
endl!();
use crate::{ast::*, ast_kind::AstType};
use crate::{ast::*, ast_kind::#ast_kind_type};
endl!();
use walk_mut::*;
use #walk_mod::*;
endl!();
/// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in place.
pub trait VisitMut<'a>: Sized {
fn enter_node(&mut self, ty: AstType) {}
fn leave_node(&mut self, ty: AstType) {}
/// Syntax tree traversal
pub trait #trait_name <'a>: Sized {
fn enter_node(&mut self, kind: #ast_kind_type #ast_kind_life) {}
fn leave_node(&mut self, kind: #ast_kind_type #ast_kind_life) {}
endl!();
fn enter_scope(&mut self, flags: ScopeFlags) {}
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {}
fn leave_scope(&mut self) {}
endl!();
#may_alloc
#(#visits)*
}
endl!();
pub mod walk_mut {
pub mod #walk_mod {
use super::*;
#(#walks)*
@ -533,7 +489,7 @@ impl<'a> VisitBuilder<'a> {
let flags = scope_args
.flags
.map_or_else(|| quote!(ScopeFlags::empty()), |it| it.to_token_stream());
let args = if let Some(strict_if) = scope_args.strict_if {
let flags = if let Some(strict_if) = scope_args.strict_if {
let strict_if =
strict_if.to_token_stream().replace_ident("self", &format_ident!("it"));
quote! {{
@ -547,7 +503,7 @@ impl<'a> VisitBuilder<'a> {
flags
};
let mut enter = cond.as_ref().into_token_stream();
enter.extend(maybe_conditional(quote!(visitor.enter_scope(#args);)));
enter.extend(maybe_conditional(quote!(visitor.enter_scope(#flags, &it.scope_id);)));
let leave = maybe_conditional(quote!(visitor.leave_scope();));
(enter, leave)
},