Skip to content

Commit

Permalink
feat(traverse): record current block scope (#8007)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
overlookmotel committed Dec 20, 2024
1 parent 059a5dd commit 6b6444b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
17 changes: 17 additions & 0 deletions crates/oxc_traverse/scripts/lib/walk.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
14 changes: 14 additions & 0 deletions crates/oxc_traverse/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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);
}
}
14 changes: 14 additions & 0 deletions crates/oxc_traverse/src/context/scoping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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),
}
}

Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_traverse/src/generated/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand All @@ -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);
}

Expand Down Expand Up @@ -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),
));
Expand All @@ -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);
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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),
));
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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(
Expand All @@ -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);
}

Expand Down Expand Up @@ -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>)
{
Expand All @@ -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);
}

Expand Down

0 comments on commit 6b6444b

Please sign in to comment.