fix(es/fixer): Preserve parenthesis for optional chaining (#8399)

**Related issue:**
 - Closes #8398
This commit is contained in:
magic-akari 2023-12-11 11:05:58 +08:00 committed by GitHub
parent 3b845c33b3
commit a69f172aac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 170 additions and 145 deletions

View File

@ -0,0 +1,15 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"target": "es2022",
"minify": {
"compress": true
},
"loose": false
},
"isModule": true,
"minify": true
}

View 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?.())));

View File

@ -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?.();

View File

@ -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

View File

@ -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"];

View File

@ -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;

View File

@ -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
});

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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.
//

View File

@ -0,0 +1 @@
(obj?.a).b;

View File

@ -0,0 +1 @@
(obj?.a).b;

View File

@ -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.
//

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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(),