From eba5033a5839c435e6d1bd1960ca2ebfa4308acc Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:49:33 +0000 Subject: [PATCH] refactor(traverse): codegen `ChildScopeCollector` (#5119) Codegen `ChildScopeCollector`, so that if more types have scopes added, we don't forget to add them (like the problem #5118 fixed). The methods are in different order in the generated version, but otherwise identical to before this PR. `visit_finally_clause` has to be added manually, as `oxc_traverse` codegen does not read or understand `#[visit(as)]` attrs. --- crates/oxc_traverse/scripts/build.mjs | 2 + .../scripts/lib/scopes_collector.mjs | 50 +++++++ crates/oxc_traverse/src/context/scoping.rs | 123 +--------------- .../src/generated/scopes_collector.rs | 131 ++++++++++++++++++ crates/oxc_traverse/src/lib.rs | 2 + 5 files changed, 186 insertions(+), 122 deletions(-) create mode 100644 crates/oxc_traverse/scripts/lib/scopes_collector.mjs create mode 100644 crates/oxc_traverse/src/generated/scopes_collector.rs diff --git a/crates/oxc_traverse/scripts/build.mjs b/crates/oxc_traverse/scripts/build.mjs index e387dee175ed0..d41510f6a7f95 100644 --- a/crates/oxc_traverse/scripts/build.mjs +++ b/crates/oxc_traverse/scripts/build.mjs @@ -19,6 +19,7 @@ import getTypesFromCode from './lib/parse.mjs'; import generateTraverseTraitCode from './lib/traverse.mjs'; import generateAncestorsCode from './lib/ancestor.mjs'; import generateWalkFunctionsCode from './lib/walk.mjs'; +import generateScopesCollectorCode from './lib/scopes_collector.mjs'; const execAsync = promisify(exec); @@ -32,6 +33,7 @@ const outputDirPath = pathJoin(fileURLToPath(import.meta.url), '../../src/genera await writeToFile('traverse.rs', generateTraverseTraitCode(types)); await writeToFile('ancestor.rs', generateAncestorsCode(types)); await writeToFile('walk.rs', generateWalkFunctionsCode(types)); +await writeToFile('scopes_collector.rs', generateScopesCollectorCode(types)); async function writeToFile(filename, code) { code = `${PREAMBLE}${code}`; diff --git a/crates/oxc_traverse/scripts/lib/scopes_collector.mjs b/crates/oxc_traverse/scripts/lib/scopes_collector.mjs new file mode 100644 index 0000000000000..72a0a90dc6be1 --- /dev/null +++ b/crates/oxc_traverse/scripts/lib/scopes_collector.mjs @@ -0,0 +1,50 @@ +import {camelToSnake} from './utils.mjs'; + +export default function generateScopesCollectorCode(types) { + let methods = ''; + for (const type of Object.values(types)) { + if (type.kind === 'enum' || !type.scopeArgs) continue; + + const extraParams = type.scopeArgs.flags === 'flags' ? ', _flags: ScopeFlags' : ''; + methods += ` + #[inline] + fn visit_${camelToSnake(type.name)}(&mut self, it: &${type.rawName}${extraParams}) { + self.add_scope(&it.scope_id); + } + `; + } + + return ` + use std::cell::Cell; + + #[allow(clippy::wildcard_imports)] + use oxc_ast::{ast::*, visit::Visit}; + use oxc_syntax::scope::{ScopeFlags, ScopeId}; + + /// Visitor that locates all child scopes. + /// NB: Child scopes only, not grandchild scopes. + /// Does not do full traversal - stops each time it hits a node with a scope. + pub(crate) struct ChildScopeCollector { + pub(crate) scope_ids: Vec, + } + + impl ChildScopeCollector { + pub(crate) fn new() -> Self { + Self { scope_ids: vec![] } + } + + pub(crate) fn add_scope(&mut self, scope_id: &Cell>) { + self.scope_ids.push(scope_id.get().unwrap()); + } + } + + impl<'a> Visit<'a> for ChildScopeCollector { + ${methods} + + #[inline] + fn visit_finally_clause(&mut self, it: &BlockStatement<'a>) { + self.add_scope(&it.scope_id); + } + } + `; +} diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index 691eab2d19cc3..92e899552125b 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -12,6 +12,7 @@ use oxc_syntax::{ }; use super::ast_operations::GatherNodeParts; +use crate::scopes_collector::ChildScopeCollector; /// Traverse scope context. /// @@ -516,125 +517,3 @@ fn create_uid_name_base(name: &str) -> CompactString { str.push_str(name); str } - -/// Visitor that locates all child scopes. -/// NB: Child scopes only, not grandchild scopes. -/// Does not do full traversal - stops each time it hits a node with a scope. -struct ChildScopeCollector { - scope_ids: Vec, -} - -impl ChildScopeCollector { - fn new() -> Self { - Self { scope_ids: vec![] } - } - - fn add_scope(&mut self, scope_id: &Cell>) { - self.scope_ids.push(scope_id.get().unwrap()); - } -} - -impl<'a> Visit<'a> for ChildScopeCollector { - #[inline] - fn visit_program(&mut self, program: &Program<'a>) { - self.add_scope(&program.scope_id); - } - - #[inline] - fn visit_block_statement(&mut self, stmt: &BlockStatement<'a>) { - self.add_scope(&stmt.scope_id); - } - - #[inline] - fn visit_for_statement(&mut self, stmt: &ForStatement<'a>) { - self.add_scope(&stmt.scope_id); - } - - #[inline] - fn visit_for_in_statement(&mut self, stmt: &ForInStatement<'a>) { - self.add_scope(&stmt.scope_id); - } - - #[inline] - fn visit_for_of_statement(&mut self, stmt: &ForOfStatement<'a>) { - self.add_scope(&stmt.scope_id); - } - - #[inline] - fn visit_switch_statement(&mut self, stmt: &SwitchStatement<'a>) { - self.add_scope(&stmt.scope_id); - } - - #[inline] - fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) { - self.add_scope(&clause.scope_id); - } - - #[inline] - fn visit_finally_clause(&mut self, block: &BlockStatement<'a>) { - self.add_scope(&block.scope_id); - } - - #[inline] - fn visit_function(&mut self, func: &Function<'a>, _flags: ScopeFlags) { - self.add_scope(&func.scope_id); - } - - #[inline] - fn visit_class(&mut self, class: &Class<'a>) { - self.add_scope(&class.scope_id); - } - - #[inline] - fn visit_static_block(&mut self, block: &StaticBlock<'a>) { - self.add_scope(&block.scope_id); - } - - #[inline] - fn visit_arrow_function_expression(&mut self, expr: &ArrowFunctionExpression<'a>) { - self.add_scope(&expr.scope_id); - } - - #[inline] - fn visit_ts_enum_declaration(&mut self, decl: &TSEnumDeclaration<'a>) { - self.add_scope(&decl.scope_id); - } - - #[inline] - fn visit_ts_module_declaration(&mut self, decl: &TSModuleDeclaration<'a>) { - self.add_scope(&decl.scope_id); - } - - #[inline] - fn visit_ts_interface_declaration(&mut self, it: &TSInterfaceDeclaration<'a>) { - self.add_scope(&it.scope_id); - } - - #[inline] - fn visit_ts_mapped_type(&mut self, it: &TSMappedType<'a>) { - self.add_scope(&it.scope_id); - } - - #[inline] - fn visit_ts_conditional_type(&mut self, it: &TSConditionalType<'a>) { - self.add_scope(&it.scope_id); - } - - #[inline] - fn visit_ts_type_alias_declaration(&mut self, it: &TSTypeAliasDeclaration<'a>) { - self.add_scope(&it.scope_id); - } - - #[inline] - fn visit_ts_method_signature(&mut self, it: &TSMethodSignature<'a>) { - self.add_scope(&it.scope_id); - } - - #[inline] - fn visit_ts_construct_signature_declaration( - &mut self, - it: &TSConstructSignatureDeclaration<'a>, - ) { - self.add_scope(&it.scope_id); - } -} diff --git a/crates/oxc_traverse/src/generated/scopes_collector.rs b/crates/oxc_traverse/src/generated/scopes_collector.rs new file mode 100644 index 0000000000000..4eaca8af35d31 --- /dev/null +++ b/crates/oxc_traverse/src/generated/scopes_collector.rs @@ -0,0 +1,131 @@ +// Auto-generated code, DO NOT EDIT DIRECTLY! +// Generated by `oxc_traverse/scripts/build.mjs`. +// To alter this generated file you have to edit the codegen. + +use std::cell::Cell; + +#[allow(clippy::wildcard_imports)] +use oxc_ast::{ast::*, visit::Visit}; +use oxc_syntax::scope::{ScopeFlags, ScopeId}; + +/// Visitor that locates all child scopes. +/// NB: Child scopes only, not grandchild scopes. +/// Does not do full traversal - stops each time it hits a node with a scope. +pub(crate) struct ChildScopeCollector { + pub(crate) scope_ids: Vec, +} + +impl ChildScopeCollector { + pub(crate) fn new() -> Self { + Self { scope_ids: vec![] } + } + + pub(crate) fn add_scope(&mut self, scope_id: &Cell>) { + self.scope_ids.push(scope_id.get().unwrap()); + } +} + +impl<'a> Visit<'a> for ChildScopeCollector { + #[inline] + fn visit_program(&mut self, it: &Program<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_block_statement(&mut self, it: &BlockStatement<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_for_statement(&mut self, it: &ForStatement<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_for_in_statement(&mut self, it: &ForInStatement<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_switch_statement(&mut self, it: &SwitchStatement<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_catch_clause(&mut self, it: &CatchClause<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_function(&mut self, it: &Function<'a>, _flags: ScopeFlags) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_arrow_function_expression(&mut self, it: &ArrowFunctionExpression<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_class(&mut self, it: &Class<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_static_block(&mut self, it: &StaticBlock<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_enum_declaration(&mut self, it: &TSEnumDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_conditional_type(&mut self, it: &TSConditionalType<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_type_alias_declaration(&mut self, it: &TSTypeAliasDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_interface_declaration(&mut self, it: &TSInterfaceDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_method_signature(&mut self, it: &TSMethodSignature<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_construct_signature_declaration( + &mut self, + it: &TSConstructSignatureDeclaration<'a>, + ) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_module_declaration(&mut self, it: &TSModuleDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_mapped_type(&mut self, it: &TSMappedType<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_finally_clause(&mut self, it: &BlockStatement<'a>) { + self.add_scope(&it.scope_id); + } +} diff --git a/crates/oxc_traverse/src/lib.rs b/crates/oxc_traverse/src/lib.rs index 5984bb3e5751b..5f2b4dafd514a 100644 --- a/crates/oxc_traverse/src/lib.rs +++ b/crates/oxc_traverse/src/lib.rs @@ -69,11 +69,13 @@ pub use context::{TraverseAncestry, TraverseCtx, TraverseScoping}; mod generated { pub mod ancestor; + pub(super) mod scopes_collector; pub mod traverse; pub(super) mod walk; } pub use generated::ancestor; pub use generated::ancestor::Ancestor; +use generated::scopes_collector; pub use generated::traverse::Traverse; use generated::walk;