Skip to content

Commit

Permalink
fix(transformer): create new scopes for new blocks in TS transform (#…
Browse files Browse the repository at this point in the history
…3908)

Create scopes for new `BlockStatement`s inserted in TS transform, and update scope tree.
  • Loading branch information
overlookmotel committed Jun 26, 2024
1 parent 01572f0 commit 17ad8f7
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 81 deletions.
21 changes: 1 addition & 20 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,27 +417,8 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
if flags.contains(ScopeFlags::Top) { None } else { Some(self.current_scope_id) };

let mut flags = flags;
// Inherit strict mode for functions
// https://tc39.es/ecma262/#sec-strict-mode-code
if let Some(parent_scope_id) = parent_scope_id {
let mut strict_mode = self.scope.root_flags().is_strict_mode();
let parent_scope_flags = self.scope.get_flags(parent_scope_id);

if !strict_mode
&& (parent_scope_flags.is_function() || parent_scope_flags.is_ts_module_block())
&& parent_scope_flags.is_strict_mode()
{
strict_mode = true;
}

// inherit flags for non-function scopes
if !flags.contains(ScopeFlags::Function) {
flags |= parent_scope_flags & ScopeFlags::Modifiers;
};

if strict_mode {
flags |= ScopeFlags::StrictMode;
}
flags = self.scope.get_new_scope_flags(flags, parent_scope_id);
}

self.current_scope_id = self.scope.add_scope(parent_scope_id, flags);
Expand Down
39 changes: 38 additions & 1 deletion crates/oxc_semantic/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ impl ScopeTree {
self.child_ids.get(&scope_id)
}

pub fn get_child_ids_mut(&mut self, scope_id: ScopeId) -> Option<&mut Vec<ScopeId>> {
self.child_ids.get_mut(&scope_id)
}

pub fn descendants_from_root(&self) -> impl Iterator<Item = ScopeId> + '_ {
self.parent_ids.iter_enumerated().map(|(scope_id, _)| scope_id)
}
Expand All @@ -95,10 +99,43 @@ impl ScopeTree {
&mut self.flags[scope_id]
}

pub fn get_new_scope_flags(&self, flags: ScopeFlags, parent_scope_id: ScopeId) -> ScopeFlags {
let mut strict_mode = self.root_flags().is_strict_mode();
let parent_scope_flags = self.get_flags(parent_scope_id);

// Inherit strict mode for functions
// https://tc39.es/ecma262/#sec-strict-mode-code
if !strict_mode
&& (parent_scope_flags.is_function() || parent_scope_flags.is_ts_module_block())
&& parent_scope_flags.is_strict_mode()
{
strict_mode = true;
}

// inherit flags for non-function scopes
let mut flags = flags;
if !flags.contains(ScopeFlags::Function) {
flags |= parent_scope_flags & ScopeFlags::Modifiers;
};

if strict_mode {
flags |= ScopeFlags::StrictMode;
}

flags
}

pub fn get_parent_id(&self, scope_id: ScopeId) -> Option<ScopeId> {
self.parent_ids[scope_id]
}

pub fn set_parent_id(&mut self, scope_id: ScopeId, parent_id: Option<ScopeId>) {
self.parent_ids[scope_id] = parent_id;
if let Some(parent_id) = parent_id {
self.child_ids.entry(parent_id).or_default().push(scope_id);
}
}

/// Get a variable binding by name that was declared in the top-level scope
pub fn get_root_binding(&self, name: &str) -> Option<SymbolId> {
self.get_binding(self.root_scope_id(), name)
Expand Down Expand Up @@ -143,7 +180,7 @@ impl ScopeTree {
&mut self.bindings[scope_id]
}

pub(crate) fn add_scope(&mut self, parent_id: Option<ScopeId>, flags: ScopeFlags) -> ScopeId {
pub fn add_scope(&mut self, parent_id: Option<ScopeId>, flags: ScopeFlags) -> ScopeId {
let scope_id = self.parent_ids.push(parent_id);
_ = self.flags.push(flags);
_ = self.bindings.push(Bindings::default());
Expand Down
16 changes: 8 additions & 8 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,24 +280,24 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x3_es2015.transform_declaration_on_exit(decl);
}

fn enter_if_statement(&mut self, stmt: &mut IfStatement<'a>, _ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_if_statement(stmt);
fn enter_if_statement(&mut self, stmt: &mut IfStatement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_if_statement(stmt, ctx);
}

fn enter_while_statement(&mut self, stmt: &mut WhileStatement<'a>, _ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_while_statement(stmt);
fn enter_while_statement(&mut self, stmt: &mut WhileStatement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_while_statement(stmt, ctx);
}

fn enter_do_while_statement(
&mut self,
stmt: &mut DoWhileStatement<'a>,
_ctx: &mut TraverseCtx<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.x0_typescript.transform_do_while_statement(stmt);
self.x0_typescript.transform_do_while_statement(stmt, ctx);
}

fn enter_for_statement(&mut self, stmt: &mut ForStatement<'a>, _ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_for_statement(stmt);
fn enter_for_statement(&mut self, stmt: &mut ForStatement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_for_statement(stmt, ctx);
}

fn enter_ts_export_assignment(
Expand Down
107 changes: 64 additions & 43 deletions crates/oxc_transformer/src/typescript/annotations.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#![allow(clippy::unused_self)]

use std::rc::Rc;
use std::{cell::Cell, rc::Rc};

use oxc_allocator::Vec as ArenaVec;
use oxc_ast::ast::*;
use oxc_span::{Atom, GetSpan, Span, SPAN};
use oxc_syntax::{operator::AssignmentOperator, reference::ReferenceFlag, symbol::SymbolId};
use oxc_syntax::{
operator::AssignmentOperator, reference::ReferenceFlag, scope::ScopeFlags, symbol::SymbolId,
};
use oxc_traverse::TraverseCtx;
use rustc_hash::FxHashSet;

Expand Down Expand Up @@ -370,16 +372,23 @@ impl<'a> TypeScriptAnnotations<'a> {
/// // to
/// if (true) { super() } else { super() }
/// ```
pub fn transform_if_statement(&mut self, stmt: &mut IfStatement<'a>) {
pub fn transform_if_statement(
&mut self,
stmt: &mut IfStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if !self.assignments.is_empty() {
if let Statement::ExpressionStatement(expr) = &stmt.consequent {
if expr.expression.is_super_call_expression() {
// TODO: Need to create a scope for this block
stmt.consequent = self.ctx.ast.block_statement(self.ctx.ast.block(
expr.span,
self.ctx.ast.new_vec_single(self.ctx.ast.copy(&stmt.consequent)),
));
let consequent_span = match &stmt.consequent {
Statement::ExpressionStatement(expr)
if expr.expression.is_super_call_expression() =>
{
Some(expr.span)
}
_ => None,
};
if let Some(span) = consequent_span {
let consequent = ctx.ast.move_statement(&mut stmt.consequent);
stmt.consequent = Self::create_block_with_statement(consequent, span, ctx);
}

let alternate_span = match &stmt.alternate {
Expand All @@ -392,52 +401,64 @@ impl<'a> TypeScriptAnnotations<'a> {
};
if let Some(span) = alternate_span {
let alternate = stmt.alternate.take().unwrap();
// TODO: Need to create a scope for this block
stmt.alternate = Some(self.ctx.ast.block_statement(
self.ctx.ast.block(span, self.ctx.ast.new_vec_single(alternate)),
));
stmt.alternate = Some(Self::create_block_with_statement(alternate, span, ctx));
}
}

if stmt.consequent.is_typescript_syntax() {
// TODO: Need to create a scope for this block
stmt.consequent = self.ctx.ast.block_statement(
self.ctx.ast.block(stmt.consequent.span(), self.ctx.ast.new_vec()),
);
}
Self::replace_with_empty_block_if_ts(&mut stmt.consequent, ctx);

if stmt.alternate.as_ref().is_some_and(Statement::is_typescript_syntax) {
stmt.alternate = None;
}
}

pub fn transform_for_statement(&mut self, stmt: &mut ForStatement<'a>) {
if stmt.body.is_typescript_syntax() {
// TODO: Need to create a scope for this block
stmt.body = self
.ctx
.ast
.block_statement(self.ctx.ast.block(stmt.body.span(), self.ctx.ast.new_vec()));
}
fn create_block_with_statement(
stmt: Statement<'a>,
span: Span,
ctx: &mut TraverseCtx<'a>,
) -> Statement<'a> {
let scope_id = ctx.insert_scope_below_statement(&stmt, ScopeFlags::empty());
let block = BlockStatement {
span,
body: ctx.ast.new_vec_single(stmt),
scope_id: Cell::new(Some(scope_id)),
};
Statement::BlockStatement(ctx.ast.alloc(block))
}

pub fn transform_while_statement(&mut self, stmt: &mut WhileStatement<'a>) {
if stmt.body.is_typescript_syntax() {
// TODO: Need to create a scope for this block
stmt.body = self
.ctx
.ast
.block_statement(self.ctx.ast.block(stmt.body.span(), self.ctx.ast.new_vec()));
}
pub fn transform_for_statement(
&mut self,
stmt: &mut ForStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
}

pub fn transform_while_statement(
&mut self,
stmt: &mut WhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
}

pub fn transform_do_while_statement(&mut self, stmt: &mut DoWhileStatement<'a>) {
if stmt.body.is_typescript_syntax() {
// TODO: Need to create a scope for this block
stmt.body = self
.ctx
.ast
.block_statement(self.ctx.ast.block(stmt.body.span(), self.ctx.ast.new_vec()));
pub fn transform_do_while_statement(
&mut self,
stmt: &mut DoWhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
Self::replace_with_empty_block_if_ts(&mut stmt.body, ctx);
}

fn replace_with_empty_block_if_ts(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
if stmt.is_typescript_syntax() {
let scope_id = ctx.create_scope_child_of_current(ScopeFlags::empty());
let block = BlockStatement {
span: stmt.span(),
body: ctx.ast.new_vec(),
scope_id: Cell::new(Some(scope_id)),
};
*stmt = Statement::BlockStatement(ctx.ast.alloc(block));
}
}

Expand Down
32 changes: 24 additions & 8 deletions crates/oxc_transformer/src/typescript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,36 @@ impl<'a> TypeScript<'a> {
self.r#enum.transform_statement(stmt, ctx);
}

pub fn transform_if_statement(&mut self, stmt: &mut IfStatement<'a>) {
self.annotations.transform_if_statement(stmt);
pub fn transform_if_statement(
&mut self,
stmt: &mut IfStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.transform_if_statement(stmt, ctx);
}

pub fn transform_while_statement(&mut self, stmt: &mut WhileStatement<'a>) {
self.annotations.transform_while_statement(stmt);
pub fn transform_while_statement(
&mut self,
stmt: &mut WhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.transform_while_statement(stmt, ctx);
}

pub fn transform_do_while_statement(&mut self, stmt: &mut DoWhileStatement<'a>) {
self.annotations.transform_do_while_statement(stmt);
pub fn transform_do_while_statement(
&mut self,
stmt: &mut DoWhileStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.transform_do_while_statement(stmt, ctx);
}

pub fn transform_for_statement(&mut self, stmt: &mut ForStatement<'a>) {
self.annotations.transform_for_statement(stmt);
pub fn transform_for_statement(
&mut self,
stmt: &mut ForStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.transform_for_statement(stmt, ctx);
}

pub fn transform_tagged_template_expression(
Expand Down
52 changes: 51 additions & 1 deletion crates/oxc_traverse/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use oxc_allocator::{Allocator, Box};
use oxc_ast::AstBuilder;
use oxc_ast::{
ast::{Expression, Statement},
AstBuilder,
};
use oxc_semantic::{ScopeTree, SymbolTable};
use oxc_span::CompactStr;
use oxc_syntax::{
Expand Down Expand Up @@ -209,6 +212,14 @@ impl<'a> TraverseCtx<'a> {
self.scoping.current_scope_id()
}

/// Get current scope flags.
///
/// Shortcut for `ctx.scoping.current_scope_flags`.
#[inline]
pub fn current_scope_flags(&self) -> ScopeFlags {
self.scoping.current_scope_flags()
}

/// Get scopes tree.
///
/// Shortcut for `ctx.scoping.scopes`.
Expand Down Expand Up @@ -275,6 +286,45 @@ impl<'a> TraverseCtx<'a> {
self.scoping.find_scope_by_flags(finder)
}

/// Create new scope as child of current scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
///
/// This is a shortcut for `ctx.scoping.create_scope_child_of_current`.
pub fn create_scope_child_of_current(&mut self, flags: ScopeFlags) -> ScopeId {
self.scoping.create_scope_child_of_current(flags)
}

/// Insert a scope into scope tree below a statement.
///
/// Statement must be in current scope.
/// New scope is created as child of current scope.
/// All child scopes of the statement are reassigned to be children of the new scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
///
/// This is a shortcut for `ctx.scoping.insert_scope_below_statement`.
pub fn insert_scope_below_statement(&mut self, stmt: &Statement, flags: ScopeFlags) -> ScopeId {
self.scoping.insert_scope_below_statement(stmt, flags)
}

/// Insert a scope into scope tree below an expression.
///
/// Expression must be in current scope.
/// New scope is created as child of current scope.
/// All child scopes of the expression are reassigned to be children of the new scope.
///
/// `flags` provided are amended to inherit from parent scope's flags.
///
/// This is a shortcut for `ctx.scoping.insert_scope_below_expression`.
pub fn insert_scope_below_expression(
&mut self,
expr: &Expression,
flags: ScopeFlags,
) -> ScopeId {
self.scoping.insert_scope_below_expression(expr, flags)
}

/// Generate UID.
///
/// This is a shortcut for `ctx.scoping.generate_uid`.
Expand Down
Loading

0 comments on commit 17ad8f7

Please sign in to comment.