From 66b5b110bf94259e0e0223224940bb8d0384e9b6 Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Mon, 24 Apr 2023 13:23:53 +0800 Subject: [PATCH] fix(es/compat): Transform curried function call with optional chaining (#7313) **Related issue:** - Closes #7312 --- .../issues-6xxx/6459/1/output/index.ts | 2 +- .../issues-6xxx/6459/3/output/index.ts | 2 +- .../tsc-references/deleteChain.1.normal.js | 8 ++-- .../tsc-references/deleteChain.2.minified.js | 4 +- .../tsc-references/parentheses.1.normal.js | 2 +- .../tsc-references/parentheses.2.minified.js | 2 +- crates/swc_ecma_transforms_base/src/fixer.rs | 16 ++++++++ .../src/es2020/opt_chaining.rs | 21 ++++++++++ .../tests/es2020_optional_chaining.rs | 41 +++++++++++++++++++ .../tests/opt-chain/issue-7312/exec.js | 3 ++ 10 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 crates/swc_ecma_transforms_compat/tests/opt-chain/issue-7312/exec.js diff --git a/crates/swc/tests/fixture/issues-6xxx/6459/1/output/index.ts b/crates/swc/tests/fixture/issues-6xxx/6459/1/output/index.ts index ec4e5fe1913..fd534b9f810 100644 --- a/crates/swc/tests/fixture/issues-6xxx/6459/1/output/index.ts +++ b/crates/swc/tests/fixture/issues-6xxx/6459/1/output/index.ts @@ -1 +1 @@ -(myValue.children?.children)[0].value = 'My Value'; +(myValue.children?.children[0]).value = 'My Value'; diff --git a/crates/swc/tests/fixture/issues-6xxx/6459/3/output/index.ts b/crates/swc/tests/fixture/issues-6xxx/6459/3/output/index.ts index b464686388f..a2d71573f23 100644 --- a/crates/swc/tests/fixture/issues-6xxx/6459/3/output/index.ts +++ b/crates/swc/tests/fixture/issues-6xxx/6459/3/output/index.ts @@ -1 +1 @@ -(foo?.bar.bar.bar?.baz).baz.baz.foo.baz = 1; +(foo?.bar.bar.bar?.baz.baz.baz).foo.baz = 1; diff --git a/crates/swc/tests/tsc-references/deleteChain.1.normal.js b/crates/swc/tests/tsc-references/deleteChain.1.normal.js index 03736e44e3a..812a768b6f2 100644 --- a/crates/swc/tests/tsc-references/deleteChain.1.normal.js +++ b/crates/swc/tests/tsc-references/deleteChain.1.normal.js @@ -1,14 +1,14 @@ //// [deleteChain.ts] -var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _o4_b_c_d1, _o4_b1, _o4_b_c_d2, _o4_b2, _o5_b_c_d, _o5_b, _o5_b_c_d1, _o5_b1, _o6_b_c_d, _o6_b, _o6_b_c_d1, _o6_b1; +var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _ref, _o4_b1, _o4_b_c_d1, _o4_b2, _o5_b_c_d, _o5_b, _o5_b_c_d1, _o5_b1, _o6_b_c_d, _o6_b, _o6_b_c_d1, _o6_b1; o1 === null || o1 === void 0 ? void 0 : delete o1.b; delete (o1 === null || o1 === void 0 ? void 0 : o1.b); o2 === null || o2 === void 0 ? void 0 : delete o2.b.c; -o2 === null || o2 === void 0 ? void 0 : delete o2.b.c; +delete (o2 === null || o2 === void 0 ? void 0 : o2.b.c); (_o3_b = o3.b) === null || _o3_b === void 0 ? void 0 : delete _o3_b.c; delete ((_o3_b1 = o3.b) === null || _o3_b1 === void 0 ? void 0 : _o3_b1.c); (_o4_b_c_d = (_o4_b = o4.b) === null || _o4_b === void 0 ? void 0 : _o4_b.c.d) === null || _o4_b_c_d === void 0 ? void 0 : delete _o4_b_c_d.e; -(_o4_b_c_d1 = (_o4_b1 = o4.b) === null || _o4_b1 === void 0 ? void 0 : _o4_b1.c.d) === null || _o4_b_c_d1 === void 0 ? void 0 : delete _o4_b_c_d1.e; -delete ((_o4_b_c_d2 = (_o4_b2 = o4.b) === null || _o4_b2 === void 0 ? void 0 : _o4_b2.c.d) === null || _o4_b_c_d2 === void 0 ? void 0 : _o4_b_c_d2.e); +(_ref = (_o4_b1 = o4.b) === null || _o4_b1 === void 0 ? void 0 : _o4_b1.c.d) === null || _ref === void 0 ? void 0 : delete _ref.e; +delete ((_o4_b_c_d1 = (_o4_b2 = o4.b) === null || _o4_b2 === void 0 ? void 0 : _o4_b2.c.d) === null || _o4_b_c_d1 === void 0 ? void 0 : _o4_b_c_d1.e); (_o5_b_c_d = (_o5_b = o5.b) === null || _o5_b === void 0 ? void 0 : _o5_b.call(o5).c.d) === null || _o5_b_c_d === void 0 ? void 0 : delete _o5_b_c_d.e; delete ((_o5_b_c_d1 = (_o5_b1 = o5.b) === null || _o5_b1 === void 0 ? void 0 : _o5_b1.call(o5).c.d) === null || _o5_b_c_d1 === void 0 ? void 0 : _o5_b_c_d1.e); (_o6_b_c_d = (_o6_b = o6.b) === null || _o6_b === void 0 ? void 0 : _o6_b["c"].d) === null || _o6_b_c_d === void 0 ? void 0 : delete _o6_b_c_d["e"]; diff --git a/crates/swc/tests/tsc-references/deleteChain.2.minified.js b/crates/swc/tests/tsc-references/deleteChain.2.minified.js index a31b3d6f43c..13549dc4bc9 100644 --- a/crates/swc/tests/tsc-references/deleteChain.2.minified.js +++ b/crates/swc/tests/tsc-references/deleteChain.2.minified.js @@ -1,3 +1,3 @@ //// [deleteChain.ts] -var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _o4_b_c_d1, _o4_b1, _o4_b_c_d2, _o4_b2, _o5_b_c_d, _o5_b, _o5_b_c_d1, _o5_b1, _o6_b_c_d, _o6_b, _o6_b_c_d1, _o6_b1; -null == o1 || delete o1.b, delete (null == o1 ? void 0 : o1.b), null == o2 || delete o2.b.c, null == o2 || delete o2.b.c, null === (_o3_b = o3.b) || void 0 === _o3_b || delete _o3_b.c, delete (null === (_o3_b1 = o3.b) || void 0 === _o3_b1 ? void 0 : _o3_b1.c), null === (_o4_b_c_d = null === (_o4_b = o4.b) || void 0 === _o4_b ? void 0 : _o4_b.c.d) || void 0 === _o4_b_c_d || delete _o4_b_c_d.e, null === (_o4_b_c_d1 = null === (_o4_b1 = o4.b) || void 0 === _o4_b1 ? void 0 : _o4_b1.c.d) || void 0 === _o4_b_c_d1 || delete _o4_b_c_d1.e, delete (null === (_o4_b_c_d2 = null === (_o4_b2 = o4.b) || void 0 === _o4_b2 ? void 0 : _o4_b2.c.d) || void 0 === _o4_b_c_d2 ? void 0 : _o4_b_c_d2.e), null === (_o5_b_c_d = null === (_o5_b = o5.b) || void 0 === _o5_b ? void 0 : _o5_b.call(o5).c.d) || void 0 === _o5_b_c_d || delete _o5_b_c_d.e, delete (null === (_o5_b_c_d1 = null === (_o5_b1 = o5.b) || void 0 === _o5_b1 ? void 0 : _o5_b1.call(o5).c.d) || void 0 === _o5_b_c_d1 ? void 0 : _o5_b_c_d1.e), null === (_o6_b_c_d = null === (_o6_b = o6.b) || void 0 === _o6_b ? void 0 : _o6_b.c.d) || void 0 === _o6_b_c_d || delete _o6_b_c_d.e, delete (null === (_o6_b_c_d1 = null === (_o6_b1 = o6.b) || void 0 === _o6_b1 ? void 0 : _o6_b1.c.d) || void 0 === _o6_b_c_d1 ? void 0 : _o6_b_c_d1.e); +var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _ref, _o4_b1, _o4_b_c_d1, _o4_b2, _o5_b_c_d, _o5_b, _o5_b_c_d1, _o5_b1, _o6_b_c_d, _o6_b, _o6_b_c_d1, _o6_b1; +null == o1 || delete o1.b, delete (null == o1 ? void 0 : o1.b), null == o2 || delete o2.b.c, delete (null == o2 ? void 0 : o2.b.c), null === (_o3_b = o3.b) || void 0 === _o3_b || delete _o3_b.c, delete (null === (_o3_b1 = o3.b) || void 0 === _o3_b1 ? void 0 : _o3_b1.c), null === (_o4_b_c_d = null === (_o4_b = o4.b) || void 0 === _o4_b ? void 0 : _o4_b.c.d) || void 0 === _o4_b_c_d || delete _o4_b_c_d.e, null === (_ref = null === (_o4_b1 = o4.b) || void 0 === _o4_b1 ? void 0 : _o4_b1.c.d) || void 0 === _ref || delete _ref.e, delete (null === (_o4_b_c_d1 = null === (_o4_b2 = o4.b) || void 0 === _o4_b2 ? void 0 : _o4_b2.c.d) || void 0 === _o4_b_c_d1 ? void 0 : _o4_b_c_d1.e), null === (_o5_b_c_d = null === (_o5_b = o5.b) || void 0 === _o5_b ? void 0 : _o5_b.call(o5).c.d) || void 0 === _o5_b_c_d || delete _o5_b_c_d.e, delete (null === (_o5_b_c_d1 = null === (_o5_b1 = o5.b) || void 0 === _o5_b1 ? void 0 : _o5_b1.call(o5).c.d) || void 0 === _o5_b_c_d1 ? void 0 : _o5_b_c_d1.e), null === (_o6_b_c_d = null === (_o6_b = o6.b) || void 0 === _o6_b ? void 0 : _o6_b.c.d) || void 0 === _o6_b_c_d || delete _o6_b_c_d.e, delete (null === (_o6_b_c_d1 = null === (_o6_b1 = o6.b) || void 0 === _o6_b1 ? void 0 : _o6_b1.c.d) || void 0 === _o6_b_c_d1 ? void 0 : _o6_b_c_d1.e); diff --git a/crates/swc/tests/tsc-references/parentheses.1.normal.js b/crates/swc/tests/tsc-references/parentheses.1.normal.js index 952836f56c3..f58ca270b55 100644 --- a/crates/swc/tests/tsc-references/parentheses.1.normal.js +++ b/crates/swc/tests/tsc-references/parentheses.1.normal.js @@ -2,4 +2,4 @@ o1(o1 !== null && o1 !== void 0 ? o1 : 1); (o2 === null || o2 === void 0 ? void 0 : o2.b)(o1 !== null && o1 !== void 0 ? o1 : 1); (o3 === null || o3 === void 0 ? void 0 : o3.b())(o1 !== null && o1 !== void 0 ? o1 : 1); -o4 === null || o4 === void 0 ? void 0 : o4.b().c(o1 !== null && o1 !== void 0 ? o1 : 1); +(o4 === null || o4 === void 0 ? void 0 : o4.b().c)(o1 !== null && o1 !== void 0 ? o1 : 1); diff --git a/crates/swc/tests/tsc-references/parentheses.2.minified.js b/crates/swc/tests/tsc-references/parentheses.2.minified.js index d5530bf83a6..ee0dd1315a0 100644 --- a/crates/swc/tests/tsc-references/parentheses.2.minified.js +++ b/crates/swc/tests/tsc-references/parentheses.2.minified.js @@ -1,2 +1,2 @@ //// [parentheses.ts] -o1(null != o1 ? o1 : 1), (null == o2 ? void 0 : o2.b)(null != o1 ? o1 : 1), (null == o3 ? void 0 : o3.b())(null != o1 ? o1 : 1), null == o4 || o4.b().c(null != o1 ? o1 : 1); +o1(null != o1 ? o1 : 1), (null == o2 ? void 0 : o2.b)(null != o1 ? o1 : 1), (null == o3 ? void 0 : o3.b())(null != o1 ? o1 : 1), (null == o4 ? void 0 : o4.b().c)(null != o1 ? o1 : 1); diff --git a/crates/swc_ecma_transforms_base/src/fixer.rs b/crates/swc_ecma_transforms_base/src/fixer.rs index 17103d8c2c3..6daa14d15a5 100644 --- a/crates/swc_ecma_transforms_base/src/fixer.rs +++ b/crates/swc_ecma_transforms_base/src/fixer.rs @@ -1000,6 +1000,11 @@ impl Fixer<'_> { return; } + // `(a?.b.c)() !== a?.b.c()` + if Self::contain_opt_chain(expr) { + return; + } + let expr_span = expr.span(); let paren_span = *paren_span; self.unwrap_expr(expr); @@ -1011,6 +1016,17 @@ impl Fixer<'_> { } } + fn contain_opt_chain(e: &Expr) -> bool { + match e { + Expr::Member(member) => Self::contain_opt_chain(&member.obj), + Expr::OptChain(_) => true, + Expr::Call(CallExpr { callee, .. }) if callee.is_expr() => { + Self::contain_opt_chain(callee.as_expr().unwrap()) + } + _ => false, + } + } + fn handle_expr_stmt(&mut self, expr: &mut Expr) { match expr { // It's important for arrow pass to work properly. diff --git a/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs b/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs index 2fa09f7b05d..31fe89a08df 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs @@ -235,6 +235,27 @@ impl OptChaining { })), }; } + Callee::Expr(callee) if callee.is_call() => { + let mut callee = callee.take().call().unwrap(); + let callee = self.handle_call(&mut callee); + + return match callee { + Ok(expr) => Ok(CondExpr { + span: e.span, + alt: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: expr.alt.as_callee(), + args: e.args.take(), + type_args: e.type_args.take(), + })), + ..expr + }), + Err(callee) => Err(Expr::Call(CallExpr { + callee: callee.as_callee(), + ..e.take() + })), + }; + } _ => {} } diff --git a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs index 8566ad1d727..060fbea8be3 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs @@ -105,6 +105,47 @@ foo == null ? void 0 : foo.bar == null ? void 0 : foo.bar(foo.bar, true); "# ); +// parentheses +test!( + syntax(), + |_| tr(Default::default()), + parentheses, + r#" + (o1)(o1 ?? 1); + (o2?.b)(o1 ?? 1); + (o3?.b())(o1 ?? 1); + (o4?.b().c)(o1 ?? 1); +"#, + r#" + o1(o1 ?? 1); + (o2 === null || o2 === void 0 ? void 0 : o2.b)(o1 ?? 1); + (o3 === null || o3 === void 0 ? void 0 : o3.b())(o1 ?? 1); + (o4 === null || o4 === void 0 ? void 0 : o4.b().c)(o1 ?? 1); +"# +); + +// curried_function_call +test!( + syntax(), + |_| tr(Default::default()), + curried_function_call, + r#" + a?.b()(); + a?.b()()(); + a?.b()().c; + a?.b()()?.c; + a?.b()()?.c()(); +"#, + r#" + var _a_b, _a_b1; + a === null || a === void 0 ? void 0 : a.b()(); + a === null || a === void 0 ? void 0 : a.b()()(); + a === null || a === void 0 ? void 0 : a.b()().c; + (_a_b = a === null || a === void 0 ? void 0 : a.b()()) === null || _a_b === void 0 ? void 0 : _a_b.c; + (_a_b1 = a === null || a === void 0 ? void 0 : a.b()()) === null || _a_b1 === void 0 ? void 0 : _a_b1.c()(); +"# +); + // indirect_eval_call test!( syntax(), diff --git a/crates/swc_ecma_transforms_compat/tests/opt-chain/issue-7312/exec.js b/crates/swc_ecma_transforms_compat/tests/opt-chain/issue-7312/exec.js new file mode 100644 index 00000000000..d7160a5327a --- /dev/null +++ b/crates/swc_ecma_transforms_compat/tests/opt-chain/issue-7312/exec.js @@ -0,0 +1,3 @@ +var a = undefined; + +a?.a()()?.a()();