feat(traverse): add methods for creating Expression::Identifiers (#7301)

It's a common pattern in transformer to call `ctx.create_ident_reference()` and then convert to an `Expression` with `Expression::Identifier(ctx.ast.alloc(ident))`.

Add methods to do this in a single method call.
This commit is contained in:
overlookmotel 2024-11-16 05:36:35 +00:00
parent ba0b2ff856
commit faf8dde4ff
9 changed files with 66 additions and 56 deletions

View file

@ -277,13 +277,8 @@ impl<'a> HelperLoaderStore<'a> {
static HELPER_VAR: &str = "babelHelpers"; static HELPER_VAR: &str = "babelHelpers";
let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), HELPER_VAR); let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), HELPER_VAR);
let ident = ctx.create_ident_reference( let object =
SPAN, ctx.create_ident_expr(SPAN, Atom::from(HELPER_VAR), symbol_id, ReferenceFlags::Read);
Atom::from(HELPER_VAR),
symbol_id,
ReferenceFlags::Read,
);
let object = Expression::Identifier(ctx.alloc(ident));
let property = ctx.ast.identifier_name(SPAN, Atom::from(helper.name())); let property = ctx.ast.identifier_name(SPAN, Atom::from(helper.name()));
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)) Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false))
} }

View file

@ -226,14 +226,12 @@ impl<'a> ModuleImportsStore<'a> {
require_symbol_id: Option<SymbolId>, require_symbol_id: Option<SymbolId>,
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Statement<'a> { ) -> Statement<'a> {
let var_kind = VariableDeclarationKind::Var; let callee = ctx.create_ident_expr(
let ident = ctx.create_ident_reference(
SPAN, SPAN,
Atom::from("require"), Atom::from("require"),
require_symbol_id, require_symbol_id,
ReferenceFlags::read(), ReferenceFlags::read(),
); );
let callee = Expression::Identifier(ctx.alloc(ident));
let args = { let args = {
let arg = Argument::from(ctx.ast.expression_string_literal(SPAN, source)); let arg = Argument::from(ctx.ast.expression_string_literal(SPAN, source));
@ -241,6 +239,7 @@ impl<'a> ModuleImportsStore<'a> {
}; };
let Some(Import::Default(local)) = names.into_iter().next() else { unreachable!() }; let Some(Import::Default(local)) = names.into_iter().next() else { unreachable!() };
let id = local.create_binding_pattern(ctx); let id = local.create_binding_pattern(ctx);
let var_kind = VariableDeclarationKind::Var;
let decl = { let decl = {
let init = ctx.ast.expression_call(SPAN, callee, NONE, args, false); let init = ctx.ast.expression_call(SPAN, callee, NONE, args, false);
let decl = ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false); let decl = ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false);

View file

@ -164,18 +164,12 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
*reference.flags_mut() = ReferenceFlags::Write; *reference.flags_mut() = ReferenceFlags::Write;
} }
Expression::Identifier(ctx.ast.alloc(ctx.create_bound_ident_reference( ctx.create_bound_ident_expr(SPAN, ident.name.clone(), symbol_id, ReferenceFlags::Read)
SPAN,
ident.name.clone(),
symbol_id,
ReferenceFlags::Read,
)))
} else { } else {
// Unbound reference. Could possibly trigger a getter so we need to only evaluate it once. // Unbound reference. Could possibly trigger a getter so we need to only evaluate it once.
// Assign to a temp var. // Assign to a temp var.
let reference = Expression::Identifier(ctx.ast.alloc( let reference =
ctx.create_unbound_ident_reference(SPAN, ident.name.clone(), ReferenceFlags::Read), ctx.create_unbound_ident_expr(SPAN, ident.name.clone(), ReferenceFlags::Read);
));
let binding = self.create_temp_var(reference, &mut temp_var_inits, ctx); let binding = self.create_temp_var(reference, &mut temp_var_inits, ctx);
binding.create_read_expression(ctx) binding.create_read_expression(ctx)
}; };
@ -495,14 +489,12 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
if let Some(symbol_id) = symbol_id { if let Some(symbol_id) = symbol_id {
// This variable is declared in scope so evaluating it multiple times can't trigger a getter. // This variable is declared in scope so evaluating it multiple times can't trigger a getter.
// No need for a temp var. // No need for a temp var.
return Expression::Identifier(ctx.ast.alloc( return ctx.create_bound_ident_expr(
ctx.create_bound_ident_reference(
SPAN, SPAN,
ident.name.clone(), ident.name.clone(),
symbol_id, symbol_id,
ReferenceFlags::Read, ReferenceFlags::Read,
), );
));
} }
// Unbound reference. Could possibly trigger a getter so we need to only evaluate it once. // Unbound reference. Could possibly trigger a getter so we need to only evaluate it once.
// Assign to a temp var. // Assign to a temp var.
@ -548,13 +540,8 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> { ) -> Expression<'a> {
let math_symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "Math"); let math_symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "Math");
let ident_math = ctx.create_ident_reference( let object =
SPAN, ctx.create_ident_expr(SPAN, Atom::from("Math"), math_symbol_id, ReferenceFlags::Read);
Atom::from("Math"),
math_symbol_id,
ReferenceFlags::Read,
);
let object = Expression::Identifier(ctx.alloc(ident_math));
let property = ctx.ast.identifier_name(SPAN, "pow"); let property = ctx.ast.identifier_name(SPAN, "pow");
let callee = let callee =
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)); Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false));

View file

@ -310,7 +310,7 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
let id = caller_function.id.as_ref().unwrap(); let id = caller_function.id.as_ref().unwrap();
// If the function has an id, then we need to return the id. // If the function has an id, then we need to return the id.
// `function foo() { ... }` -> `function foo() {} return foo;` // `function foo() { ... }` -> `function foo() {} return foo;`
let reference = ctx.create_bound_ident_reference( let reference = ctx.create_bound_ident_expr(
SPAN, SPAN,
id.name.clone(), id.name.clone(),
id.symbol_id(), id.symbol_id(),
@ -318,8 +318,7 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
); );
let statement = Statement::FunctionDeclaration(caller_function); let statement = Statement::FunctionDeclaration(caller_function);
statements.push(statement); statements.push(statement);
let argument = Some(Expression::Identifier(ctx.alloc(reference))); statements.push(ctx.ast.statement_return(SPAN, Some(reference)));
statements.push(ctx.ast.statement_return(SPAN, argument));
} else { } else {
// If the function doesn't have an id, then we need to return the function itself. // If the function doesn't have an id, then we need to return the function itself.
// `function() { ... }` -> `return function() { ... };` // `function() { ... }` -> `return function() { ... };`
@ -597,13 +596,12 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Statement<'a> { ) -> Statement<'a> {
let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "arguments"); let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "arguments");
let arguments_ident = ctx.create_ident_reference( let arguments_ident = Argument::from(ctx.create_ident_expr(
SPAN, SPAN,
Atom::from("arguments"), Atom::from("arguments"),
symbol_id, symbol_id,
ReferenceFlags::Read, ReferenceFlags::Read,
); ));
let arguments_ident = Argument::Identifier(ctx.alloc(arguments_ident));
// (this, arguments) // (this, arguments)
let mut arguments = ctx.ast.vec_with_capacity(2); let mut arguments = ctx.ast.vec_with_capacity(2);

View file

@ -141,9 +141,8 @@ impl<'a, 'ctx> ObjectRestSpread<'a, 'ctx> {
} }
fn object_assign(symbol_id: Option<SymbolId>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { fn object_assign(symbol_id: Option<SymbolId>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
let ident = let object =
ctx.create_ident_reference(SPAN, Atom::from("Object"), symbol_id, ReferenceFlags::Read); ctx.create_ident_expr(SPAN, Atom::from("Object"), symbol_id, ReferenceFlags::Read);
let object = Expression::Identifier(ctx.alloc(ident));
let property = ctx.ast.identifier_name(SPAN, Atom::from("assign")); let property = ctx.ast.identifier_name(SPAN, Atom::from("assign"));
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)) Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false))
} }

View file

@ -336,13 +336,12 @@ impl<'a, 'ctx> Traverse<'a> for ReactRefresh<'a, 'ctx> {
binding_name.as_str(), binding_name.as_str(),
) )
.map(|symbol_id| { .map(|symbol_id| {
let ident = ctx.create_bound_ident_reference( let mut expr = ctx.create_bound_ident_expr(
SPAN, SPAN,
binding_name, binding_name,
symbol_id, symbol_id,
ReferenceFlags::Read, ReferenceFlags::Read,
); );
let mut expr = Expression::Identifier(ctx.alloc(ident));
if is_member_expression { if is_member_expression {
// binding_name.hook_name // binding_name.hook_name
@ -496,13 +495,12 @@ impl<'a, 'ctx> ReactRefresh<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Statement<'a> { ) -> Statement<'a> {
let left = self.create_registration(id.name.clone(), ReferenceFlags::Write, ctx); let left = self.create_registration(id.name.clone(), ReferenceFlags::Write, ctx);
let right = ctx.create_bound_ident_reference( let right = ctx.create_bound_ident_expr(
SPAN, SPAN,
id.name.clone(), id.name.clone(),
id.symbol_id(), id.symbol_id(),
ReferenceFlags::Read, ReferenceFlags::Read,
); );
let right = Expression::Identifier(ctx.alloc(right));
let expr = ctx.ast.expression_assignment(SPAN, AssignmentOperator::Assign, left, right); let expr = ctx.ast.expression_assignment(SPAN, AssignmentOperator::Assign, left, right);
ctx.ast.statement_expression(SPAN, expr) ctx.ast.statement_expression(SPAN, expr)
} }

View file

@ -180,13 +180,7 @@ impl<'a, 'ctx> Traverse<'a> for RegExp<'a, 'ctx> {
let callee = { let callee = {
let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "RegExp"); let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "RegExp");
let ident = ctx.create_ident_reference( ctx.create_ident_expr(SPAN, Atom::from("RegExp"), symbol_id, ReferenceFlags::read())
SPAN,
Atom::from("RegExp"),
symbol_id,
ReferenceFlags::read(),
);
Expression::Identifier(ctx.alloc(ident))
}; };
let mut arguments = ctx.ast.vec_with_capacity(2); let mut arguments = ctx.ast.vec_with_capacity(2);

View file

@ -122,13 +122,12 @@ impl<'a> TypeScriptEnum<'a> {
} else { } else {
// }(Foo || {}); // }(Foo || {});
let op = LogicalOperator::Or; let op = LogicalOperator::Or;
let left = ctx.create_bound_ident_reference( let left = ctx.create_bound_ident_expr(
decl.id.span, decl.id.span,
enum_name.clone(), enum_name.clone(),
var_symbol_id, var_symbol_id,
ReferenceFlags::Read, ReferenceFlags::Read,
); );
let left = Expression::Identifier(ctx.alloc(left));
let right = ast.expression_object(SPAN, ast.vec(), None); let right = ast.expression_object(SPAN, ast.vec(), None);
let expression = ast.expression_logical(SPAN, left, op, right); let expression = ast.expression_logical(SPAN, left, op, right);
ast.vec1(Argument::from(expression)) ast.vec1(Argument::from(expression))

View file

@ -445,6 +445,18 @@ impl<'a> TraverseCtx<'a> {
self.ast.identifier_reference_with_reference_id(span, name, reference_id) self.ast.identifier_reference_with_reference_id(span, name, reference_id)
} }
/// Create an `Expression::Identifier` bound to a `SymbolId`.
pub fn create_bound_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: SymbolId,
flags: ReferenceFlags,
) -> Expression<'a> {
let ident = self.create_bound_ident_reference(span, name, symbol_id, flags);
Expression::Identifier(self.ast.alloc(ident))
}
/// Create an unbound reference. /// Create an unbound reference.
/// ///
/// This is a shortcut for `ctx.scoping.create_unbound_reference`. /// This is a shortcut for `ctx.scoping.create_unbound_reference`.
@ -468,6 +480,17 @@ impl<'a> TraverseCtx<'a> {
self.ast.identifier_reference_with_reference_id(span, name, reference_id) self.ast.identifier_reference_with_reference_id(span, name, reference_id)
} }
/// Create an unbound `Expression::Identifier`.
pub fn create_unbound_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
flags: ReferenceFlags,
) -> Expression<'a> {
let ident = self.create_unbound_ident_reference(span, name, flags);
Expression::Identifier(self.ast.alloc(ident))
}
/// Create a reference optionally bound to a `SymbolId`. /// Create a reference optionally bound to a `SymbolId`.
/// ///
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference` /// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_reference`
@ -502,6 +525,24 @@ impl<'a> TraverseCtx<'a> {
} }
} }
/// Create an `Expression::Identifier` optionally bound to a `SymbolId`.
///
/// If you know if there's a `SymbolId` or not, prefer `TraverseCtx::create_bound_ident_expr`
/// or `TraverseCtx::create_unbound_ident_expr`.
pub fn create_ident_expr(
&mut self,
span: Span,
name: Atom<'a>,
symbol_id: Option<SymbolId>,
flags: ReferenceFlags,
) -> Expression<'a> {
if let Some(symbol_id) = symbol_id {
self.create_bound_ident_expr(span, name, symbol_id, flags)
} else {
self.create_unbound_ident_expr(span, name, flags)
}
}
/// Create reference in current scope, looking up binding for `name`, /// Create reference in current scope, looking up binding for `name`,
/// ///
/// This is a shortcut for `ctx.scoping.create_reference_in_current_scope`. /// This is a shortcut for `ctx.scoping.create_reference_in_current_scope`.