mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 14:16:12 +03:00
fix(es/fixer): Preserve parenthesis for optional chaining (#8399)
**Related issue:** - Closes #8398
This commit is contained in:
parent
3b845c33b3
commit
a69f172aac
15
crates/swc/tests/fixture/issues-8xxx/8398/input/.swcrc
Normal file
15
crates/swc/tests/fixture/issues-8xxx/8398/input/.swcrc
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true
|
||||
},
|
||||
"target": "es2022",
|
||||
"minify": {
|
||||
"compress": true
|
||||
},
|
||||
"loose": false
|
||||
},
|
||||
"isModule": true,
|
||||
"minify": true
|
||||
}
|
12
crates/swc/tests/fixture/issues-8xxx/8398/input/index.js
Normal file
12
crates/swc/tests/fixture/issues-8xxx/8398/input/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
(obj?.a).b;
|
||||
(obj?.a)();
|
||||
(obj?.a)[0];
|
||||
(obj?.a)`hello`;
|
||||
|
||||
(obj.a?.()).a;
|
||||
(obj.a?.())();
|
||||
(obj.a?.())[0];
|
||||
(obj.a?.())`hello`;
|
||||
|
||||
(((a?.b)));
|
||||
(((a?.())));
|
@ -0,0 +1 @@
|
||||
(obj?.a).b,(obj?.a)(),(obj?.a)[0],(obj?.a)`hello`,(obj.a?.()).a,(obj.a?.())(),(obj.a?.())[0],(obj.a?.())`hello`,a?.b,a?.();
|
@ -46,7 +46,7 @@ var needsExemplar = function() {
|
||||
var _ = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : x;
|
||||
return void 0;
|
||||
};
|
||||
var expected = /** @type {{name: string, readonly middleInit: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */ /** @type {*} */ null;
|
||||
var expected = /** @type {*} */ null;
|
||||
/**
|
||||
*
|
||||
* @param {typeof returnExemplar} a
|
||||
|
@ -1,15 +1,15 @@
|
||||
//// [deleteChain.ts]
|
||||
var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _this, _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;
|
||||
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;
|
||||
o1 === null || o1 === void 0 ? true : delete o1.b;
|
||||
o1 === null || o1 === void 0 ? true : delete o1.b;
|
||||
delete (o1 === null || o1 === void 0 ? void 0 : o1.b);
|
||||
o2 === null || o2 === void 0 ? true : delete o2.b.c;
|
||||
delete (o2 === null || o2 === void 0 ? void 0 : o2.b.c);
|
||||
o2 === null || o2 === void 0 ? true : delete o2.b.c;
|
||||
(_o3_b = o3.b) === null || _o3_b === void 0 ? true : delete _o3_b.c;
|
||||
delete ((_o3_b1 = o3.b) === null || _o3_b1 === void 0 ? void 0 : _o3_b1.c);
|
||||
(_o3_b1 = o3.b) === null || _o3_b1 === void 0 ? true : delete _o3_b1.c;
|
||||
(_o4_b = o4.b) === null || _o4_b === void 0 ? true : (_o4_b_c_d = _o4_b.c.d) === null || _o4_b_c_d === void 0 ? true : delete _o4_b_c_d.e;
|
||||
(_this = (_o4_b1 = o4.b) === null || _o4_b1 === void 0 ? void 0 : _o4_b1.c.d) === null || _this === void 0 ? true : delete _this.e;
|
||||
delete ((_o4_b2 = o4.b) === null || _o4_b2 === void 0 ? void 0 : (_o4_b_c_d1 = _o4_b2.c.d) === null || _o4_b_c_d1 === void 0 ? void 0 : _o4_b_c_d1.e);
|
||||
(_o4_b1 = o4.b) === null || _o4_b1 === void 0 ? true : (_o4_b_c_d1 = _o4_b1.c.d) === null || _o4_b_c_d1 === void 0 ? true : delete _o4_b_c_d1.e;
|
||||
(_o4_b2 = o4.b) === null || _o4_b2 === void 0 ? true : (_o4_b_c_d2 = _o4_b2.c.d) === null || _o4_b_c_d2 === void 0 ? true : delete _o4_b_c_d2.e;
|
||||
(_o5_b = o5.b) === null || _o5_b === void 0 ? true : (_o5_b_c_d = _o5_b.call(o5).c.d) === null || _o5_b_c_d === void 0 ? true : delete _o5_b_c_d.e;
|
||||
delete ((_o5_b1 = o5.b) === null || _o5_b1 === void 0 ? void 0 : (_o5_b_c_d1 = _o5_b1.call(o5).c.d) === null || _o5_b_c_d1 === void 0 ? void 0 : _o5_b_c_d1.e);
|
||||
(_o5_b1 = o5.b) === null || _o5_b1 === void 0 ? true : (_o5_b_c_d1 = _o5_b1.call(o5).c.d) === null || _o5_b_c_d1 === void 0 ? true : delete _o5_b_c_d1.e;
|
||||
(_o6_b = o6.b) === null || _o6_b === void 0 ? true : (_o6_b_c_d = _o6_b["c"].d) === null || _o6_b_c_d === void 0 ? true : delete _o6_b_c_d["e"];
|
||||
delete ((_o6_b1 = o6.b) === null || _o6_b1 === void 0 ? void 0 : (_o6_b_c_d1 = _o6_b1["c"].d) === null || _o6_b_c_d1 === void 0 ? void 0 : _o6_b_c_d1["e"]);
|
||||
(_o6_b1 = o6.b) === null || _o6_b1 === void 0 ? true : (_o6_b_c_d1 = _o6_b1["c"].d) === null || _o6_b_c_d1 === void 0 ? true : delete _o6_b_c_d1["e"];
|
||||
|
@ -1,3 +1,3 @@
|
||||
//// [deleteChain.ts]
|
||||
var _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _this, _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 = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || delete _o4_b_c_d.e, null === (_this = null === (_o4_b1 = o4.b) || void 0 === _o4_b1 ? void 0 : _o4_b1.c.d) || void 0 === _this || delete _this.e, delete (null === (_o4_b2 = o4.b) || void 0 === _o4_b2 ? void 0 : null === (_o4_b_c_d1 = _o4_b2.c.d) || void 0 === _o4_b_c_d1 ? void 0 : _o4_b_c_d1.e), null === (_o5_b = o5.b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(o5).c.d) || void 0 === _o5_b_c_d || delete _o5_b_c_d.e, delete (null === (_o5_b1 = o5.b) || void 0 === _o5_b1 ? void 0 : null === (_o5_b_c_d1 = _o5_b1.call(o5).c.d) || void 0 === _o5_b_c_d1 ? void 0 : _o5_b_c_d1.e), null === (_o6_b = o6.b) || void 0 === _o6_b || null === (_o6_b_c_d = _o6_b.c.d) || void 0 === _o6_b_c_d || delete _o6_b_c_d.e, delete (null === (_o6_b1 = o6.b) || void 0 === _o6_b1 ? void 0 : null === (_o6_b_c_d1 = _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, _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, null == o1 || delete 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, null === (_o3_b1 = o3.b) || void 0 === _o3_b1 || delete _o3_b1.c, null === (_o4_b = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || delete _o4_b_c_d.e, null === (_o4_b1 = o4.b) || void 0 === _o4_b1 || null === (_o4_b_c_d1 = _o4_b1.c.d) || void 0 === _o4_b_c_d1 || delete _o4_b_c_d1.e, null === (_o4_b2 = o4.b) || void 0 === _o4_b2 || null === (_o4_b_c_d2 = _o4_b2.c.d) || void 0 === _o4_b_c_d2 || delete _o4_b_c_d2.e, null === (_o5_b = o5.b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(o5).c.d) || void 0 === _o5_b_c_d || delete _o5_b_c_d.e, null === (_o5_b1 = o5.b) || void 0 === _o5_b1 || null === (_o5_b_c_d1 = _o5_b1.call(o5).c.d) || void 0 === _o5_b_c_d1 || delete _o5_b_c_d1.e, null === (_o6_b = o6.b) || void 0 === _o6_b || null === (_o6_b_c_d = _o6_b.c.d) || void 0 === _o6_b_c_d || delete _o6_b_c_d.e, null === (_o6_b1 = o6.b) || void 0 === _o6_b1 || null === (_o6_b_c_d1 = _o6_b1.c.d) || void 0 === _o6_b_c_d1 || delete _o6_b_c_d1.e;
|
||||
|
@ -11,7 +11,7 @@
|
||||
//// [index.js]
|
||||
/**
|
||||
* @typedef {import("./externs")} Foo
|
||||
*/ let a = /** @type {Foo} */ /** @type {*} */ undefined;
|
||||
*/ let a = /** @type {*} */ undefined;
|
||||
a = new Foo({
|
||||
doer: Foo.Bar
|
||||
});
|
||||
|
@ -8,8 +8,8 @@ const foo = new Foo();
|
||||
(_foo_m1 = foo.m) === null || _foo_m1 === void 0 ? void 0 : _foo_m1.call(foo);
|
||||
(_foo_m2 = foo.m) === null || _foo_m2 === void 0 ? void 0 : _foo_m2.call(foo);
|
||||
(_foo_m3 = foo.m) === null || _foo_m3 === void 0 ? void 0 : _foo_m3.call(foo);
|
||||
// https://github.com/microsoft/TypeScript/issues/50148
|
||||
(foo === null || foo === void 0 ? void 0 : foo.m).length;
|
||||
(// https://github.com/microsoft/TypeScript/issues/50148
|
||||
foo === null || foo === void 0 ? void 0 : foo.m).length;
|
||||
(foo === null || foo === void 0 ? void 0 : foo.m).length;
|
||||
(foo === null || foo === void 0 ? void 0 : foo["m"]).length;
|
||||
(foo === null || foo === void 0 ? void 0 : foo["m"]).length;
|
||||
|
@ -2,4 +2,4 @@
|
||||
const foo = new class {
|
||||
m() {}
|
||||
}();
|
||||
foo.m?.(), foo.m?.(), foo.m?.(), foo.m?.(), foo?.m.length, foo?.m.length, foo?.m.length, foo?.m.length;
|
||||
foo.m?.(), foo.m?.(), foo.m?.(), foo.m?.(), (foo?.m).length, (foo?.m).length, (foo?.m).length, (foo?.m).length;
|
||||
|
@ -334,10 +334,6 @@ impl VisitMut for Pure<'_> {
|
||||
}
|
||||
|
||||
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||
if let Expr::Paren(p) = e {
|
||||
*e = *p.expr.take();
|
||||
}
|
||||
|
||||
{
|
||||
let ctx = Ctx {
|
||||
in_first_expr: false,
|
||||
|
@ -97,7 +97,13 @@ impl VisitMut for InfoMarker<'_> {
|
||||
fn visit_mut_call_expr(&mut self, n: &mut CallExpr) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if has_noinline(self.comments, n.span) {
|
||||
// TODO: remove after we figure out how to move comments properly
|
||||
if has_noinline(self.comments, n.span)
|
||||
|| match &n.callee {
|
||||
Callee::Expr(e) => has_noinline(self.comments, e.span()),
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
n.span = n.span.apply_mark(self.marks.noinline);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ use crate::HEAVY_TASK_PARALLELS;
|
||||
/// Optimizer invoked before invoking compressor.
|
||||
///
|
||||
/// - Remove parens.
|
||||
/// TODO: remove completely after #8333
|
||||
pub(crate) fn precompress_optimizer<'a>() -> impl 'a + VisitMut {
|
||||
PrecompressOptimizer {}
|
||||
}
|
||||
@ -26,14 +27,6 @@ impl Parallel for PrecompressOptimizer {
|
||||
impl VisitMut for PrecompressOptimizer {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
if let Expr::Paren(p) = e {
|
||||
*e = *p.expr.take();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_pat_or_expr(&mut self, n: &mut PatOrExpr) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
|
@ -39,7 +39,11 @@ use swc_ecma_parser::{
|
||||
EsConfig, Parser, Syntax,
|
||||
};
|
||||
use swc_ecma_testing::{exec_node_js, JsExecOptions};
|
||||
use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver};
|
||||
use swc_ecma_transforms_base::{
|
||||
fixer::{fixer, paren_remover},
|
||||
hygiene::hygiene,
|
||||
resolver,
|
||||
};
|
||||
use swc_ecma_utils::drop_span;
|
||||
use swc_ecma_visit::{FoldWith, Visit, VisitMut, VisitMutWith, VisitWith};
|
||||
use testing::{assert_eq, unignore_fixture, DebugUsingDisplay, NormalizedOutput};
|
||||
@ -194,7 +198,12 @@ fn run(
|
||||
.map_err(|err| {
|
||||
err.into_diagnostic(handler).emit();
|
||||
})
|
||||
.map(|module| module.fold_with(&mut resolver(unresolved_mark, top_level_mark, false)));
|
||||
.map(|mut module| {
|
||||
module.visit_mut_with(&mut paren_remover(Some(&comments)));
|
||||
module.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
|
||||
|
||||
module
|
||||
});
|
||||
|
||||
// Ignore parser errors.
|
||||
//
|
||||
|
@ -0,0 +1 @@
|
||||
(obj?.a).b;
|
@ -0,0 +1 @@
|
||||
(obj?.a).b;
|
@ -32,7 +32,11 @@ use swc_ecma_parser::{
|
||||
lexer::{input::SourceFileInput, Lexer},
|
||||
EsConfig, Parser, Syntax,
|
||||
};
|
||||
use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver};
|
||||
use swc_ecma_transforms_base::{
|
||||
fixer::{fixer, paren_remover},
|
||||
hygiene::hygiene,
|
||||
resolver,
|
||||
};
|
||||
use swc_ecma_visit::{FoldWith, VisitMutWith};
|
||||
use testing::assert_eq;
|
||||
|
||||
@ -197,7 +201,12 @@ fn run(cm: Lrc<SourceMap>, handler: &Handler, input: &Path, config: &str) -> Opt
|
||||
.map_err(|err| {
|
||||
err.into_diagnostic(handler).emit();
|
||||
})
|
||||
.map(|module| module.fold_with(&mut resolver(unresolved_mark, top_level_mark, false)));
|
||||
.map(|mut module| {
|
||||
module.visit_mut_with(&mut paren_remover(Some(&comments)));
|
||||
module.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
|
||||
|
||||
module
|
||||
});
|
||||
|
||||
// Ignore parser errors.
|
||||
//
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{hash::BuildHasherDefault, ops::RangeFull};
|
||||
use std::{hash::BuildHasherDefault, mem, ops::RangeFull};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use rustc_hash::FxHasher;
|
||||
@ -18,6 +18,7 @@ pub fn fixer(comments: Option<&dyn Comments>) -> impl '_ + Fold + VisitMut {
|
||||
ctx: Default::default(),
|
||||
span_map: Default::default(),
|
||||
in_for_stmt_head: Default::default(),
|
||||
in_opt_chain: Default::default(),
|
||||
remove_only: false,
|
||||
})
|
||||
}
|
||||
@ -28,6 +29,7 @@ pub fn paren_remover(comments: Option<&dyn Comments>) -> impl '_ + Fold + VisitM
|
||||
ctx: Default::default(),
|
||||
span_map: Default::default(),
|
||||
in_for_stmt_head: Default::default(),
|
||||
in_opt_chain: Default::default(),
|
||||
remove_only: true,
|
||||
})
|
||||
}
|
||||
@ -42,6 +44,7 @@ struct Fixer<'a> {
|
||||
span_map: IndexMap<Span, Span, BuildHasherDefault<FxHasher>>,
|
||||
|
||||
in_for_stmt_head: bool,
|
||||
in_opt_chain: bool,
|
||||
|
||||
remove_only: bool,
|
||||
}
|
||||
@ -84,21 +87,6 @@ macro_rules! array {
|
||||
}
|
||||
|
||||
impl Fixer<'_> {
|
||||
fn visit_call<T: VisitMutWith<Self>>(
|
||||
&mut self,
|
||||
args: &mut Vec<ExprOrSpread>,
|
||||
callee: &mut T,
|
||||
) -> Context {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr;
|
||||
args.visit_mut_with(self);
|
||||
|
||||
self.ctx = Context::Callee { is_new: false };
|
||||
callee.visit_mut_with(self);
|
||||
|
||||
old
|
||||
}
|
||||
|
||||
fn wrap_callee(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Lit(Lit::Num(..) | Lit::Str(..)) => (),
|
||||
@ -397,24 +385,47 @@ impl VisitMut for Fixer<'_> {
|
||||
}
|
||||
|
||||
fn visit_mut_call_expr(&mut self, node: &mut CallExpr) {
|
||||
let old = self.visit_call(&mut node.args, &mut node.callee);
|
||||
let ctx = mem::replace(&mut self.ctx, Context::Callee { is_new: false });
|
||||
|
||||
node.callee.visit_mut_with(self);
|
||||
if let Callee::Expr(e) = &mut node.callee {
|
||||
if let Expr::OptChain(_) = &**e {
|
||||
self.wrap(e)
|
||||
} else {
|
||||
self.wrap_callee(e)
|
||||
match &**e {
|
||||
Expr::OptChain(_) if !self.in_opt_chain => self.wrap(e),
|
||||
_ => self.wrap_callee(e),
|
||||
}
|
||||
}
|
||||
|
||||
self.ctx = old;
|
||||
self.ctx = Context::ForcedExpr;
|
||||
|
||||
node.args.visit_mut_with(self);
|
||||
|
||||
self.ctx = ctx;
|
||||
}
|
||||
|
||||
fn visit_mut_opt_chain_base(&mut self, n: &mut OptChainBase) {
|
||||
if !n.is_member() {
|
||||
n.visit_mut_children_with(self);
|
||||
return;
|
||||
}
|
||||
|
||||
let in_opt_chain = mem::replace(&mut self.in_opt_chain, true);
|
||||
n.visit_mut_children_with(self);
|
||||
self.in_opt_chain = in_opt_chain;
|
||||
}
|
||||
|
||||
fn visit_mut_opt_call(&mut self, node: &mut OptCall) {
|
||||
let old = self.visit_call(&mut node.args, &mut node.callee);
|
||||
let ctx = mem::replace(&mut self.ctx, Context::Callee { is_new: false });
|
||||
let in_opt_chain = mem::replace(&mut self.in_opt_chain, true);
|
||||
|
||||
node.callee.visit_mut_with(self);
|
||||
self.wrap_callee(&mut node.callee);
|
||||
|
||||
self.ctx = old;
|
||||
self.in_opt_chain = in_opt_chain;
|
||||
|
||||
self.ctx = Context::ForcedExpr;
|
||||
node.args.visit_mut_with(self);
|
||||
|
||||
self.ctx = ctx;
|
||||
}
|
||||
|
||||
fn visit_mut_class(&mut self, node: &mut Class) {
|
||||
@ -471,6 +482,7 @@ impl VisitMut for Fixer<'_> {
|
||||
}
|
||||
}
|
||||
self.unwrap_expr(e);
|
||||
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
self.ctx = ctx;
|
||||
@ -575,32 +587,31 @@ impl VisitMut for Fixer<'_> {
|
||||
}
|
||||
|
||||
fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) {
|
||||
n.obj.visit_mut_with(self);
|
||||
n.prop.visit_mut_with(self);
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
match n {
|
||||
MemberExpr { obj, .. }
|
||||
if obj.is_object() && matches!(self.ctx, Context::ForcedExpr) => {}
|
||||
|
||||
MemberExpr { obj, .. }
|
||||
if obj.is_fn_expr()
|
||||
|| obj.is_cond()
|
||||
|| obj.is_unary()
|
||||
|| obj.is_seq()
|
||||
|| obj.is_update()
|
||||
|| obj.is_bin()
|
||||
|| obj.is_object()
|
||||
|| obj.is_assign()
|
||||
|| obj.is_arrow()
|
||||
|| obj.is_class()
|
||||
|| obj.is_yield_expr()
|
||||
|| obj.is_await_expr()
|
||||
|| (obj.is_call() && matches!(self.ctx, Context::Callee { is_new: true }))
|
||||
|| matches!(**obj, Expr::New(NewExpr { args: None, .. })) =>
|
||||
{
|
||||
self.wrap(obj);
|
||||
match *n.obj {
|
||||
Expr::Object(..) if self.ctx == Context::ForcedExpr => {}
|
||||
Expr::Fn(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Unary(..)
|
||||
| Expr::Seq(..)
|
||||
| Expr::Update(..)
|
||||
| Expr::Bin(..)
|
||||
| Expr::Object(..)
|
||||
| Expr::Assign(..)
|
||||
| Expr::Arrow(..)
|
||||
| Expr::Class(..)
|
||||
| Expr::Yield(..)
|
||||
| Expr::Await(..)
|
||||
| Expr::New(NewExpr { args: None, .. }) => {
|
||||
self.wrap(&mut n.obj);
|
||||
}
|
||||
Expr::Call(..) if self.ctx == Context::Callee { is_new: true } => {
|
||||
self.wrap(&mut n.obj);
|
||||
}
|
||||
Expr::OptChain(..) if !self.in_opt_chain => {
|
||||
self.wrap(&mut n.obj);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -691,7 +702,8 @@ impl VisitMut for Fixer<'_> {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
match &*e.tag {
|
||||
Expr::Arrow(..)
|
||||
Expr::OptChain(..)
|
||||
| Expr::Arrow(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Bin(..)
|
||||
| Expr::Seq(..)
|
||||
@ -857,17 +869,13 @@ impl Fixer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
let expr = Expr::Seq(SeqExpr { span: *span, exprs });
|
||||
let mut expr = Expr::Seq(SeqExpr { span: *span, exprs });
|
||||
|
||||
match self.ctx {
|
||||
Context::ForcedExpr => {
|
||||
*e = Expr::Paren(ParenExpr {
|
||||
span: *span,
|
||||
expr: Box::new(expr),
|
||||
})
|
||||
}
|
||||
_ => *e = expr,
|
||||
if let Context::ForcedExpr = self.ctx {
|
||||
self.wrap(&mut expr);
|
||||
};
|
||||
|
||||
*e = expr;
|
||||
}
|
||||
|
||||
Expr::Cond(expr) => {
|
||||
@ -907,10 +915,7 @@ impl Fixer<'_> {
|
||||
|| callee.is_await_expr()
|
||||
|| callee.is_assign() =>
|
||||
{
|
||||
*callee = Box::new(Expr::Paren(ParenExpr {
|
||||
span: callee.span(),
|
||||
expr: callee.take(),
|
||||
}))
|
||||
self.wrap(callee);
|
||||
}
|
||||
Expr::OptChain(OptChainExpr { base, .. }) => match &mut **base {
|
||||
OptChainBase::Call(OptCall { callee, .. })
|
||||
@ -919,10 +924,7 @@ impl Fixer<'_> {
|
||||
|| callee.is_await_expr()
|
||||
|| callee.is_assign() =>
|
||||
{
|
||||
*callee = Box::new(Expr::Paren(ParenExpr {
|
||||
span: callee.span(),
|
||||
expr: callee.take(),
|
||||
}))
|
||||
self.wrap(callee);
|
||||
}
|
||||
|
||||
OptChainBase::Call(OptCall { callee, .. }) if callee.is_fn_expr() => match self.ctx
|
||||
@ -974,51 +976,31 @@ impl Fixer<'_> {
|
||||
};
|
||||
|
||||
let expr = Box::new(e.take());
|
||||
*e = Expr::Paren(ParenExpr { expr, span })
|
||||
*e = Expr::Paren(ParenExpr { expr, span });
|
||||
}
|
||||
|
||||
/// Removes paren
|
||||
fn unwrap_expr(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Seq(SeqExpr { exprs, .. }) if exprs.len() == 1 => {
|
||||
self.unwrap_expr(exprs.last_mut().unwrap());
|
||||
*e = *exprs.last_mut().unwrap().take();
|
||||
}
|
||||
|
||||
Expr::Paren(ParenExpr {
|
||||
span: paren_span,
|
||||
expr,
|
||||
..
|
||||
}) => {
|
||||
// `(a?.b).c !== a?.b.c`
|
||||
if expr.is_opt_chain() {
|
||||
return;
|
||||
loop {
|
||||
match e {
|
||||
Expr::Seq(SeqExpr { exprs, .. }) if exprs.len() == 1 => {
|
||||
*e = *exprs[0].take();
|
||||
}
|
||||
|
||||
// `(a?.b.c)() !== a?.b.c()`
|
||||
if Self::contain_opt_chain(expr) {
|
||||
return;
|
||||
Expr::Paren(ParenExpr {
|
||||
span: paren_span,
|
||||
expr,
|
||||
..
|
||||
}) => {
|
||||
let expr_span = expr.span();
|
||||
let paren_span = *paren_span;
|
||||
*e = *expr.take();
|
||||
|
||||
self.span_map.insert(expr_span, paren_span);
|
||||
}
|
||||
|
||||
let expr_span = expr.span();
|
||||
let paren_span = *paren_span;
|
||||
self.unwrap_expr(expr);
|
||||
*e = *expr.take();
|
||||
|
||||
self.span_map.insert(expr_span, paren_span);
|
||||
_ => return,
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use swc_common::{chain, Mark, SyntaxContext};
|
||||
use swc_ecma_transforms_base::resolver;
|
||||
use swc_ecma_transforms_base::{fixer::paren_remover, resolver};
|
||||
use swc_ecma_utils::ExprCtx;
|
||||
use swc_ecma_visit::as_folder;
|
||||
|
||||
@ -15,6 +15,7 @@ macro_rules! test_stmt {
|
||||
|
||||
chain!(
|
||||
resolver(unresolved_mark, top_level_mark, false),
|
||||
paren_remover(None),
|
||||
expr_simplifier(top_level_mark, Default::default()),
|
||||
as_folder(super::Remover {
|
||||
changed: false,
|
||||
|
@ -1278,15 +1278,8 @@ impl VisitMut for SimplifyExpr {
|
||||
|
||||
match expr {
|
||||
// Do nothing.
|
||||
Expr::Lit(_) | Expr::This(..) => return,
|
||||
|
||||
// Remove parenthesis. This may break ast, but it will be fixed up later.
|
||||
Expr::Paren(ParenExpr { expr: e, .. }) => {
|
||||
self.changed = true;
|
||||
|
||||
*expr = *e.take();
|
||||
return;
|
||||
}
|
||||
// Note: Paren should be handled in fixer
|
||||
Expr::Lit(_) | Expr::This(..) | Expr::Paren(..) => return,
|
||||
|
||||
Expr::Seq(seq) if seq.exprs.is_empty() => return,
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use swc_common::{chain, Mark, SyntaxContext};
|
||||
use swc_ecma_transforms_base::resolver;
|
||||
use swc_ecma_transforms_base::{fixer::paren_remover, resolver};
|
||||
use swc_ecma_transforms_testing::test_transform;
|
||||
use swc_ecma_utils::ExprCtx;
|
||||
use swc_ecma_visit::as_folder;
|
||||
@ -15,6 +15,7 @@ fn fold(src: &str, expected: &str) {
|
||||
|
||||
chain!(
|
||||
resolver(unresolved_mark, top_level_mark, false),
|
||||
paren_remover(None),
|
||||
as_folder(SimplifyExpr {
|
||||
expr_ctx: ExprCtx {
|
||||
unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
|
||||
|
@ -2032,6 +2032,11 @@ impl Minifier<'_> {
|
||||
&mut swc_ecma_transforms_base::resolver(unresolved_mark, top_level_mark, false),
|
||||
);
|
||||
|
||||
let program = swc_ecma_visit::FoldWith::fold_with(
|
||||
program,
|
||||
&mut swc_ecma_transforms_base::fixer::paren_remover(Some(&comments)),
|
||||
);
|
||||
|
||||
let program = swc_ecma_minifier::optimize(
|
||||
program,
|
||||
cm.clone(),
|
||||
|
Loading…
Reference in New Issue
Block a user