diff --git a/crates/oxc_transformer/src/common/duplicate.rs b/crates/oxc_transformer/src/common/duplicate.rs index 5728f03441d99c..8a8fc73f34dd3f 100644 --- a/crates/oxc_transformer/src/common/duplicate.rs +++ b/crates/oxc_transformer/src/common/duplicate.rs @@ -5,6 +5,7 @@ use std::{ ptr, }; +use oxc_allocator::CloneIn; use oxc_ast::ast::{AssignmentOperator, Expression}; use oxc_span::SPAN; use oxc_syntax::reference::ReferenceFlags; @@ -109,13 +110,27 @@ impl<'a> TransformCtx<'a> { self.var_declarations.create_uid_var(&ident.name, ctx) } - // Reading `this` or `super` cannot have side effects, so no need for temp var - Expression::ThisExpression(this) => { - let references = create_array(|| ctx.ast.expression_this(this.span)); + // Reading any of these cannot have side effects, so no need for temp var + Expression::ThisExpression(_) + | Expression::Super(_) + | Expression::BooleanLiteral(_) + | Expression::NullLiteral(_) + | Expression::NumericLiteral(_) + | Expression::BigIntLiteral(_) + | Expression::RegExpLiteral(_) + | Expression::StringLiteral(_) => { + let references = create_array(|| expr.clone_in(ctx.ast.allocator)); return (expr, references); } - Expression::Super(super_expr) => { - let references = create_array(|| ctx.ast.expression_super(super_expr.span)); + // Template literal can have no side effects if it has no expressions + Expression::TemplateLiteral(lit) if lit.expressions.is_empty() => { + let references = create_array(|| { + ctx.ast.expression_template_literal( + lit.span, + lit.quasis.clone_in(ctx.ast.allocator), + ctx.ast.vec(), + ) + }); return (expr, references); } // Anything else requires temp var diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js new file mode 100644 index 00000000000000..b9be03c46d0099 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js @@ -0,0 +1,5 @@ +var _o, _o2; +let o; +o ?? (o = {}); +(_o = o).a ?? (_o.a = 1); +(_o2 = o)["b"] ?? (_o2["b"] = 2); diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 9b3374feec861b..13a74d86e731b1 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 99/111 +Passed: 102/114 # All Passed: * babel-plugin-transform-class-static-block diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js new file mode 100644 index 00000000000000..4b53b708e96123 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js @@ -0,0 +1,15 @@ +let boundObj; + +boundObj[true] &&= 1; +boundObj[null] &&= 2; +boundObj[123] &&= 3; +boundObj[123n] &&= 4; +boundObj[/abc/g] &&= 5; +boundObj["abc"] &&= 6; + +unboundObj[true] &&= 7; +unboundObj[null] &&= 8; +unboundObj[123] &&= 9; +unboundObj[123n] &&= 10; +unboundObj[/abc/g] &&= 11; +unboundObj["abc"] &&= 12; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js new file mode 100644 index 00000000000000..ba899a272fb421 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js @@ -0,0 +1,22 @@ +var _unboundObj, + _unboundObj2, + _unboundObj3, + _unboundObj4, + _unboundObj5, + _unboundObj6; + +let boundObj; + +boundObj[true] && (boundObj[true] = 1); +boundObj[null] && (boundObj[null] = 2); +boundObj[123] && (boundObj[123] = 3); +boundObj[123n] && (boundObj[123n] = 4); +boundObj[/abc/g] && (boundObj[/abc/g] = 5); +boundObj["abc"] && (boundObj["abc"] = 6); + +(_unboundObj = unboundObj)[true] && (_unboundObj[true] = 7); +(_unboundObj2 = unboundObj)[null] && (_unboundObj2[null] = 8); +(_unboundObj3 = unboundObj)[123] && (_unboundObj3[123] = 9); +(_unboundObj4 = unboundObj)[123n] && (_unboundObj4[123n] = 10); +(_unboundObj5 = unboundObj)[/abc/g] && (_unboundObj5[/abc/g] = 11); +(_unboundObj6 = unboundObj)["abc"] && (_unboundObj6["abc"] = 12); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js new file mode 100644 index 00000000000000..42d116ad6b4b67 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js @@ -0,0 +1,7 @@ +let boundObj; + +boundObj[`abc`] &&= 1; +unboundObj[`abc`] &&= 2; + +boundObj[`abc${foo}def`] &&= 3; +unboundObj[`abc${foo}def`] &&= 4; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js new file mode 100644 index 00000000000000..0bbabb75209300 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js @@ -0,0 +1,12 @@ +var _unboundObj, + _ref, + _unboundObj2, + _ref2; + +let boundObj; + +boundObj[`abc`] && (boundObj[`abc`] = 1); +(_unboundObj = unboundObj)[`abc`] && (_unboundObj[`abc`] = 2); + +boundObj[_ref = `abc${foo}def`] && (boundObj[_ref] = 3); +(_unboundObj2 = unboundObj)[_ref2 = `abc${foo}def`] && (_unboundObj2[_ref2] = 4); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js new file mode 100644 index 00000000000000..b68995d026eabd --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js @@ -0,0 +1,5 @@ +let boundProp; + +"abc".length &&= 1; +"abc"[boundProp] &&= 2; +"abc"[unboundProp] &&= 3; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js new file mode 100644 index 00000000000000..aeeafa7c6736f2 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js @@ -0,0 +1,7 @@ +var _unboundProp; + +let boundProp; + +"abc".length && ("abc".length = 1); +"abc"[boundProp] && ("abc"[boundProp] = 2); +"abc"[_unboundProp = unboundProp] && ("abc"[_unboundProp] = 3);