From 6b6444b52344562fa368827828aa35990c3a86aa Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Fri, 20 Dec 2024 03:50:44 +0000 Subject: [PATCH] feat(traverse): record current block scope (#8007) Record "block" scope ID along with "hoist" scope ID in `Traverse`. "Block" scope is the scope where a `let` statement would be inserted above current position in AST. Block scope and current scope differ from each other inside classes. For example, if want to create a `let` temp var for `foo()` or `bar()` in example below, should use block scope not current scope. Current scope is the class itself, but the `let` statement will be inserted *outside* the class. ```js class C { [foo()]: bar(); } ``` All transforms which create `let` bindings should use block scope not current scope. We should add `VarDeclarationsStore::insert_let` method which uses block scope, to accompany `insert_var` (which uses hoist scope). This is implemented in a rather hacky way, and we should improve it later. Notably, we're not considering `for` statements as block scopes because we currently have no way to insert `let` statements into them if they don't have a body block. --- crates/oxc_traverse/scripts/lib/walk.mjs | 17 +++++++++++++++++ crates/oxc_traverse/src/context/mod.rs | 14 ++++++++++++++ crates/oxc_traverse/src/context/scoping.rs | 14 ++++++++++++++ crates/oxc_traverse/src/generated/walk.rs | 18 ++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/crates/oxc_traverse/scripts/lib/walk.mjs b/crates/oxc_traverse/scripts/lib/walk.mjs index 03f10795761d9..2fab7dbcfd360 100644 --- a/crates/oxc_traverse/scripts/lib/walk.mjs +++ b/crates/oxc_traverse/scripts/lib/walk.mjs @@ -129,6 +129,23 @@ function generateWalkForStruct(type, types) { `; exitScopeCode += 'ctx.set_current_hoist_scope_id(previous_hoist_scope_id);'; } + + // TODO: Type names shouldn't be hard-coded here. Block scopes should be signalled by attrs in AST. + let isBlockScope = [ + 'Program', + 'BlockStatement', + 'Function', + 'ArrowFunctionExpression', + 'StaticBlock', + 'TSModuleDeclaration', + ].includes(type.name); + if (isBlockScope) { + enterScopeCode += ` + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); + `; + exitScopeCode += 'ctx.set_current_block_scope_id(previous_block_scope_id);'; + } } const fieldsCodes = visitedFields.map((field, index) => { diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 4df72220d5c43..98b62e8d5c0b2 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -188,6 +188,14 @@ impl<'a> TraverseCtx<'a> { self.scoping.current_hoist_scope_id() } + /// Get current block scope ID. + /// + /// Shortcut for `ctx.scoping.current_block_scope_id`. + #[inline] + pub fn current_block_scope_id(&self) -> ScopeId { + self.scoping.current_block_scope_id() + } + /// Get current scope flags. /// /// Shortcut for `ctx.scoping.current_scope_flags`. @@ -659,4 +667,10 @@ impl<'a> TraverseCtx<'a> { pub(crate) fn set_current_hoist_scope_id(&mut self, scope_id: ScopeId) { self.scoping.set_current_hoist_scope_id(scope_id); } + + /// Shortcut for `ctx.scoping.set_current_block_scope_id`, to make `walk_*` methods less verbose. + #[inline] + pub(crate) fn set_current_block_scope_id(&mut self, scope_id: ScopeId) { + self.scoping.set_current_block_scope_id(scope_id); + } } diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index 5ec763a87689d..96825d5bd7da6 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -27,6 +27,7 @@ pub struct TraverseScoping { uid_names: Option>, current_scope_id: ScopeId, current_hoist_scope_id: ScopeId, + current_block_scope_id: ScopeId, } // Public methods @@ -43,6 +44,12 @@ impl TraverseScoping { self.current_hoist_scope_id } + /// Get current block scope ID + #[inline] + pub(crate) fn current_block_scope_id(&self) -> ScopeId { + self.current_block_scope_id + } + /// Get current scope flags #[inline] pub fn current_scope_flags(&self) -> ScopeFlags { @@ -390,6 +397,7 @@ impl TraverseScoping { // Dummy values. Both immediately overwritten in `walk_program`. current_scope_id: ScopeId::new(0), current_hoist_scope_id: ScopeId::new(0), + current_block_scope_id: ScopeId::new(0), } } @@ -410,6 +418,12 @@ impl TraverseScoping { self.current_hoist_scope_id = scope_id; } + /// Set current block scope ID + #[inline] + pub(crate) fn set_current_block_scope_id(&mut self, scope_id: ScopeId) { + self.current_block_scope_id = scope_id; + } + /// Get `uid_names`. /// /// Iterate through all symbols and unresolved references in AST and identify any var names diff --git a/crates/oxc_traverse/src/generated/walk.rs b/crates/oxc_traverse/src/generated/walk.rs index 9229e3ac35823..8437b28fb5c73 100644 --- a/crates/oxc_traverse/src/generated/walk.rs +++ b/crates/oxc_traverse/src/generated/walk.rs @@ -39,6 +39,8 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>( ctx.set_current_scope_id(current_scope_id); let previous_hoist_scope_id = ctx.current_hoist_scope_id(); ctx.set_current_hoist_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); let pop_token = ctx .push_stack(Ancestor::ProgramHashbang(ancestor::ProgramWithoutHashbang(node, PhantomData))); if let Some(field) = @@ -62,6 +64,7 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>( ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); ctx.set_current_hoist_scope_id(previous_hoist_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_program(&mut *node, ctx); } @@ -1425,6 +1428,8 @@ pub(crate) unsafe fn walk_block_statement<'a, Tr: Traverse<'a>>( .get() .unwrap(); ctx.set_current_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); let pop_token = ctx.push_stack(Ancestor::BlockStatementBody( ancestor::BlockStatementWithoutBody(node, PhantomData), )); @@ -1435,6 +1440,7 @@ pub(crate) unsafe fn walk_block_statement<'a, Tr: Traverse<'a>>( ); ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_block_statement(&mut *node, ctx); } @@ -2267,6 +2273,8 @@ pub(crate) unsafe fn walk_function<'a, Tr: Traverse<'a>>( ctx.set_current_scope_id(current_scope_id); let previous_hoist_scope_id = ctx.current_hoist_scope_id(); ctx.set_current_hoist_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); let pop_token = ctx.push_stack(Ancestor::FunctionId(ancestor::FunctionWithoutId(node, PhantomData))); if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_FUNCTION_ID) @@ -2308,6 +2316,7 @@ pub(crate) unsafe fn walk_function<'a, Tr: Traverse<'a>>( ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); ctx.set_current_hoist_scope_id(previous_hoist_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_function(&mut *node, ctx); } @@ -2401,6 +2410,8 @@ pub(crate) unsafe fn walk_arrow_function_expression<'a, Tr: Traverse<'a>>( ctx.set_current_scope_id(current_scope_id); let previous_hoist_scope_id = ctx.current_hoist_scope_id(); ctx.set_current_hoist_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); let pop_token = ctx.push_stack(Ancestor::ArrowFunctionExpressionTypeParameters( ancestor::ArrowFunctionExpressionWithoutTypeParameters(node, PhantomData), )); @@ -2434,6 +2445,7 @@ pub(crate) unsafe fn walk_arrow_function_expression<'a, Tr: Traverse<'a>>( ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); ctx.set_current_hoist_scope_id(previous_hoist_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_arrow_function_expression(&mut *node, ctx); } @@ -2655,6 +2667,8 @@ pub(crate) unsafe fn walk_static_block<'a, Tr: Traverse<'a>>( ctx.set_current_scope_id(current_scope_id); let previous_hoist_scope_id = ctx.current_hoist_scope_id(); ctx.set_current_hoist_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); let pop_token = ctx .push_stack(Ancestor::StaticBlockBody(ancestor::StaticBlockWithoutBody(node, PhantomData))); walk_statements( @@ -2665,6 +2679,7 @@ pub(crate) unsafe fn walk_static_block<'a, Tr: Traverse<'a>>( ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); ctx.set_current_hoist_scope_id(previous_hoist_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_static_block(&mut *node, ctx); } @@ -4917,6 +4932,8 @@ pub(crate) unsafe fn walk_ts_module_declaration<'a, Tr: Traverse<'a>>( ctx.set_current_scope_id(current_scope_id); let previous_hoist_scope_id = ctx.current_hoist_scope_id(); ctx.set_current_hoist_scope_id(current_scope_id); + let previous_block_scope_id = ctx.current_block_scope_id(); + ctx.set_current_block_scope_id(current_scope_id); if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_TS_MODULE_DECLARATION_BODY) as *mut Option) { @@ -4926,6 +4943,7 @@ pub(crate) unsafe fn walk_ts_module_declaration<'a, Tr: Traverse<'a>>( ctx.pop_stack(pop_token); ctx.set_current_scope_id(previous_scope_id); ctx.set_current_hoist_scope_id(previous_hoist_scope_id); + ctx.set_current_block_scope_id(previous_block_scope_id); traverser.exit_ts_module_declaration(&mut *node, ctx); }