mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(transformer): remove nested match in logical assignment operator transform (#5897)
No need to convert `AssignmentTarget` to `MemberExpression`, and then match on `MemberExpression::StaticMemberExpression` etc. Can just match on `AssignmentTarget::StaticMemberExpression` etc directly. Diff is large due to big block of code having less indentation, but contains no changes apart from those described above.
This commit is contained in:
parent
d335a6760e
commit
b11d91ce76
1 changed files with 160 additions and 165 deletions
|
|
@ -117,6 +117,7 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
|
|||
|
||||
// TODO: refactor this block, add tests, cover private identifier
|
||||
match &mut assignment_expr.left {
|
||||
// `a &&= c` -> `a && (a = c)`
|
||||
AssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||
let reference = ctx.symbols_mut().get_reference_mut(ident.reference_id().unwrap());
|
||||
*reference.flags_mut() = ReferenceFlags::Read;
|
||||
|
|
@ -128,191 +129,185 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
|
|||
),
|
||||
);
|
||||
}
|
||||
left @ match_member_expression!(AssignmentTarget) => {
|
||||
let member_expr = left.to_member_expression_mut();
|
||||
let op = AssignmentOperator::Assign;
|
||||
// `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)`
|
||||
AssignmentTarget::StaticMemberExpression(static_expr) => {
|
||||
if let Some(ident) = self.maybe_generate_memoised(&static_expr.object, ctx) {
|
||||
// (_o = o).a
|
||||
let right = ctx.ast.move_expression(&mut static_expr.object);
|
||||
let target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
|
||||
),
|
||||
);
|
||||
let object = ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
AssignmentOperator::Assign,
|
||||
target,
|
||||
right,
|
||||
);
|
||||
left_expr = Expression::from(ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
object,
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
false,
|
||||
));
|
||||
|
||||
// `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)`
|
||||
match member_expr {
|
||||
MemberExpression::StaticMemberExpression(static_expr) => {
|
||||
if let Some(ident) = self.maybe_generate_memoised(&static_expr.object, ctx)
|
||||
{
|
||||
// (_o = o).a
|
||||
let right = ctx.ast.move_expression(&mut static_expr.object);
|
||||
let target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
|
||||
),
|
||||
);
|
||||
let object = ctx.ast.expression_assignment(SPAN, op, target, right);
|
||||
left_expr = Expression::from(ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
object,
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
false,
|
||||
));
|
||||
// (_o.a = 1)
|
||||
let assign_expr = ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(ident),
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
false,
|
||||
);
|
||||
assign_target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_member_expression(assign_expr),
|
||||
);
|
||||
} else {
|
||||
// transform `obj.x ||= 1` to `obj.x || (obj.x = 1)`
|
||||
let object = ctx.ast.move_expression(&mut static_expr.object);
|
||||
|
||||
// (_o.a = 1)
|
||||
let assign_expr = ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(ident),
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
false,
|
||||
);
|
||||
assign_target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_member_expression(assign_expr),
|
||||
);
|
||||
} else {
|
||||
// transform `obj.x ||= 1` to `obj.x || (obj.x = 1)`
|
||||
let object = ctx.ast.move_expression(&mut static_expr.object);
|
||||
// TODO: We should use static_expr.clone_in instead of cloning the properties,
|
||||
// but currently clone_in will get rid of IdentifierReference's reference_id
|
||||
let static_expr_cloned = ctx.ast.static_member_expression(
|
||||
static_expr.span,
|
||||
Self::clone_expression(&object, ctx),
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
static_expr.optional,
|
||||
);
|
||||
|
||||
// TODO: We should use static_expr.clone_in instead of cloning the properties,
|
||||
// but currently clone_in will get rid of IdentifierReference's reference_id
|
||||
let static_expr_cloned = ctx.ast.static_member_expression(
|
||||
static_expr.span,
|
||||
Self::clone_expression(&object, ctx),
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
static_expr.optional,
|
||||
);
|
||||
left_expr = ctx.ast.expression_member(
|
||||
ctx.ast.member_expression_from_static(static_expr_cloned),
|
||||
);
|
||||
|
||||
left_expr = ctx.ast.expression_member(
|
||||
ctx.ast.member_expression_from_static(static_expr_cloned),
|
||||
);
|
||||
let member_expr_moved = ctx.ast.member_expression_static(
|
||||
static_expr.span,
|
||||
object,
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
static_expr.optional,
|
||||
);
|
||||
|
||||
let member_expr_moved = ctx.ast.member_expression_static(
|
||||
static_expr.span,
|
||||
object,
|
||||
static_expr.property.clone_in(ctx.ast.allocator),
|
||||
static_expr.optional,
|
||||
);
|
||||
assign_target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_member_expression(member_expr_moved),
|
||||
);
|
||||
};
|
||||
}
|
||||
// `a[b.y] &&= c;` ->
|
||||
// `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);`
|
||||
AssignmentTarget::ComputedMemberExpression(computed_expr) => {
|
||||
if let Some(ident) = self.maybe_generate_memoised(&computed_expr.object, ctx) {
|
||||
// (_o = object)
|
||||
let right = ctx.ast.move_expression(&mut computed_expr.object);
|
||||
let target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
|
||||
),
|
||||
);
|
||||
let object = ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
AssignmentOperator::Assign,
|
||||
target,
|
||||
right,
|
||||
);
|
||||
|
||||
assign_target = AssignmentTarget::from(
|
||||
ctx.ast
|
||||
.simple_assignment_target_member_expression(member_expr_moved),
|
||||
);
|
||||
};
|
||||
let mut expression = ctx.ast.move_expression(&mut computed_expr.expression);
|
||||
|
||||
// _b = expression
|
||||
let property = self.maybe_generate_memoised(&expression, ctx);
|
||||
|
||||
if let Some(ref property) = property {
|
||||
let left = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(property, ReferenceFlags::Write),
|
||||
),
|
||||
);
|
||||
expression = ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
AssignmentOperator::Assign,
|
||||
left,
|
||||
expression,
|
||||
);
|
||||
}
|
||||
// `a[b.y] &&= c;` ->
|
||||
// `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);`
|
||||
MemberExpression::ComputedMemberExpression(computed_expr) => {
|
||||
if let Some(ident) =
|
||||
self.maybe_generate_memoised(&computed_expr.object, ctx)
|
||||
|
||||
// _o[_b]
|
||||
assign_target = AssignmentTarget::from(ctx.ast.member_expression_computed(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(&ident, ReferenceFlags::Read),
|
||||
),
|
||||
property.map_or_else(
|
||||
|| expression.clone_in(ctx.ast.allocator),
|
||||
|ident| ctx.ast.expression_from_identifier_reference(ident),
|
||||
),
|
||||
false,
|
||||
));
|
||||
|
||||
left_expr = Expression::from(
|
||||
ctx.ast.member_expression_computed(SPAN, object, expression, false),
|
||||
);
|
||||
} else {
|
||||
// transform `obj[++key] ||= 1` to `obj[_key = ++key] || (obj[_key] = 1)`
|
||||
let property_ident =
|
||||
self.maybe_generate_memoised(&computed_expr.expression, ctx);
|
||||
|
||||
let object = ctx.ast.move_expression(&mut computed_expr.object);
|
||||
let mut expression = ctx.ast.move_expression(&mut computed_expr.expression);
|
||||
|
||||
// TODO: ideally we should use computed_expr.clone_in instead of cloning the properties,
|
||||
// but currently clone_in will get rid of IdentifierReference's reference_id
|
||||
let new_compute_expr = ctx.ast.computed_member_expression(
|
||||
computed_expr.span,
|
||||
Self::clone_expression(&object, ctx),
|
||||
{
|
||||
// (_o = object)
|
||||
let right = ctx.ast.move_expression(&mut computed_expr.object);
|
||||
let target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(&ident, ReferenceFlags::Write),
|
||||
),
|
||||
);
|
||||
let object = ctx.ast.expression_assignment(SPAN, op, target, right);
|
||||
|
||||
let mut expression =
|
||||
ctx.ast.move_expression(&mut computed_expr.expression);
|
||||
|
||||
// _b = expression
|
||||
let property = self.maybe_generate_memoised(&expression, ctx);
|
||||
|
||||
if let Some(ref property) = property {
|
||||
// _key = ++key
|
||||
if let Some(property_ident) = &property_ident {
|
||||
let left = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(
|
||||
property,
|
||||
property_ident,
|
||||
ReferenceFlags::Write,
|
||||
),
|
||||
),
|
||||
);
|
||||
expression =
|
||||
ctx.ast.expression_assignment(SPAN, op, left, expression);
|
||||
}
|
||||
|
||||
// _o[_b]
|
||||
assign_target =
|
||||
AssignmentTarget::from(ctx.ast.member_expression_computed(
|
||||
ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(
|
||||
&ident,
|
||||
ReferenceFlags::Read,
|
||||
),
|
||||
),
|
||||
property.map_or_else(
|
||||
|| expression.clone_in(ctx.ast.allocator),
|
||||
|ident| ctx.ast.expression_from_identifier_reference(ident),
|
||||
),
|
||||
false,
|
||||
));
|
||||
AssignmentOperator::Assign,
|
||||
left,
|
||||
ctx.ast.move_expression(&mut expression),
|
||||
)
|
||||
} else {
|
||||
Self::clone_expression(&expression, ctx)
|
||||
}
|
||||
},
|
||||
computed_expr.optional,
|
||||
);
|
||||
|
||||
left_expr = Expression::from(
|
||||
ctx.ast.member_expression_computed(SPAN, object, expression, false),
|
||||
);
|
||||
} else {
|
||||
// transform `obj[++key] ||= 1` to `obj[_key = ++key] || (obj[_key] = 1)`
|
||||
let property_ident =
|
||||
self.maybe_generate_memoised(&computed_expr.expression, ctx);
|
||||
left_expr = ctx.ast.expression_member(
|
||||
ctx.ast.member_expression_from_computed(new_compute_expr),
|
||||
);
|
||||
|
||||
let object = ctx.ast.move_expression(&mut computed_expr.object);
|
||||
let mut expression =
|
||||
ctx.ast.move_expression(&mut computed_expr.expression);
|
||||
// obj[_key] = 1
|
||||
let new_compute_expr = ctx.ast.computed_member_expression(
|
||||
computed_expr.span,
|
||||
object,
|
||||
{
|
||||
if let Some(property_ident) = property_ident {
|
||||
ctx.ast.expression_from_identifier_reference(property_ident)
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
},
|
||||
computed_expr.optional,
|
||||
);
|
||||
|
||||
// TODO: ideally we should use computed_expr.clone_in instead of cloning the properties,
|
||||
// but currently clone_in will get rid of IdentifierReference's reference_id
|
||||
let new_compute_expr = ctx.ast.computed_member_expression(
|
||||
computed_expr.span,
|
||||
Self::clone_expression(&object, ctx),
|
||||
{
|
||||
// _key = ++key
|
||||
if let Some(property_ident) = &property_ident {
|
||||
let left = AssignmentTarget::from(
|
||||
ctx.ast
|
||||
.simple_assignment_target_from_identifier_reference(
|
||||
ctx.clone_identifier_reference(
|
||||
property_ident,
|
||||
ReferenceFlags::Write,
|
||||
),
|
||||
),
|
||||
);
|
||||
ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
op,
|
||||
left,
|
||||
ctx.ast.move_expression(&mut expression),
|
||||
)
|
||||
} else {
|
||||
Self::clone_expression(&expression, ctx)
|
||||
}
|
||||
},
|
||||
computed_expr.optional,
|
||||
);
|
||||
|
||||
left_expr = ctx.ast.expression_member(
|
||||
ctx.ast.member_expression_from_computed(new_compute_expr),
|
||||
);
|
||||
|
||||
// obj[_key] = 1
|
||||
let new_compute_expr = ctx.ast.computed_member_expression(
|
||||
computed_expr.span,
|
||||
object,
|
||||
{
|
||||
if let Some(property_ident) = property_ident {
|
||||
ctx.ast.expression_from_identifier_reference(property_ident)
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
},
|
||||
computed_expr.optional,
|
||||
);
|
||||
|
||||
assign_target = AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_member_expression(
|
||||
ctx.ast.member_expression_from_computed(new_compute_expr),
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
||||
MemberExpression::PrivateFieldExpression(_) => return,
|
||||
}
|
||||
assign_target =
|
||||
AssignmentTarget::from(ctx.ast.simple_assignment_target_member_expression(
|
||||
ctx.ast.member_expression_from_computed(new_compute_expr),
|
||||
));
|
||||
};
|
||||
}
|
||||
// TODO
|
||||
#[allow(clippy::match_same_arms)]
|
||||
AssignmentTarget::PrivateFieldExpression(_) => return,
|
||||
// All other are TypeScript syntax.
|
||||
|
||||
// It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is not simple.
|
||||
|
|
|
|||
Loading…
Reference in a new issue