refactor(transformer): shared VarDeclarations (#6170)

First step towards #5049.

Various transforms need to add a `var` statement at top of enclosing statement block.

e.g.:

```js
// Input
a ??= b;
```

```js
// Output
var _a;
(_a = a) !== null && _a !== void 0 ? _a : (a = b);
```

Each of these transforms previously maintained it's own stack and added `var` statements individually.

Share this functionality in a "common" utility transform which maintains a single stack to serve them all.
This commit is contained in:
overlookmotel 2024-09-30 03:53:00 +00:00
parent b92fe8401a
commit 21b08ba141
11 changed files with 228 additions and 263 deletions

View file

@ -0,0 +1,37 @@
//! Utility transforms which are in common between other transforms.
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
use crate::TransformCtx;
mod var_declarations;
use var_declarations::VarDeclarations;
pub use var_declarations::VarDeclarationsStore;
pub struct Common<'a, 'ctx> {
var_declarations: VarDeclarations<'a, 'ctx>,
}
impl<'a, 'ctx> Common<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { var_declarations: VarDeclarations::new(ctx) }
}
}
impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.var_declarations.exit_program(program, ctx);
}
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.var_declarations.enter_statements(stmts, ctx);
}
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.var_declarations.exit_statements(stmts, ctx);
}
}

View file

@ -0,0 +1,111 @@
//! Utility transform to add `var` declarations to top of statement blocks.
//!
//! `VarDeclarationsStore` contains a stack of `Vec<VariableDeclarator>`s.
//! It is stored on `TransformCtx`.
//!
//! `VarDeclarations` transform pushes an empty entry onto this stack when entering a statement block,
//! and when exiting the block, writes a `var` statement to top of block containing the declarators.
//!
//! Other transforms can add declarators to the store by calling methods of `VarDeclarationsStore`:
//!
//! ```rs
//! self.ctx.var_declarations.insert_declarator(name, symbol_id, None, ctx);
//! ```
use std::cell::RefCell;
use oxc_allocator::Vec;
use oxc_ast::{ast::*, NONE};
use oxc_span::SPAN;
use oxc_syntax::symbol::SymbolId;
use oxc_traverse::{Traverse, TraverseCtx};
use crate::{context::TransformCtx, helpers::stack::SparseStack};
/// Transform that maintains the stack of `Vec<VariableDeclarator>`s, and adds a `var` statement
/// to top of a statement block if another transform has requested that.
///
/// Must run after all other transforms.
pub struct VarDeclarations<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}
impl<'a, 'ctx> VarDeclarations<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { ctx }
}
}
impl<'a, 'ctx> Traverse<'a> for VarDeclarations<'a, 'ctx> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
let declarators = self.ctx.var_declarations.declarators.borrow();
debug_assert!(declarators.len() == 1);
debug_assert!(declarators.last().is_none());
}
fn enter_statements(
&mut self,
_stmts: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
let mut declarators = self.ctx.var_declarations.declarators.borrow_mut();
declarators.push(None);
}
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
let mut declarators = self.ctx.var_declarations.declarators.borrow_mut();
if let Some(declarators) = declarators.pop() {
debug_assert!(!declarators.is_empty());
let variable = ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarators,
false,
);
stmts.insert(0, Statement::VariableDeclaration(variable));
}
}
}
/// Store for `VariableDeclarator`s to be added to enclosing statement block.
pub struct VarDeclarationsStore<'a> {
declarators: RefCell<SparseStack<Vec<'a, VariableDeclarator<'a>>>>,
}
impl<'a> VarDeclarationsStore<'a> {
pub fn new() -> Self {
Self { declarators: RefCell::new(SparseStack::new()) }
}
}
impl<'a> VarDeclarationsStore<'a> {
/// Add a `VariableDeclarator` to be inserted at top of current enclosing statement block,
/// given `name` and `symbol_id`.
pub fn insert_declarator(
&self,
name: Atom<'a>,
symbol_id: SymbolId,
init: Option<Expression<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
let ident = BindingIdentifier::new_with_symbol_id(SPAN, name, symbol_id);
let ident = ctx.ast.binding_pattern_kind_from_binding_identifier(ident);
let ident = ctx.ast.binding_pattern(ident, NONE, false);
self.insert_declarator_binding_pattern(ident, init, ctx);
}
/// Add a `VariableDeclarator` to be inserted at top of current enclosing statement block,
/// given a `BindingPattern`.
pub fn insert_declarator_binding_pattern(
&self,
ident: BindingPattern<'a>,
init: Option<Expression<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
let declarator =
ctx.ast.variable_declarator(SPAN, VariableDeclarationKind::Var, ident, init, false);
let mut declarators = self.declarators.borrow_mut();
declarators.last_mut_or_init(|| ctx.ast.vec()).push(declarator);
}
}

View file

@ -9,7 +9,9 @@ use oxc_ast::{AstBuilder, Trivias};
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::SourceType;
use crate::{helpers::module_imports::ModuleImports, TransformOptions};
use crate::{
common::VarDeclarationsStore, helpers::module_imports::ModuleImports, TransformOptions,
};
pub struct TransformCtx<'a> {
errors: RefCell<Vec<OxcDiagnostic>>,
@ -31,6 +33,8 @@ pub struct TransformCtx<'a> {
// Helpers
/// Manage import statement globally
pub module_imports: ModuleImports<'a>,
/// Manage inserting `var` statements globally
pub var_declarations: VarDeclarationsStore<'a>,
}
impl<'a> TransformCtx<'a> {
@ -59,6 +63,7 @@ impl<'a> TransformCtx<'a> {
source_text,
trivias,
module_imports: ModuleImports::new(allocator),
var_declarations: VarDeclarationsStore::new(),
}
}

View file

@ -37,7 +37,7 @@ use oxc_span::SPAN;
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
use oxc_traverse::{Traverse, TraverseCtx};
use crate::helpers::stack::SparseStack;
use crate::TransformCtx;
/// ES2016: Exponentiation Operator
///
@ -45,8 +45,8 @@ use crate::helpers::stack::SparseStack;
/// * <https://babel.dev/docs/babel-plugin-transform-exponentiation-operator>
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-exponentiation-operator>
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
pub struct ExponentiationOperator<'a> {
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
pub struct ExponentiationOperator<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}
#[derive(Debug)]
@ -55,44 +55,13 @@ struct Exploded<'a> {
uid: Expression<'a>,
}
impl<'a> ExponentiationOperator<'a> {
pub fn new() -> Self {
Self { var_declarations: SparseStack::new() }
impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { ctx }
}
}
impl<'a> Traverse<'a> for ExponentiationOperator<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
debug_assert!(self.var_declarations.len() == 1);
debug_assert!(self.var_declarations.last().is_none());
}
fn enter_statements(
&mut self,
_statements: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
self.var_declarations.push(None);
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if let Some(declarations) = self.var_declarations.pop() {
debug_assert!(!declarations.is_empty());
let variable = ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarations,
false,
);
statements.insert(0, Statement::VariableDeclaration(variable));
}
}
impl<'a, 'ctx> Traverse<'a> for ExponentiationOperator<'a, 'ctx> {
// NOTE: Bail bigint arguments to `Math.pow`, which are runtime errors.
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
match expr {
@ -139,7 +108,7 @@ impl<'a> Traverse<'a> for ExponentiationOperator<'a> {
}
}
impl<'a> ExponentiationOperator<'a> {
impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
match expr {
Expression::Identifier(ident) => ctx.ast.expression_from_identifier_reference(
@ -325,17 +294,8 @@ impl<'a> ExponentiationOperator<'a> {
ctx.generate_uid_in_current_scope(name, SymbolFlags::FunctionScopedVariable);
let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id));
{
// var _name;
let binding_identifier =
BindingIdentifier::new_with_symbol_id(SPAN, symbol_name.clone(), symbol_id);
let kind = VariableDeclarationKind::Var;
let id = ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier);
let id = ctx.ast.binding_pattern(id, NONE, false);
self.var_declarations
.last_mut_or_init(|| ctx.ast.vec())
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
}
// var _name;
self.ctx.var_declarations.insert_declarator(symbol_name.clone(), symbol_id, None, ctx);
let ident =
ctx.create_reference_id(SPAN, symbol_name, Some(symbol_id), ReferenceFlags::Read);

View file

@ -3,51 +3,25 @@ mod options;
pub use exponentiation_operator::ExponentiationOperator;
pub use options::ES2016Options;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
pub struct ES2016<'a> {
use crate::TransformCtx;
pub struct ES2016<'a, 'ctx> {
options: ES2016Options,
// Plugins
exponentiation_operator: ExponentiationOperator<'a>,
exponentiation_operator: ExponentiationOperator<'a, 'ctx>,
}
impl<'a> ES2016<'a> {
pub fn new(options: ES2016Options) -> Self {
Self { exponentiation_operator: ExponentiationOperator::new(), options }
impl<'a, 'ctx> ES2016<'a, 'ctx> {
pub fn new(options: ES2016Options, ctx: &'ctx TransformCtx<'a>) -> Self {
Self { exponentiation_operator: ExponentiationOperator::new(ctx), options }
}
}
impl<'a> Traverse<'a> for ES2016<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.exponentiation_operator {
self.exponentiation_operator.exit_program(program, ctx);
}
}
fn enter_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.exponentiation_operator {
self.exponentiation_operator.enter_statements(statements, ctx);
}
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.exponentiation_operator {
self.exponentiation_operator.exit_statements(statements, ctx);
}
}
impl<'a, 'ctx> Traverse<'a> for ES2016<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.exponentiation_operator {
self.exponentiation_operator.enter_expression(expr, ctx);

View file

@ -3,51 +3,25 @@ mod options;
pub use nullish_coalescing_operator::NullishCoalescingOperator;
pub use options::ES2020Options;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
pub struct ES2020<'a> {
use crate::TransformCtx;
pub struct ES2020<'a, 'ctx> {
options: ES2020Options,
// Plugins
nullish_coalescing_operator: NullishCoalescingOperator<'a>,
nullish_coalescing_operator: NullishCoalescingOperator<'a, 'ctx>,
}
impl<'a> ES2020<'a> {
pub fn new(options: ES2020Options) -> Self {
Self { nullish_coalescing_operator: NullishCoalescingOperator::new(), options }
impl<'a, 'ctx> ES2020<'a, 'ctx> {
pub fn new(options: ES2020Options, ctx: &'ctx TransformCtx<'a>) -> Self {
Self { nullish_coalescing_operator: NullishCoalescingOperator::new(ctx), options }
}
}
impl<'a> Traverse<'a> for ES2020<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.nullish_coalescing_operator {
self.nullish_coalescing_operator.exit_program(program, ctx);
}
}
fn enter_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.nullish_coalescing_operator {
self.nullish_coalescing_operator.enter_statements(statements, ctx);
}
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.nullish_coalescing_operator {
self.nullish_coalescing_operator.exit_statements(statements, ctx);
}
}
impl<'a, 'ctx> Traverse<'a> for ES2020<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.nullish_coalescing_operator {
self.nullish_coalescing_operator.enter_expression(expr, ctx);

View file

@ -28,57 +28,26 @@
//! * Babel plugin implementation: <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-nullish-coalescing-operator>
//! * Nullish coalescing TC39 proposal: <https://github.com/tc39-transfer/proposal-nullish-coalescing>
use oxc_allocator::{CloneIn, Vec};
use oxc_allocator::CloneIn;
use oxc_ast::{ast::*, NONE};
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
use oxc_span::SPAN;
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
use crate::helpers::stack::SparseStack;
use crate::TransformCtx;
pub struct NullishCoalescingOperator<'a> {
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
pub struct NullishCoalescingOperator<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}
impl<'a> NullishCoalescingOperator<'a> {
pub fn new() -> Self {
Self { var_declarations: SparseStack::new() }
impl<'a, 'ctx> NullishCoalescingOperator<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { ctx }
}
}
impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
debug_assert!(self.var_declarations.len() == 1);
debug_assert!(self.var_declarations.last().is_none());
}
fn enter_statements(
&mut self,
_stmts: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
self.var_declarations.push(None);
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if let Some(declarations) = self.var_declarations.pop() {
debug_assert!(!declarations.is_empty());
let variable = ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarations,
false,
);
statements.insert(0, Statement::VariableDeclaration(variable));
}
}
impl<'a, 'ctx> Traverse<'a> for NullishCoalescingOperator<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
// left ?? right
if !matches!(expr, Expression::LogicalExpression(logical_expr) if logical_expr.operator == LogicalOperator::Coalesce)
@ -156,17 +125,14 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
// `(x) => x;` -> `((x) => x)();`
new_expr = ctx.ast.expression_call(SPAN, arrow_function, NONE, ctx.ast.vec(), false);
} else {
let kind = VariableDeclarationKind::Var;
self.var_declarations
.last_mut_or_init(|| ctx.ast.vec())
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
self.ctx.var_declarations.insert_declarator_binding_pattern(id, None, ctx);
}
*expr = new_expr;
}
}
impl<'a> NullishCoalescingOperator<'a> {
impl<'a, 'ctx> NullishCoalescingOperator<'a, 'ctx> {
fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
match expr {
Expression::Identifier(ident) => ctx.ast.expression_from_identifier_reference(

View file

@ -53,57 +53,26 @@
//! * Babel plugin implementation: <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-logical-assignment-operators>
//! * Logical Assignment TC39 proposal: <https://github.com/tc39/proposal-logical-assignment>
use oxc_allocator::{CloneIn, Vec};
use oxc_ast::{ast::*, NONE};
use oxc_allocator::CloneIn;
use oxc_ast::ast::*;
use oxc_semantic::{ReferenceFlags, SymbolFlags};
use oxc_span::SPAN;
use oxc_syntax::operator::{AssignmentOperator, LogicalOperator};
use oxc_traverse::{Traverse, TraverseCtx};
use crate::helpers::stack::SparseStack;
use crate::TransformCtx;
pub struct LogicalAssignmentOperators<'a> {
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
pub struct LogicalAssignmentOperators<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}
impl<'a> LogicalAssignmentOperators<'a> {
pub fn new() -> Self {
Self { var_declarations: SparseStack::new() }
impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { ctx }
}
}
impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
debug_assert!(self.var_declarations.len() == 1);
debug_assert!(self.var_declarations.last().is_none());
}
fn enter_statements(
&mut self,
_stmts: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
self.var_declarations.push(None);
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if let Some(declarations) = self.var_declarations.pop() {
debug_assert!(!declarations.is_empty());
let variable = ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
declarations,
false,
);
statements.insert(0, Statement::VariableDeclaration(variable));
}
}
impl<'a, 'ctx> Traverse<'a> for LogicalAssignmentOperators<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
let Expression::AssignmentExpression(assignment_expr) = expr else { return };
@ -154,7 +123,7 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
}
}
impl<'a> LogicalAssignmentOperators<'a> {
impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> {
fn convert_identifier(
ident: &IdentifierReference<'a>,
ctx: &mut TraverseCtx<'a>,
@ -390,15 +359,7 @@ impl<'a> LogicalAssignmentOperators<'a> {
let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id));
// var _name;
let binding_identifier =
BindingIdentifier::new_with_symbol_id(SPAN, symbol_name.clone(), symbol_id);
let kind = VariableDeclarationKind::Var;
let id = ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier);
let id = ctx.ast.binding_pattern(id, NONE, false);
self.var_declarations
.last_mut_or_init(|| ctx.ast.vec())
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
self.ctx.var_declarations.insert_declarator(symbol_name.clone(), symbol_id, None, ctx);
// _name = name
Some(ctx.create_reference_id(SPAN, symbol_name, Some(symbol_id), ReferenceFlags::Write))

View file

@ -3,51 +3,25 @@ mod options;
pub use logical_assignment_operators::LogicalAssignmentOperators;
pub use options::ES2021Options;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
pub struct ES2021<'a> {
use crate::TransformCtx;
pub struct ES2021<'a, 'ctx> {
options: ES2021Options,
// Plugins
logical_assignment_operators: LogicalAssignmentOperators<'a>,
logical_assignment_operators: LogicalAssignmentOperators<'a, 'ctx>,
}
impl<'a> ES2021<'a> {
pub fn new(options: ES2021Options) -> Self {
Self { logical_assignment_operators: LogicalAssignmentOperators::new(), options }
impl<'a, 'ctx> ES2021<'a, 'ctx> {
pub fn new(options: ES2021Options, ctx: &'ctx TransformCtx<'a>) -> Self {
Self { logical_assignment_operators: LogicalAssignmentOperators::new(ctx), options }
}
}
impl<'a> Traverse<'a> for ES2021<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.logical_assignment_operators {
self.logical_assignment_operators.exit_program(program, ctx);
}
}
fn enter_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.logical_assignment_operators {
self.logical_assignment_operators.enter_statements(statements, ctx);
}
}
fn exit_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.logical_assignment_operators {
self.logical_assignment_operators.exit_statements(statements, ctx);
}
}
impl<'a, 'ctx> Traverse<'a> for ES2021<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.logical_assignment_operators {
self.logical_assignment_operators.enter_expression(expr, ctx);

View file

@ -9,6 +9,7 @@
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
// Core
mod common;
mod compiler_assumptions;
mod context;
mod options;
@ -32,6 +33,7 @@ mod helpers {
use std::path::Path;
use common::Common;
use es2016::ES2016;
use es2018::ES2018;
use es2019::ES2019;
@ -91,13 +93,14 @@ impl<'a> Transformer<'a> {
let mut transformer = TransformerImpl {
x0_typescript: TypeScript::new(self.options.typescript, &self.ctx),
x1_react: React::new(self.options.react, &self.ctx),
x2_es2021: ES2021::new(self.options.es2021),
x2_es2020: ES2020::new(self.options.es2020),
x2_es2021: ES2021::new(self.options.es2021, &self.ctx),
x2_es2020: ES2020::new(self.options.es2020, &self.ctx),
x2_es2019: ES2019::new(self.options.es2019),
x2_es2018: ES2018::new(self.options.es2018),
x2_es2016: ES2016::new(self.options.es2016),
x2_es2016: ES2016::new(self.options.es2016, &self.ctx),
x3_es2015: ES2015::new(self.options.es2015),
x4_regexp: RegExp::new(self.options.regexp, &self.ctx),
common: Common::new(&self.ctx),
};
let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes);
@ -109,13 +112,14 @@ struct TransformerImpl<'a, 'ctx> {
// NOTE: all callbacks must run in order.
x0_typescript: TypeScript<'a, 'ctx>,
x1_react: React<'a, 'ctx>,
x2_es2021: ES2021<'a>,
x2_es2020: ES2020<'a>,
x2_es2021: ES2021<'a, 'ctx>,
x2_es2020: ES2020<'a, 'ctx>,
x2_es2019: ES2019,
x2_es2018: ES2018,
x2_es2016: ES2016<'a>,
x2_es2016: ES2016<'a, 'ctx>,
x3_es2015: ES2015<'a>,
x4_regexp: RegExp<'a, 'ctx>,
common: Common<'a, 'ctx>,
}
impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
@ -127,10 +131,8 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.exit_program(program, ctx);
self.x0_typescript.exit_program(program, ctx);
self.x2_es2021.exit_program(program, ctx);
self.x2_es2020.exit_program(program, ctx);
self.x2_es2016.exit_program(program, ctx);
self.x3_es2015.exit_program(program, ctx);
self.common.exit_program(program, ctx);
}
// ALPHASORT
@ -300,11 +302,9 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
}
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.common.enter_statements(stmts, ctx);
self.x0_typescript.enter_statements(stmts, ctx);
self.x1_react.enter_statements(stmts, ctx);
self.x2_es2021.enter_statements(stmts, ctx);
self.x2_es2020.enter_statements(stmts, ctx);
self.x2_es2016.enter_statements(stmts, ctx);
}
fn exit_arrow_function_expression(
@ -334,9 +334,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.exit_statements(stmts, ctx);
self.x1_react.exit_statements(stmts, ctx);
self.x2_es2021.exit_statements(stmts, ctx);
self.x2_es2020.exit_statements(stmts, ctx);
self.x2_es2016.exit_statements(stmts, ctx);
self.common.exit_statements(stmts, ctx);
}
fn enter_tagged_template_expression(

View file

@ -1456,7 +1456,12 @@ after transform: SymbolId(21): [ReferenceId(132), ReferenceId(133), ReferenceId(
rebuilt : SymbolId(14): [ReferenceId(123), ReferenceId(127)]
* logical-assignment/null-coalescing/input.js
x Output mismatch
Symbol reference IDs mismatch:
after transform: SymbolId(17): [ReferenceId(86), ReferenceId(87), ReferenceId(90)]
rebuilt : SymbolId(10): [ReferenceId(73), ReferenceId(79)]
Symbol reference IDs mismatch:
after transform: SymbolId(20): [ReferenceId(94), ReferenceId(95), ReferenceId(98)]
rebuilt : SymbolId(13): [ReferenceId(88), ReferenceId(94)]
* logical-assignment/null-coalescing-without-other/input.js
Symbol reference IDs mismatch: