From 70986f51d82efbbd702b70dc6e208f390b8fd293 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Thu, 19 Sep 2024 20:58:25 +0100 Subject: [PATCH] refactor(transformer): remove nested match in logical assignment operator transform --- .../es2021/logical_assignment_operators.rs | 343 +++++++++--------- 1 file changed, 169 insertions(+), 174 deletions(-) diff --git a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs index c3ca3064fd8f19..8ebdded35abcaf 100644 --- a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs +++ b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs @@ -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)` - 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); - - // 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), - ); - - 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 &&= 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, + )); + + // (_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, + ); + + 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, + ); + + 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, + ); + + 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, - )); - - 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), - { - // _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, - } + AssignmentOperator::Assign, + 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), + )); + }; } + // 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.