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.
This commit is contained in:
overlookmotel 2024-12-20 03:50:44 +00:00
parent 059a5dd56a
commit 6b6444b523
4 changed files with 63 additions and 0 deletions

View file

@ -129,6 +129,23 @@ function generateWalkForStruct(type, types) {
`; `;
exitScopeCode += 'ctx.set_current_hoist_scope_id(previous_hoist_scope_id);'; 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) => { const fieldsCodes = visitedFields.map((field, index) => {

View file

@ -188,6 +188,14 @@ impl<'a> TraverseCtx<'a> {
self.scoping.current_hoist_scope_id() 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. /// Get current scope flags.
/// ///
/// Shortcut for `ctx.scoping.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) { pub(crate) fn set_current_hoist_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_hoist_scope_id(scope_id); 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);
}
} }

View file

@ -27,6 +27,7 @@ pub struct TraverseScoping {
uid_names: Option<FxHashSet<CompactStr>>, uid_names: Option<FxHashSet<CompactStr>>,
current_scope_id: ScopeId, current_scope_id: ScopeId,
current_hoist_scope_id: ScopeId, current_hoist_scope_id: ScopeId,
current_block_scope_id: ScopeId,
} }
// Public methods // Public methods
@ -43,6 +44,12 @@ impl TraverseScoping {
self.current_hoist_scope_id 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 /// Get current scope flags
#[inline] #[inline]
pub fn current_scope_flags(&self) -> ScopeFlags { pub fn current_scope_flags(&self) -> ScopeFlags {
@ -390,6 +397,7 @@ impl TraverseScoping {
// Dummy values. Both immediately overwritten in `walk_program`. // Dummy values. Both immediately overwritten in `walk_program`.
current_scope_id: ScopeId::new(0), current_scope_id: ScopeId::new(0),
current_hoist_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; 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`. /// Get `uid_names`.
/// ///
/// Iterate through all symbols and unresolved references in AST and identify any var names /// Iterate through all symbols and unresolved references in AST and identify any var names

View file

@ -39,6 +39,8 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>(
ctx.set_current_scope_id(current_scope_id); ctx.set_current_scope_id(current_scope_id);
let previous_hoist_scope_id = ctx.current_hoist_scope_id(); let previous_hoist_scope_id = ctx.current_hoist_scope_id();
ctx.set_current_hoist_scope_id(current_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 let pop_token = ctx
.push_stack(Ancestor::ProgramHashbang(ancestor::ProgramWithoutHashbang(node, PhantomData))); .push_stack(Ancestor::ProgramHashbang(ancestor::ProgramWithoutHashbang(node, PhantomData)));
if let Some(field) = if let Some(field) =
@ -62,6 +64,7 @@ pub(crate) unsafe fn walk_program<'a, Tr: Traverse<'a>>(
ctx.pop_stack(pop_token); ctx.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); ctx.set_current_scope_id(previous_scope_id);
ctx.set_current_hoist_scope_id(previous_hoist_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); traverser.exit_program(&mut *node, ctx);
} }
@ -1425,6 +1428,8 @@ pub(crate) unsafe fn walk_block_statement<'a, Tr: Traverse<'a>>(
.get() .get()
.unwrap(); .unwrap();
ctx.set_current_scope_id(current_scope_id); 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( let pop_token = ctx.push_stack(Ancestor::BlockStatementBody(
ancestor::BlockStatementWithoutBody(node, PhantomData), 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.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); 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); 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); ctx.set_current_scope_id(current_scope_id);
let previous_hoist_scope_id = ctx.current_hoist_scope_id(); let previous_hoist_scope_id = ctx.current_hoist_scope_id();
ctx.set_current_hoist_scope_id(current_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 = let pop_token =
ctx.push_stack(Ancestor::FunctionId(ancestor::FunctionWithoutId(node, PhantomData))); ctx.push_stack(Ancestor::FunctionId(ancestor::FunctionWithoutId(node, PhantomData)));
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)
@ -2308,6 +2316,7 @@ pub(crate) unsafe fn walk_function<'a, Tr: Traverse<'a>>(
ctx.pop_stack(pop_token); ctx.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); ctx.set_current_scope_id(previous_scope_id);
ctx.set_current_hoist_scope_id(previous_hoist_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); 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); ctx.set_current_scope_id(current_scope_id);
let previous_hoist_scope_id = ctx.current_hoist_scope_id(); let previous_hoist_scope_id = ctx.current_hoist_scope_id();
ctx.set_current_hoist_scope_id(current_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( let pop_token = ctx.push_stack(Ancestor::ArrowFunctionExpressionTypeParameters(
ancestor::ArrowFunctionExpressionWithoutTypeParameters(node, PhantomData), 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.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); ctx.set_current_scope_id(previous_scope_id);
ctx.set_current_hoist_scope_id(previous_hoist_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); 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); ctx.set_current_scope_id(current_scope_id);
let previous_hoist_scope_id = ctx.current_hoist_scope_id(); let previous_hoist_scope_id = ctx.current_hoist_scope_id();
ctx.set_current_hoist_scope_id(current_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 let pop_token = ctx
.push_stack(Ancestor::StaticBlockBody(ancestor::StaticBlockWithoutBody(node, PhantomData))); .push_stack(Ancestor::StaticBlockBody(ancestor::StaticBlockWithoutBody(node, PhantomData)));
walk_statements( walk_statements(
@ -2665,6 +2679,7 @@ pub(crate) unsafe fn walk_static_block<'a, Tr: Traverse<'a>>(
ctx.pop_stack(pop_token); ctx.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); ctx.set_current_scope_id(previous_scope_id);
ctx.set_current_hoist_scope_id(previous_hoist_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); 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); ctx.set_current_scope_id(current_scope_id);
let previous_hoist_scope_id = ctx.current_hoist_scope_id(); let previous_hoist_scope_id = ctx.current_hoist_scope_id();
ctx.set_current_hoist_scope_id(current_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) if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_TS_MODULE_DECLARATION_BODY)
as *mut Option<TSModuleDeclarationBody>) as *mut Option<TSModuleDeclarationBody>)
{ {
@ -4926,6 +4943,7 @@ pub(crate) unsafe fn walk_ts_module_declaration<'a, Tr: Traverse<'a>>(
ctx.pop_stack(pop_token); ctx.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id); ctx.set_current_scope_id(previous_scope_id);
ctx.set_current_hoist_scope_id(previous_hoist_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); traverser.exit_ts_module_declaration(&mut *node, ctx);
} }