diff --git a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs index 83373f2abe48e..6699f3b7d0abe 100644 --- a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs +++ b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs @@ -164,13 +164,32 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> { ctx.ast.simple_assignment_target_member_expression(assign_expr), ); } else { - left_expr = Expression::from(MemberExpression::StaticMemberExpression( - static_expr.clone_in(ctx.ast.allocator), - )); + // 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.clone_in(ctx.ast.allocator), - ), + ctx.ast + .simple_assignment_target_member_expression(member_expr_moved), ); }; } @@ -229,31 +248,65 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> { 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 mut expr = computed_expr.clone_in(ctx.ast.allocator); - if let Some(property_ident) = &property_ident { - let left = AssignmentTarget::from( - ctx.ast.simple_assignment_target_from_identifier_reference( - property_ident.clone(), - ), - ); - let right = computed_expr.expression.clone_in(ctx.ast.allocator); - expr.expression = - ctx.ast.expression_assignment(SPAN, op, left, right); - } - left_expr = - Expression::from(MemberExpression::ComputedMemberExpression(expr)); + 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, + ); - let mut expr = computed_expr.clone_in(ctx.ast.allocator); - if let Some(property_ident) = property_ident { - expr.expression = - ctx.ast.expression_from_identifier_reference(property_ident); - } assign_target = AssignmentTarget::from( ctx.ast.simple_assignment_target_member_expression( - MemberExpression::ComputedMemberExpression(expr), + ctx.ast.member_expression_from_computed(new_compute_expr), ), ); }; @@ -279,22 +332,31 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> { } impl<'a> LogicalAssignmentOperators<'a> { + /// Clone an expression + /// + /// If it is an identifier, clone the identifier by [TraverseCtx::clone_identifier_reference], otherwise, use [CloneIn]. + /// + /// TODO: remove this until is resolved. + pub fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { + match expr { + Expression::Identifier(ident) => ctx.ast.expression_from_identifier_reference( + ctx.clone_identifier_reference(ident, ReferenceFlags::Read), + ), + _ => expr.clone_in(ctx.ast.allocator), + } + } + pub fn maybe_generate_memoised( &mut self, expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Option> { - let name = match expr { - Expression::Super(_) | Expression::ThisExpression(_) => return None, - Expression::Identifier(ident) => ident.name.clone(), - Expression::StringLiteral(str) => str.value.clone(), - _ => { - return None; - } - }; + if ctx.symbols().is_static(expr) { + return None; + } - let symbol_id = - ctx.generate_uid_in_current_scope(name.as_str(), SymbolFlags::FunctionScopedVariable); + let symbol_id = ctx + .generate_uid_in_current_scope_based_on_node(expr, SymbolFlags::FunctionScopedVariable); let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id)); // var _name; diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index d5045be53ea90..b7fd7429047f7 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,6 +1,6 @@ commit: 12619ffe -Passed: 396/953 +Passed: 397/953 # All Passed: * babel-preset-react @@ -1276,349 +1276,8 @@ preset-env: unknown field `shippedProposals`, expected `targets` or `bugfixes` preset-env: unknown field `shippedProposals`, expected `targets` or `bugfixes` -# babel-plugin-transform-logical-assignment-operators (4/6) -* logical-assignment/general-semantics/input.js - x Semantic Collector failed after transform - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:32:8] - 31 | - 32 | expect(deep.obj.x ||= 1).toBe(1); - : ^^^^ - 33 | expect(gets).toBe(1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:32:8] - 31 | - 32 | expect(deep.obj.x ||= 1).toBe(1); - : ^^^^ - 33 | expect(gets).toBe(1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:34:8] - 33 | expect(gets).toBe(1); - 34 | expect(deep.obj.x ||= 2).toBe(1); - : ^^^^ - 35 | expect(gets).toBe(2); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:34:8] - 33 | expect(gets).toBe(1); - 34 | expect(deep.obj.x ||= 2).toBe(1); - : ^^^^ - 35 | expect(gets).toBe(2); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:37:8] - 36 | - 37 | expect(deep.obj.x &&= 0).toBe(0); - : ^^^^ - 38 | expect(gets).toBe(3); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:37:8] - 36 | - 37 | expect(deep.obj.x &&= 0).toBe(0); - : ^^^^ - 38 | expect(gets).toBe(3); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:39:8] - 38 | expect(gets).toBe(3); - 39 | expect(deep.obj.x &&= 3).toBe(0); - : ^^^^ - 40 | expect(gets).toBe(4); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:39:8] - 38 | expect(gets).toBe(3); - 39 | expect(deep.obj.x &&= 3).toBe(0); - : ^^^^ - 40 | expect(gets).toBe(4); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:43:14] - 42 | var key = 0; - 43 | expect(obj[++key] ||= 1).toBe(1); - : ^^^ - 44 | expect(key).toBe(1); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:46:14] - 45 | key = 0; - 46 | expect(obj[++key] ||= 2).toBe(1); - : ^^^ - 47 | expect(key).toBe(1); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:50:14] - 49 | key = 0; - 50 | expect(obj[++key] &&= 0).toBe(0); - : ^^^ - 51 | expect(key).toBe(1); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:53:14] - 52 | key = 0; - 53 | expect(obj[++key] &&= 3).toBe(0); - : ^^^ - 54 | expect(key).toBe(1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:57:8] - 56 | key = 0; - 57 | expect(deep.obj[++key] ||= 1).toBe(1); - : ^^^^ - 58 | expect(gets).toBe(5); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:57:19] - 56 | key = 0; - 57 | expect(deep.obj[++key] ||= 1).toBe(1); - : ^^^ - 58 | expect(gets).toBe(5); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:57:8] - 56 | key = 0; - 57 | expect(deep.obj[++key] ||= 1).toBe(1); - : ^^^^ - 58 | expect(gets).toBe(5); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:57:19] - 56 | key = 0; - 57 | expect(deep.obj[++key] ||= 1).toBe(1); - : ^^^ - 58 | expect(gets).toBe(5); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:61:8] - 60 | key = 0; - 61 | expect(deep.obj[++key] ||= 2).toBe(1); - : ^^^^ - 62 | expect(gets).toBe(6); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:61:19] - 60 | key = 0; - 61 | expect(deep.obj[++key] ||= 2).toBe(1); - : ^^^ - 62 | expect(gets).toBe(6); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:61:8] - 60 | key = 0; - 61 | expect(deep.obj[++key] ||= 2).toBe(1); - : ^^^^ - 62 | expect(gets).toBe(6); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:61:19] - 60 | key = 0; - 61 | expect(deep.obj[++key] ||= 2).toBe(1); - : ^^^ - 62 | expect(gets).toBe(6); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:66:8] - 65 | key = 0; - 66 | expect(deep.obj[++key] &&= 0).toBe(0); - : ^^^^ - 67 | expect(gets).toBe(7); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:66:19] - 65 | key = 0; - 66 | expect(deep.obj[++key] &&= 0).toBe(0); - : ^^^ - 67 | expect(gets).toBe(7); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:66:8] - 65 | key = 0; - 66 | expect(deep.obj[++key] &&= 0).toBe(0); - : ^^^^ - 67 | expect(gets).toBe(7); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:66:19] - 65 | key = 0; - 66 | expect(deep.obj[++key] &&= 0).toBe(0); - : ^^^ - 67 | expect(gets).toBe(7); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:70:8] - 69 | key = 0; - 70 | expect(deep.obj[++key] &&= 3).toBe(0); - : ^^^^ - 71 | expect(gets).toBe(8); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:70:19] - 69 | key = 0; - 70 | expect(deep.obj[++key] &&= 3).toBe(0); - : ^^^ - 71 | expect(gets).toBe(8); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:70:8] - 69 | key = 0; - 70 | expect(deep.obj[++key] &&= 3).toBe(0); - : ^^^^ - 71 | expect(gets).toBe(8); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/general-semantics/input.js:70:19] - 69 | key = 0; - 70 | expect(deep.obj[++key] &&= 3).toBe(0); - : ^^^ - 71 | expect(gets).toBe(8); - `---- - - +# babel-plugin-transform-logical-assignment-operators (5/6) * logical-assignment/null-coalescing/input.js - x Semantic Collector failed after transform - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:28:8] - 27 | obj.x = undefined; - 28 | expect(deep.obj.x ??= 1).toBe(1); - : ^^^^ - 29 | expect(gets, 1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:28:8] - 27 | obj.x = undefined; - 28 | expect(deep.obj.x ??= 1).toBe(1); - : ^^^^ - 29 | expect(gets, 1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:30:8] - 29 | expect(gets, 1); - 30 | expect(deep.obj.x ??= 2).toBe(1); - : ^^^^ - 31 | expect(gets, 2); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:30:8] - 29 | expect(gets, 1); - 30 | expect(deep.obj.x ??= 2).toBe(1); - : ^^^^ - 31 | expect(gets, 2); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:35:14] - 34 | obj.x = undefined; - 35 | expect(obj[++key] ??= 1).toBe(1); - : ^^^ - 36 | expect(key, 1); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:38:14] - 37 | key = 0; - 38 | expect(obj[++key] ??= 2).toBe(1); - : ^^^ - 39 | expect(key, 1); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:43:8] - 42 | key = 0; - 43 | expect(deep.obj[++key] ??= 1).toBe(1); - : ^^^^ - 44 | expect(gets, 3); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:43:19] - 42 | key = 0; - 43 | expect(deep.obj[++key] ??= 1).toBe(1); - : ^^^ - 44 | expect(gets, 3); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:43:8] - 42 | key = 0; - 43 | expect(deep.obj[++key] ??= 1).toBe(1); - : ^^^^ - 44 | expect(gets, 3); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:43:19] - 42 | key = 0; - 43 | expect(deep.obj[++key] ??= 1).toBe(1); - : ^^^ - 44 | expect(gets, 3); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:47:8] - 46 | key = 0; - 47 | expect(deep.obj[++key] ??= 2).toBe(1); - : ^^^^ - 48 | expect(gets, 4); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:47:19] - 46 | key = 0; - 47 | expect(deep.obj[++key] ??= 2).toBe(1); - : ^^^ - 48 | expect(gets, 4); - `---- - - x Missing ReferenceId: deep - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:47:8] - 46 | key = 0; - 47 | expect(deep.obj[++key] ??= 2).toBe(1); - : ^^^^ - 48 | expect(gets, 4); - `---- - - x Missing ReferenceId: key - ,-[tasks/coverage/babel/packages/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing/input.js:47:19] - 46 | key = 0; - 47 | expect(deep.obj[++key] ??= 2).toBe(1); - : ^^^ - 48 | expect(gets, 4); - `----