From 3a4bcc77fcaeb99bb9cbf8cde0bc6fe7c91be98a Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:08:10 +0000 Subject: [PATCH] fix(transformer): exponentiation transform: fix temp var names (#6318) Make naming of temp vars follow Babel. It wasn't apparent that our version of this transform was behaving differently from Babel because the Babel plugin has very few tests. The added tests replicate Babel's output. --- .typos.toml | 4 + .../src/es2016/exponentiation_operator.rs | 18 +-- .../snapshots/oxc.snap.md | 141 +++++++++++++++++- .../fixtures/assign-to-identifier/input.js | 5 + .../fixtures/assign-to-identifier/output.js | 6 + .../assign-to-member-expression/input.js | 54 +++++++ .../assign-to-member-expression/output.js | 141 ++++++++++++++++++ 7 files changed, 358 insertions(+), 11 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/output.js diff --git a/.typos.toml b/.typos.toml index c83946cc6..c4002ac83 100644 --- a/.typos.toml +++ b/.typos.toml @@ -19,7 +19,11 @@ extend-exclude = [ "tasks/coverage/babel", "tasks/coverage/test262", "tasks/coverage/typescript", + "tasks/coverage/snapshots", "tasks/prettier_conformance/prettier", + "tasks/prettier_conformance/snapshots", + "tasks/transform_conformance/tests/**/output.js", + "tasks/transform_conformance/snapshots", ] [default] diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index d3cccb538..519ea9e02 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -37,7 +37,7 @@ use oxc_ast::{ast::*, NONE}; use oxc_semantic::{ReferenceFlags, SymbolFlags}; use oxc_span::SPAN; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator}; -use oxc_traverse::{Traverse, TraverseCtx}; +use oxc_traverse::{ast_operations::get_var_name_from_node, Traverse, TraverseCtx}; use crate::TransformCtx; @@ -151,7 +151,8 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let reference = ctx.ast.expression_from_identifier_reference( ctx.create_unbound_reference_id(SPAN, ident.name.clone(), ReferenceFlags::Read), ); - self.add_new_reference(reference, &mut nodes, ctx) + let name = ident.name.as_str(); + self.add_new_reference(reference, name, &mut nodes, ctx) }; let reference = ctx.ast.move_assignment_target(assign_target); @@ -228,6 +229,7 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let mut obj = ctx.ast.move_expression(obj); // If the object reference that we need to save is locally declared, evaluating it multiple times // will not trigger getters or setters. `super` cannot be directly assigned, so use it directly too. + // TODO(improve-on-babel): We could also skip creating a temp var for `this.x **= 2`. let needs_temp_var = match &obj { Expression::Super(_) => false, Expression::Identifier(ident) => { @@ -236,7 +238,8 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { _ => true, }; if needs_temp_var { - obj = self.add_new_reference(obj, nodes, ctx); + let name = get_var_name_from_node(&obj); + obj = self.add_new_reference(obj, &name, nodes, ctx); } let computed = member_expr.is_computed(); @@ -311,7 +314,8 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { if expr.is_literal() { return expr; } - self.add_new_reference(expr, nodes, ctx) + let name = get_var_name_from_node(&expr); + self.add_new_reference(expr, &name, nodes, ctx) } MemberExpression::StaticMemberExpression(expr) => { ctx.ast.expression_string_literal(SPAN, expr.property.name.clone()) @@ -324,14 +328,10 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { fn add_new_reference( &mut self, expr: Expression<'a>, + name: &str, nodes: &mut Vec<'a, Expression<'a>>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let name = match expr { - Expression::Identifier(ref ident) => ident.name.clone().as_str(), - _ => "ref", - }; - let binding = ctx.generate_uid_in_current_scope(name, SymbolFlags::FunctionScopedVariable); // var _name; diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 8eec97bd9..80325e063 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,17 +1,154 @@ commit: 3bcfee23 -Passed: 57/66 +Passed: 57/68 # All Passed: * babel-plugin-transform-nullish-coalescing-operator * babel-plugin-transform-optional-catch-binding -* babel-plugin-transform-exponentiation-operator * babel-plugin-transform-arrow-functions * babel-preset-typescript * babel-plugin-transform-react-jsx-source * regexp +# babel-plugin-transform-exponentiation-operator (2/4) +* assign-to-identifier/input.js +Reference flags mismatch for "x": +after transform: ReferenceId(6): ReferenceFlags(Write) +rebuilt : ReferenceId(2): ReferenceFlags(Read) +Reference flags mismatch for "_y": +after transform: ReferenceId(9): ReferenceFlags(Write) +rebuilt : ReferenceId(3): ReferenceFlags(Read | Write) +Reference flags mismatch for "_z": +after transform: ReferenceId(13): ReferenceFlags(Write) +rebuilt : ReferenceId(8): ReferenceFlags(Read | Write) +Reference flags mismatch for "_q": +after transform: ReferenceId(17): ReferenceFlags(Write) +rebuilt : ReferenceId(14): ReferenceFlags(Read | Write) + +* assign-to-member-expression/input.js +Reference flags mismatch for "_obj$foo$bar": +after transform: ReferenceId(42): ReferenceFlags(Write) +rebuilt : ReferenceId(6): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropName": +after transform: ReferenceId(46): ReferenceFlags(Write) +rebuilt : ReferenceId(11): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropName": +after transform: ReferenceId(51): ReferenceFlags(Write) +rebuilt : ReferenceId(18): ReferenceFlags(Read | Write) +Reference flags mismatch for "_obj$foo2$bar": +after transform: ReferenceId(56): ReferenceFlags(Write) +rebuilt : ReferenceId(25): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropName2": +after transform: ReferenceId(58): ReferenceFlags(Write) +rebuilt : ReferenceId(27): ReferenceFlags(Read | Write) +Reference flags mismatch for "_obj$foo3$bar": +after transform: ReferenceId(63): ReferenceFlags(Write) +rebuilt : ReferenceId(34): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropName2": +after transform: ReferenceId(65): ReferenceFlags(Write) +rebuilt : ReferenceId(36): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropObj$foo$bar": +after transform: ReferenceId(70): ReferenceFlags(Write) +rebuilt : ReferenceId(43): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropObj$foo$b": +after transform: ReferenceId(75): ReferenceFlags(Write) +rebuilt : ReferenceId(50): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj": +after transform: ReferenceId(80): ReferenceFlags(Write) +rebuilt : ReferenceId(57): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj2": +after transform: ReferenceId(84): ReferenceFlags(Write) +rebuilt : ReferenceId(62): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj$foo$bar": +after transform: ReferenceId(88): ReferenceFlags(Write) +rebuilt : ReferenceId(67): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj3": +after transform: ReferenceId(92): ReferenceFlags(Write) +rebuilt : ReferenceId(72): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropName3": +after transform: ReferenceId(94): ReferenceFlags(Write) +rebuilt : ReferenceId(74): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj4": +after transform: ReferenceId(99): ReferenceFlags(Write) +rebuilt : ReferenceId(81): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropName3": +after transform: ReferenceId(101): ReferenceFlags(Write) +rebuilt : ReferenceId(83): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj$foo2$bar": +after transform: ReferenceId(106): ReferenceFlags(Write) +rebuilt : ReferenceId(90): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropName4": +after transform: ReferenceId(108): ReferenceFlags(Write) +rebuilt : ReferenceId(92): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj$foo3$bar": +after transform: ReferenceId(113): ReferenceFlags(Write) +rebuilt : ReferenceId(99): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropName4": +after transform: ReferenceId(115): ReferenceFlags(Write) +rebuilt : ReferenceId(101): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj5": +after transform: ReferenceId(120): ReferenceFlags(Write) +rebuilt : ReferenceId(108): ReferenceFlags(Read | Write) +Reference flags mismatch for "_boundPropObj2$foo$ba": +after transform: ReferenceId(122): ReferenceFlags(Write) +rebuilt : ReferenceId(110): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundObj6": +after transform: ReferenceId(127): ReferenceFlags(Write) +rebuilt : ReferenceId(117): ReferenceFlags(Read | Write) +Reference flags mismatch for "_unboundPropObj2$foo$": +after transform: ReferenceId(129): ReferenceFlags(Write) +rebuilt : ReferenceId(119): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn": +after transform: ReferenceId(134): ReferenceFlags(Write) +rebuilt : ReferenceId(126): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn$foo$bar": +after transform: ReferenceId(138): ReferenceFlags(Write) +rebuilt : ReferenceId(131): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn$prop": +after transform: ReferenceId(142): ReferenceFlags(Write) +rebuilt : ReferenceId(136): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn2": +after transform: ReferenceId(144): ReferenceFlags(Write) +rebuilt : ReferenceId(138): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn$prop2": +after transform: ReferenceId(149): ReferenceFlags(Write) +rebuilt : ReferenceId(145): ReferenceFlags(Read | Write) +Reference flags mismatch for "_ref": +after transform: ReferenceId(151): ReferenceFlags(Write) +rebuilt : ReferenceId(147): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this": +after transform: ReferenceId(156): ReferenceFlags(Write) +rebuilt : ReferenceId(154): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this$foo$bar": +after transform: ReferenceId(160): ReferenceFlags(Write) +rebuilt : ReferenceId(158): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this2": +after transform: ReferenceId(164): ReferenceFlags(Write) +rebuilt : ReferenceId(162): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this3": +after transform: ReferenceId(168): ReferenceFlags(Write) +rebuilt : ReferenceId(166): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn4$foo$bar$qux": +after transform: ReferenceId(170): ReferenceFlags(Write) +rebuilt : ReferenceId(167): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this4": +after transform: ReferenceId(175): ReferenceFlags(Write) +rebuilt : ReferenceId(174): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this$foo$bar2": +after transform: ReferenceId(179): ReferenceFlags(Write) +rebuilt : ReferenceId(178): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this5": +after transform: ReferenceId(183): ReferenceFlags(Write) +rebuilt : ReferenceId(182): ReferenceFlags(Read | Write) +Reference flags mismatch for "_this6": +after transform: ReferenceId(187): ReferenceFlags(Write) +rebuilt : ReferenceId(186): ReferenceFlags(Read | Write) +Reference flags mismatch for "_fn4$foo$bar$qux2": +after transform: ReferenceId(189): ReferenceFlags(Write) +rebuilt : ReferenceId(187): ReferenceFlags(Read | Write) + + # babel-plugin-transform-typescript (1/8) * class-property-definition/input.ts Unresolved references mismatch: diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/input.js new file mode 100644 index 000000000..b99ba921e --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/input.js @@ -0,0 +1,5 @@ +let x, fn; +x **= 2; +y **= 3; +z **= fn(); +q **= unboundFn(); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/output.js new file mode 100644 index 000000000..82b6554fb --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-identifier/output.js @@ -0,0 +1,6 @@ +var _y, _z, _q; +let x, fn; +x = Math.pow(x, 2); +_y = y, y = Math.pow(_y, 3); +_z = z, z = Math.pow(_z, fn()); +_q = q, q = Math.pow(_q, unboundFn()); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/input.js new file mode 100644 index 000000000..5ba47a83e --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/input.js @@ -0,0 +1,54 @@ +// Bound root of member expression +let obj; +obj.prop **= 2; +obj['prop blah'] **= 3; +obj.foo.bar.qux **= 4; + +let boundPropName; +obj[boundPropName] **= 5; +obj[unboundPropName] **= 6; + +let boundPropName2; +obj.foo2.bar2[boundPropName2] **= 7; +obj.foo3.bar3[unboundPropName2] **= 8; + +let boundPropObj; +obj[boundPropObj.foo.bar.qux] **= 9; +obj[unboundPropObj.foo.bar.qux] **= 10; + +// Unbound root of member expression +unboundObj.prop **= 11; +unboundObj['prop blah'] **= 12; +unboundObj.foo.bar.qux **= 13; + +let boundPropName3; +unboundObj[boundPropName3] **= 14; +unboundObj[unboundPropName3] **= 15; + +let boundPropName4; +unboundObj.foo2.bar2[boundPropName4] **= 16; +unboundObj.foo3.bar3[unboundPropName4] **= 17; + +let boundPropObj2; +unboundObj[boundPropObj2.foo.bar.qux] **= 18; +unboundObj[unboundPropObj2.foo.bar.qux] **= 19; + +// Other expressions +let fn, fn2; +fn().prop **= 20; +fn().foo().bar().qux **= 21; +fn().prop[fn2()] **= 22; +fn().prop[fn3().foo().bar().qux() + ' junk'] **= 23; + +// `this` +this.prop **= 24; +this.foo.bar.qux **= 25; +this['prop blah'] **= 26; +this[fn4().foo.bar.qux()] **= 27; + +function outer() { + this.prop **= 28; + this.foo.bar.qux **= 29; + this['prop blah'] **= 30; + this[fn4().foo.bar.qux()] **= 31; +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/output.js new file mode 100644 index 000000000..30cc7d09c --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-to-member-expression/output.js @@ -0,0 +1,141 @@ +var _obj$foo$bar, + _boundPropName, + _unboundPropName, + _obj$foo2$bar, + _boundPropName2, + _obj$foo3$bar, + _unboundPropName2, + _boundPropObj$foo$bar, + _unboundPropObj$foo$b, + _unboundObj, + _unboundObj2, + _unboundObj$foo$bar, + _unboundObj3, + _boundPropName3, + _unboundObj4, + _unboundPropName3, + _unboundObj$foo2$bar, + _boundPropName4, + _unboundObj$foo3$bar, + _unboundPropName4, + _unboundObj5, + _boundPropObj2$foo$ba, + _unboundObj6, + _unboundPropObj2$foo$, + _fn, + _fn$foo$bar, + _fn$prop, + _fn2, + _fn$prop2, + _ref, + _this, + _this$foo$bar, + _this2, + _this3, + _fn4$foo$bar$qux; + +// Bound root of member expression +let obj; +obj["prop"] = Math.pow(obj["prop"], 2); +obj["prop blah"] = Math.pow(obj["prop blah"], 3); +(_obj$foo$bar = obj.foo.bar), + (_obj$foo$bar["qux"] = Math.pow(_obj$foo$bar["qux"], 4)); +let boundPropName; +(_boundPropName = boundPropName), + (obj[_boundPropName] = Math.pow(obj[_boundPropName], 5)); +(_unboundPropName = unboundPropName), + (obj[_unboundPropName] = Math.pow(obj[_unboundPropName], 6)); +let boundPropName2; +(_obj$foo2$bar = obj.foo2.bar2), + (_boundPropName2 = boundPropName2), + (_obj$foo2$bar[_boundPropName2] = Math.pow( + _obj$foo2$bar[_boundPropName2], + 7 + )); +(_obj$foo3$bar = obj.foo3.bar3), + (_unboundPropName2 = unboundPropName2), + (_obj$foo3$bar[_unboundPropName2] = Math.pow( + _obj$foo3$bar[_unboundPropName2], + 8 + )); +let boundPropObj; +(_boundPropObj$foo$bar = boundPropObj.foo.bar.qux), + (obj[_boundPropObj$foo$bar] = Math.pow(obj[_boundPropObj$foo$bar], 9)); +(_unboundPropObj$foo$b = unboundPropObj.foo.bar.qux), + (obj[_unboundPropObj$foo$b] = Math.pow(obj[_unboundPropObj$foo$b], 10)); + +// Unbound root of member expression +(_unboundObj = unboundObj), + (_unboundObj["prop"] = Math.pow(_unboundObj["prop"], 11)); +(_unboundObj2 = unboundObj), + (_unboundObj2["prop blah"] = Math.pow(_unboundObj2["prop blah"], 12)); +(_unboundObj$foo$bar = unboundObj.foo.bar), + (_unboundObj$foo$bar["qux"] = Math.pow(_unboundObj$foo$bar["qux"], 13)); +let boundPropName3; +(_unboundObj3 = unboundObj), + (_boundPropName3 = boundPropName3), + (_unboundObj3[_boundPropName3] = Math.pow(_unboundObj3[_boundPropName3], 14)); +(_unboundObj4 = unboundObj), + (_unboundPropName3 = unboundPropName3), + (_unboundObj4[_unboundPropName3] = Math.pow( + _unboundObj4[_unboundPropName3], + 15 + )); +let boundPropName4; +(_unboundObj$foo2$bar = unboundObj.foo2.bar2), + (_boundPropName4 = boundPropName4), + (_unboundObj$foo2$bar[_boundPropName4] = Math.pow( + _unboundObj$foo2$bar[_boundPropName4], + 16 + )); +(_unboundObj$foo3$bar = unboundObj.foo3.bar3), + (_unboundPropName4 = unboundPropName4), + (_unboundObj$foo3$bar[_unboundPropName4] = Math.pow( + _unboundObj$foo3$bar[_unboundPropName4], + 17 + )); +let boundPropObj2; +(_unboundObj5 = unboundObj), + (_boundPropObj2$foo$ba = boundPropObj2.foo.bar.qux), + (_unboundObj5[_boundPropObj2$foo$ba] = Math.pow( + _unboundObj5[_boundPropObj2$foo$ba], + 18 + )); +(_unboundObj6 = unboundObj), + (_unboundPropObj2$foo$ = unboundPropObj2.foo.bar.qux), + (_unboundObj6[_unboundPropObj2$foo$] = Math.pow( + _unboundObj6[_unboundPropObj2$foo$], + 19 + )); + +// Other expressions +let fn, fn2; +(_fn = fn()), (_fn["prop"] = Math.pow(_fn["prop"], 20)); +(_fn$foo$bar = fn().foo().bar()), + (_fn$foo$bar["qux"] = Math.pow(_fn$foo$bar["qux"], 21)); +(_fn$prop = fn().prop), + (_fn2 = fn2()), + (_fn$prop[_fn2] = Math.pow(_fn$prop[_fn2], 22)); +(_fn$prop2 = fn().prop), + (_ref = fn3().foo().bar().qux() + " junk"), + (_fn$prop2[_ref] = Math.pow(_fn$prop2[_ref], 23)); + +// `this` +(_this = this), (_this["prop"] = Math.pow(_this["prop"], 24)); +(_this$foo$bar = this.foo.bar), + (_this$foo$bar["qux"] = Math.pow(_this$foo$bar["qux"], 25)); +(_this2 = this), (_this2["prop blah"] = Math.pow(_this2["prop blah"], 26)); +(_this3 = this), + (_fn4$foo$bar$qux = fn4().foo.bar.qux()), + (_this3[_fn4$foo$bar$qux] = Math.pow(_this3[_fn4$foo$bar$qux], 27)); + +function outer() { + var _this4, _this$foo$bar2, _this5, _this6, _fn4$foo$bar$qux2; + (_this4 = this), (_this4["prop"] = Math.pow(_this4["prop"], 28)); + (_this$foo$bar2 = this.foo.bar), + (_this$foo$bar2["qux"] = Math.pow(_this$foo$bar2["qux"], 29)); + (_this5 = this), (_this5["prop blah"] = Math.pow(_this5["prop blah"], 30)); + (_this6 = this), + (_fn4$foo$bar$qux2 = fn4().foo.bar.qux()), + (_this6[_fn4$foo$bar$qux2] = Math.pow(_this6[_fn4$foo$bar$qux2], 31)); +}