mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
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:
parent
059a5dd56a
commit
6b6444b523
4 changed files with 63 additions and 0 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ pub struct TraverseScoping {
|
|||
uid_names: Option<FxHashSet<CompactStr>>,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<TSModuleDeclarationBody>)
|
||||
{
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue