swc_ecma_minifier/compress/optimize/
mod.rs

1#![allow(clippy::collapsible_match)]
2
3use std::iter::once;
4
5use bitflags::bitflags;
6use rustc_hash::{FxHashMap, FxHashSet};
7use swc_atoms::{Atom, Wtf8Atom};
8use swc_common::{pass::Repeated, util::take::Take, Spanned, SyntaxContext, DUMMY_SP};
9use swc_ecma_ast::*;
10use swc_ecma_transforms_base::rename::contains_eval;
11use swc_ecma_transforms_optimization::debug_assert_valid;
12use swc_ecma_usage_analyzer::{analyzer::UsageAnalyzer, marks::Marks};
13use swc_ecma_utils::{
14    prepend_stmts, ExprCtx, ExprExt, ExprFactory, IdentUsageFinder, IsEmpty, ModuleItemLike,
15    StmtLike, Type, Value,
16};
17use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
18#[cfg(feature = "debug")]
19use tracing::{span, Level};
20use Value::Known;
21
22use self::{
23    unused::PropertyAccessOpts,
24    util::{extract_class_side_effect, Finalizer, NormalMultiReplacer, SynthesizedStmts},
25};
26use super::util::{drop_invalid_stmts, is_fine_for_if_cons};
27#[cfg(feature = "debug")]
28use crate::debug::dump;
29use crate::{
30    compress::{optimize::util::get_ids_of_pat, util::is_pure_undefined},
31    debug::AssertValid,
32    maybe_par,
33    mode::Mode,
34    option::{CompressOptions, MangleOptions},
35    program_data::{ProgramData, ScopeData, VarUsageInfoFlags},
36    util::{contains_leaping_continue_with_label, make_number, ExprOptExt, ModuleItemExt},
37};
38
39mod arguments;
40mod bools;
41mod conditionals;
42mod dead_code;
43mod evaluate;
44mod if_return;
45mod iife;
46mod inline;
47mod loops;
48mod ops;
49mod props;
50mod rest_params;
51mod sequences;
52mod strings;
53mod unused;
54mod util;
55
56/// This pass is similar to `node.optimize` of terser.
57pub(super) fn optimizer<'a>(
58    marks: Marks,
59    options: &'a CompressOptions,
60    mangle_options: Option<&'a MangleOptions>,
61    data: &'a mut ProgramData,
62    mode: &'a dyn Mode,
63) -> impl 'a + VisitMut + Repeated {
64    assert!(
65        options.top_retain.iter().all(|s| s.trim() != ""),
66        "top_retain should not contain empty string"
67    );
68
69    let ctx = Ctx {
70        expr_ctx: ExprCtx {
71            unresolved_ctxt: SyntaxContext::empty().apply_mark(marks.unresolved_mark),
72            is_unresolved_ref_safe: false,
73            in_strict: options.module,
74            remaining_depth: 6,
75        },
76        scope: SyntaxContext::default(),
77        bit_ctx: BitCtx::default(),
78    };
79
80    Optimizer {
81        marks,
82        changed: false,
83        is_module: false,
84        options,
85        mangle_options,
86        prepend_stmts: Default::default(),
87        append_stmts: Default::default(),
88        vars: Default::default(),
89        typeofs: Default::default(),
90        data,
91        ctx,
92        mode,
93        functions: Default::default(),
94    }
95}
96
97/// Syntactic context.
98///
99/// This should not be modified directly. Use `.with_ctx()` instead.
100#[derive(Debug, Clone)]
101struct Ctx {
102    expr_ctx: ExprCtx,
103
104    /// Current scope.
105    scope: SyntaxContext,
106
107    bit_ctx: BitCtx,
108}
109
110impl Ctx {
111    #[inline]
112    pub fn with(mut self, flags: BitCtx, value: bool) -> Self {
113        self.bit_ctx = self.bit_ctx.with(flags, value);
114        self
115    }
116}
117bitflags! {
118    #[derive(Debug, Clone, Copy, Default)]
119    pub(crate) struct BitCtx: u32 {
120        /// `true` if the [VarDecl] has const annotation.
121        const IsConst = 1 << 0;
122        const IsVar = 1 << 1;
123        const IsLet = 1 << 2;
124
125        const DontUsePrependNorAppend = 1 << 3;
126
127        const InBoolCtx = 1 << 4;
128
129        const InAsm = 1 << 5;
130
131        /// `true` only for [Callee::Expr].
132        const IsCallee = 1 << 6;
133
134        /// `true` if we are try block. `true` means we cannot be sure about control
135        /// flow.
136        const InTryBlock = 1 << 7;
137
138        /// `true` while handling `test` of if / while / for.
139        const InCond = 1 << 8;
140
141        /// `true` if we are in `arg` of `delete arg`.
142        const IsDeleteArg = 1 << 9;
143
144        /// `true` if we are in `arg` of `++arg` or `--arg`.
145        const IsUpdateArg = 1 << 10;
146
147        const IsLhsOfAssign = 1 << 11;
148
149        /// `false` for `d` in `d[0] = foo`.
150        const IsExactLhsOfAssign = 1 << 12;
151
152        /// `true` for loop bodies and conditions of loops.
153        const ExecutedMultipleTime = 1 << 13;
154
155        /// `true` while handling `expr` of `!expr`
156        const InBangArg = 1 << 14;
157
158        const InVarDeclOfForInOrOfLoop = 1 << 15;
159
160        const DontUseNegatedIife = 1 << 16;
161
162        /// `true` while handling top-level export decls.
163        const IsExported = 1 << 17;
164
165        /// `true` while handling top level items.
166        const TopLevel = 1 << 18;
167
168        /// `true` while we are in a function or something similar.
169        const InFnLike = 1 << 19;
170
171        const InBlock = 1 << 20;
172
173        const InObjOfNonComputedMember = 1 << 21;
174
175        const InTplExpr = 1 << 22;
176
177        /// True while handling callee, except an arrow expression in callee.
178        const IsThisAwareCallee = 1 << 23;
179
180        const IsNestedIfReturnMerging = 1 << 24;
181
182        const DontInvokeIife = 1 << 25;
183
184        const InWithStmt = 1 << 26;
185
186        const InParam = 1 << 27;
187
188        /// `true` while we are inside a class body.
189        const InClass = 1 << 28;
190    }
191}
192
193impl BitCtx {
194    #[inline]
195    fn with(mut self, flags: Self, value: bool) -> Self {
196        self.set(flags, value);
197        self
198    }
199}
200
201impl Ctx {
202    pub fn is_top_level_for_block_level_vars(&self) -> bool {
203        if !self.bit_ctx.contains(BitCtx::TopLevel) {
204            return false;
205        }
206
207        if self.bit_ctx.intersects(BitCtx::InFnLike | BitCtx::InBlock) {
208            return false;
209        }
210        true
211    }
212
213    pub fn in_top_level(&self) -> bool {
214        self.bit_ctx.contains(BitCtx::TopLevel) || !self.bit_ctx.contains(BitCtx::InFnLike)
215    }
216}
217
218struct Optimizer<'a> {
219    marks: Marks,
220
221    changed: bool,
222    is_module: bool,
223    options: &'a CompressOptions,
224    mangle_options: Option<&'a MangleOptions>,
225    /// Statements prepended to the current statement.
226    prepend_stmts: SynthesizedStmts,
227    /// Statements appended to the current statement.
228    append_stmts: SynthesizedStmts,
229
230    vars: Vars,
231
232    typeofs: Box<FxHashMap<Id, Atom>>,
233    /// This information is created by analyzing identifier usages.
234    ///
235    /// This is calculated multiple time, but only once per one
236    /// `visit_mut_module`.
237    data: &'a mut ProgramData,
238    ctx: Ctx,
239
240    mode: &'a dyn Mode,
241
242    functions: Box<FxHashMap<Id, FnMetadata>>,
243}
244
245#[derive(Default)]
246struct Vars {
247    /// Cheap to clone.
248    ///
249    /// Used for inlining.
250    lits: FxHashMap<Id, Box<Expr>>,
251
252    /// Used for `hoist_props`.
253    hoisted_props: Box<FxHashMap<(Id, Wtf8Atom), Ident>>,
254
255    /// Literals which are cheap to clone, but not sure if we can inline without
256    /// making output bigger.
257    ///
258    /// https://github.com/swc-project/swc/issues/4415
259    lits_for_cmp: FxHashMap<Id, Box<Expr>>,
260
261    /// This stores [Expr::Array] if all elements are literals.
262    lits_for_array_access: FxHashMap<Id, Box<Expr>>,
263
264    /// Used for copying functions.
265    ///
266    /// We use this to distinguish [Callee::Expr] from other [Expr]s.
267    simple_functions: FxHashMap<Id, Box<Expr>>,
268    vars_for_inlining: FxHashMap<Id, Box<Expr>>,
269
270    /// Variables which should be removed by [Finalizer] because of the order of
271    /// visit.
272    removed: FxHashSet<Id>,
273}
274
275impl Vars {
276    fn has_pending_inline_for(&self, id: &Id) -> bool {
277        self.lits.contains_key(id) || self.vars_for_inlining.contains_key(id)
278    }
279
280    /// Returns true if something is changed.
281    fn inline_with_multi_replacer<N>(&mut self, n: &mut N) -> bool
282    where
283        N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
284        N: for<'aa> VisitMutWith<Finalizer<'aa>>,
285    {
286        let mut changed = false;
287        if !self.simple_functions.is_empty()
288            || !self.lits.is_empty()
289            || !self.lits_for_cmp.is_empty()
290            || !self.lits_for_array_access.is_empty()
291            || !self.hoisted_props.is_empty()
292            || !self.removed.is_empty()
293        {
294            let mut v = Finalizer {
295                simple_functions: &self.simple_functions,
296                lits_for_cmp: &self.lits_for_cmp,
297                lits_for_array_access: &self.lits_for_array_access,
298                lits: &self.lits,
299                hoisted_props: &self.hoisted_props,
300                vars_to_remove: &self.removed,
301                changed: false,
302            };
303            n.visit_mut_with(&mut v);
304            changed |= v.changed;
305        }
306
307        if !self.vars_for_inlining.is_empty() {
308            let mut v = NormalMultiReplacer::new(&mut self.vars_for_inlining, true);
309            n.visit_mut_with(&mut v);
310            changed |= v.changed;
311        }
312
313        changed
314    }
315}
316
317impl Repeated for Optimizer<'_> {
318    fn changed(&self) -> bool {
319        self.changed
320    }
321
322    fn reset(&mut self) {
323        self.changed = false;
324    }
325}
326
327#[derive(Debug, Clone, Copy)]
328struct FnMetadata {
329    len: usize,
330}
331
332impl From<&Function> for FnMetadata {
333    fn from(f: &Function) -> Self {
334        FnMetadata {
335            len: f
336                .params
337                .iter()
338                .filter(|p| matches!(&p.pat, Pat::Ident(..) | Pat::Array(..) | Pat::Object(..)))
339                .count(),
340        }
341    }
342}
343
344impl Optimizer<'_> {
345    fn may_remove_ident(&self, id: &Ident) -> bool {
346        if self
347            .data
348            .vars
349            .get(&id.to_id())
350            .is_some_and(|v| v.flags.contains(VarUsageInfoFlags::EXPORTED))
351        {
352            return false;
353        }
354
355        if id.ctxt != self.marks.top_level_ctxt {
356            return true;
357        }
358
359        if self.options.top_level() {
360            return !self.options.top_retain.contains(&id.sym);
361        }
362
363        false
364    }
365
366    fn may_add_ident(&self) -> bool {
367        if self.ctx.in_top_level() && self.data.top.contains(ScopeData::HAS_EVAL_CALL) {
368            return false;
369        }
370
371        // in class field
372        if self.ctx.bit_ctx.contains(BitCtx::InClass)
373            && !self
374                .ctx
375                .bit_ctx
376                .intersects(BitCtx::InFnLike | BitCtx::InBlock)
377        {
378            return false;
379        }
380
381        if self.ctx.bit_ctx.contains(BitCtx::InParam) {
382            return false;
383        }
384
385        if self
386            .data
387            .scopes
388            .get(&self.ctx.scope)
389            .unwrap()
390            .contains(ScopeData::HAS_EVAL_CALL)
391        {
392            return false;
393        }
394
395        if !self.ctx.in_top_level() {
396            return true;
397        }
398
399        self.options.top_level()
400    }
401
402    fn ident_reserved(&self, sym: &Atom) -> bool {
403        if let Some(MangleOptions { reserved, .. }) = self.mangle_options {
404            reserved.contains(sym)
405        } else {
406            false
407        }
408    }
409
410    fn handle_stmts(&mut self, stmts: &mut Vec<Stmt>, will_terminate: bool) {
411        // Skip if `use asm` exists.
412        if maybe_par!(
413            stmts.iter().any(|stmt| match stmt.as_stmt() {
414                Some(Stmt::Expr(stmt)) => match &*stmt.expr {
415                    Expr::Lit(Lit::Str(Str { raw, .. })) => {
416                        matches!(raw, Some(value) if value == "\"use asm\"" || value == "'use asm'")
417                    }
418                    _ => false,
419                },
420                _ => false,
421            }),
422            *crate::LIGHT_TASK_PARALLELS
423        ) {
424            return;
425        }
426
427        self.with_ctx(self.ctx.clone()).inject_else(stmts);
428
429        self.with_ctx(self.ctx.clone())
430            .handle_stmt_likes(stmts, will_terminate);
431
432        drop_invalid_stmts(stmts);
433
434        if stmts.len() == 1 {
435            if let Stmt::Expr(ExprStmt { expr, .. }) = &stmts[0] {
436                if let Expr::Lit(Lit::Str(s)) = &**expr {
437                    if s.value == *"use strict" {
438                        stmts.clear();
439                    }
440                }
441            }
442        }
443
444        #[cfg(debug_assertions)]
445        {
446            stmts.visit_with(&mut AssertValid);
447        }
448    }
449
450    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
451    fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>, will_terminate: bool)
452    where
453        T: StmtLike + ModuleItemLike + ModuleItemExt + VisitMutWith<Self> + VisitWith<AssertValid>,
454        Vec<T>: VisitMutWith<Self> + VisitWith<UsageAnalyzer<ProgramData>> + VisitWith<AssertValid>,
455    {
456        let mut use_asm = false;
457        let prepend_stmts = self.prepend_stmts.take();
458        let append_stmts = self.append_stmts.take();
459
460        {
461            let mut child_ctx = self.ctx.clone();
462            let mut directive_count = 0;
463
464            if !stmts.is_empty() {
465                // TODO: Handle multiple directives.
466                if let Some(Stmt::Expr(ExprStmt { expr, .. })) = stmts[0].as_stmt() {
467                    if let Expr::Lit(Lit::Str(v)) = &**expr {
468                        directive_count += 1;
469
470                        match &v.raw {
471                            Some(value) if value == "\"use strict\"" || value == "'use strict'" => {
472                                child_ctx.expr_ctx.in_strict = true;
473                            }
474                            Some(value) if value == "\"use asm\"" || value == "'use asm'" => {
475                                child_ctx.bit_ctx.insert(BitCtx::InAsm);
476                                self.ctx.bit_ctx.insert(BitCtx::InAsm);
477                                use_asm = true;
478                            }
479                            _ => {}
480                        }
481                    }
482                }
483            }
484
485            let mut new = Vec::with_capacity(stmts.len() * 11 / 10);
486            for (i, mut stmt) in stmts.take().into_iter().enumerate() {
487                // debug_assert_eq!(self.prepend_stmts, Vec::new());
488                // debug_assert_eq!(self.append_stmts, Vec::new());
489
490                if i < directive_count {
491                    // Don't set in_strict for directive itself.
492                    stmt.visit_mut_with(self);
493                } else {
494                    let child_optimizer = &mut *self.with_ctx(child_ctx.clone());
495                    stmt.visit_mut_with(child_optimizer);
496                }
497
498                #[cfg(debug_assertions)]
499                {
500                    stmt.visit_with(&mut AssertValid);
501                }
502
503                new.extend(self.prepend_stmts.drain(..).map(T::from));
504
505                match stmt.try_into_stmt() {
506                    Ok(Stmt::Block(s)) if s.ctxt.has_mark(self.marks.fake_block) => {
507                        new.extend(s.stmts.into_iter().map(T::from));
508                    }
509                    Ok(s) => {
510                        new.push(T::from(s));
511                    }
512                    Err(stmt) => {
513                        new.push(stmt);
514                    }
515                }
516
517                new.extend(self.append_stmts.drain(..).map(T::from));
518            }
519            *stmts = new;
520        }
521
522        if use_asm {
523            self.ctx.bit_ctx.insert(BitCtx::InAsm);
524        }
525
526        #[cfg(debug_assertions)]
527        {
528            stmts.visit_with(&mut AssertValid);
529        }
530
531        self.merge_sequences_in_stmts(stmts, will_terminate);
532
533        #[cfg(debug_assertions)]
534        {
535            stmts.visit_with(&mut AssertValid);
536        }
537
538        self.merge_similar_ifs(stmts);
539
540        #[cfg(debug_assertions)]
541        {
542            stmts.visit_with(&mut AssertValid);
543        }
544
545        self.make_sequences(stmts);
546
547        #[cfg(debug_assertions)]
548        {
549            stmts.visit_with(&mut AssertValid);
550        }
551
552        self.drop_else_token(stmts);
553
554        #[cfg(debug_assertions)]
555        {
556            stmts.visit_with(&mut AssertValid);
557        }
558
559        // stmts.extend(self.append_stmts.drain(..).map(T::from));
560
561        drop_invalid_stmts(stmts);
562
563        // debug_assert_eq!(self.prepend_stmts, Vec::new());
564        self.prepend_stmts = prepend_stmts;
565        self.append_stmts = append_stmts;
566    }
567
568    ///
569    /// - `undefined` => `void 0`
570    fn compress_undefined(&mut self, e: &mut Expr) {
571        if let Expr::Ident(Ident { span, sym, .. }) = e {
572            if &**sym == "undefined" {
573                *e = *Expr::undefined(*span);
574            }
575        }
576    }
577
578    ///
579    /// - `true` => `!1`
580    /// - `false` => `!0`
581    fn compress_lits(&mut self, e: &mut Expr) {
582        let lit = match e {
583            Expr::Lit(lit) => lit,
584            _ => return,
585        };
586
587        if self.options.bools_as_ints || self.options.bools {
588            if let Lit::Bool(v) = lit {
589                self.changed = true;
590                report_change!("Compressing boolean literal");
591                *e = UnaryExpr {
592                    span: v.span,
593                    op: op!("!"),
594                    arg: Lit::Num(Number {
595                        span: v.span,
596                        value: if v.value { 0.0 } else { 1.0 },
597                        raw: None,
598                    })
599                    .into(),
600                }
601                .into();
602            }
603        }
604    }
605
606    /// This function will be costly if the expr is a very long binary expr.
607    /// Call it only when necessary.
608    /// See also compress::pure::misc::remove_invalid
609    fn remove_invalid_bin(&mut self, e: &mut Expr) {
610        if let Expr::Bin(BinExpr { left, right, .. }) = e {
611            self.remove_invalid_bin(left);
612            self.remove_invalid_bin(right);
613
614            if left.is_invalid() {
615                *e = *right.take();
616                self.remove_invalid_bin(e);
617            } else if right.is_invalid() {
618                *e = *left.take();
619                self.remove_invalid_bin(e);
620            }
621        }
622    }
623
624    /// Returns [None] if expression is side-effect-free.
625    /// If an expression has a side effect, only side effects are returned.
626    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
627    fn ignore_return_value(&mut self, e: &mut Expr) -> Option<Expr> {
628        self.compress_cond_to_logical_ignoring_return_value(e);
629
630        self.drop_unused_update(e);
631
632        self.drop_unused_op_assign(e);
633
634        match e {
635            Expr::This(_) | Expr::Invalid(_) | Expr::Lit(..) => {
636                report_change!(
637                    "ignore_return_value: Dropping unused expr: {}",
638                    dump(&*e, false)
639                );
640                // We don't need to run this again
641                // self.changed = true;
642                return None;
643            }
644
645            Expr::Tpl(t) if t.exprs.is_empty() => {
646                report_change!("ignore_return_value: Dropping tpl expr without expr");
647                self.changed = true;
648                return None;
649            }
650
651            // Function expression cannot have a side effect.
652            Expr::Fn(_) => {
653                report_change!(
654                    "ignore_return_value: Dropping unused fn expr as it does not have any side \
655                     effect"
656                );
657                self.changed = true;
658                return None;
659            }
660
661            Expr::Class(cls) => {
662                // Do not remove class if it's self-referencing
663                if let Some(id) = &cls.ident {
664                    if IdentUsageFinder::find(id, &cls.class.body) {
665                        return Some(cls.take().into());
666                    }
667                }
668
669                if cls
670                    .class
671                    .body
672                    .iter()
673                    .any(|m| m.as_static_block().iter().any(|s| !s.body.is_empty()))
674                {
675                    // there's nothing we can do about it
676                    return Some(cls.take().into());
677                }
678
679                let Some(side_effects) =
680                    extract_class_side_effect(self.ctx.expr_ctx, &mut cls.class)
681                else {
682                    return Some(cls.take().into());
683                };
684
685                report_change!(
686                    "ignore_return_value: Dropping unused class expr as it does not have any side \
687                     effect"
688                );
689                self.changed = true;
690
691                let side_effects: Vec<Box<Expr>> = side_effects
692                    .into_iter()
693                    .filter_map(|e| self.ignore_return_value(e))
694                    .map(Box::new)
695                    .collect();
696
697                if side_effects.is_empty() {
698                    return None;
699                }
700
701                return Some(
702                    SeqExpr {
703                        span: cls.class.span,
704                        exprs: side_effects,
705                    }
706                    .into(),
707                );
708            }
709
710            Expr::Paren(e) => return self.ignore_return_value(&mut e.expr),
711
712            Expr::Bin(BinExpr {
713                op, left, right, ..
714            }) if op.may_short_circuit() => {
715                let ctx = self.ctx.clone().with(
716                    BitCtx::DontUseNegatedIife,
717                    self.ctx.bit_ctx.contains(BitCtx::DontUseNegatedIife)
718                        || self.options.side_effects,
719                );
720                let new_r = self.with_ctx(ctx).ignore_return_value(right);
721
722                match new_r {
723                    Some(r) => {
724                        *right = Box::new(r);
725                    }
726                    None => return self.ignore_return_value(left),
727                }
728
729                return Some(e.take());
730            }
731
732            Expr::Unary(UnaryExpr {
733                op: op!("delete"), ..
734            }) => return Some(e.take()),
735
736            Expr::Unary(UnaryExpr {
737                op: op!("void"), ..
738            }) if !self.options.unused => return Some(e.take()),
739
740            // We optimize binary expressions if operation is side-effect-free and lhs and rhs is
741            // evaluated regardless of value of lhs.
742            Expr::Bin(
743                bin @ BinExpr {
744                    op:
745                        op!(bin, "+")
746                        | op!(bin, "-")
747                        | op!("*")
748                        | op!("%")
749                        | op!("**")
750                        | op!("^")
751                        | op!("&")
752                        | op!("|")
753                        | op!(">>")
754                        | op!("<<")
755                        | op!(">>>")
756                        | op!("===")
757                        | op!("!==")
758                        | op!("==")
759                        | op!("!=")
760                        | op!("<")
761                        | op!("<=")
762                        | op!(">")
763                        | op!(">="),
764                    ..
765                },
766            ) => {
767                let left = self.ignore_return_value(&mut bin.left);
768                let right = self.ignore_return_value(&mut bin.right);
769                let span = bin.span;
770
771                if left.is_none() && right.is_none() {
772                    return None;
773                } else if right.is_none() {
774                    return left;
775                } else if left.is_none() {
776                    return right;
777                }
778
779                self.changed = true;
780                report_change!("ignore_return_value: Compressing binary as seq");
781                return Some(
782                    SeqExpr {
783                        span,
784                        exprs: vec![Box::new(left.unwrap()), Box::new(right.unwrap())],
785                    }
786                    .into(),
787                );
788            }
789
790            // Pure calls can be removed
791            Expr::Call(CallExpr {
792                callee: Callee::Expr(callee),
793                args,
794                ..
795            }) if match &**callee {
796                Expr::Fn(f) => f
797                    .function
798                    .body
799                    .as_ref()
800                    .map(|body| body.stmts.is_empty())
801                    .unwrap_or(false),
802                Expr::Arrow(f) => match &*f.body {
803                    BlockStmtOrExpr::BlockStmt(body) => body.stmts.is_empty(),
804                    BlockStmtOrExpr::Expr(_) => false,
805                    #[cfg(swc_ast_unknown)]
806                    _ => panic!("unable to access unknown nodes"),
807                },
808                _ => false,
809            } && args.is_empty() =>
810            {
811                report_change!("ignore_return_value: Dropping a pure call");
812                self.changed = true;
813                return None;
814            }
815
816            Expr::Call(CallExpr {
817                callee: Callee::Expr(callee),
818                args,
819                ..
820            }) => {
821                if let Expr::Fn(FnExpr {
822                    ident: None,
823                    function,
824                }) = &mut **callee
825                {
826                    if args.is_empty() {
827                        for param in &mut function.params {
828                            self.drop_unused_param(&mut param.pat, true);
829                        }
830
831                        function.params.retain(|p| !p.pat.is_invalid());
832                    }
833                }
834
835                if args.is_empty() {
836                    if let Expr::Fn(f) = &mut **callee {
837                        if f.function.body.is_empty() {
838                            return None;
839                        }
840                    }
841                }
842
843                if let Expr::Ident(callee) = &**callee {
844                    if self.options.reduce_vars && self.options.side_effects {
845                        if let Some(usage) = self.data.vars.get(&callee.to_id()) {
846                            if !usage.flags.contains(VarUsageInfoFlags::REASSIGNED)
847                                && usage.flags.contains(VarUsageInfoFlags::PURE_FN)
848                            {
849                                self.changed = true;
850                                report_change!("Reducing function call to a variable");
851
852                                if args.iter().any(|arg| arg.spread.is_some()) {
853                                    let elems = args
854                                        .take()
855                                        .into_iter()
856                                        .filter_map(|mut arg| {
857                                            if arg.spread.is_some() {
858                                                return Some(arg);
859                                            }
860                                            self.ignore_return_value(&mut arg.expr)
861                                                .map(Box::new)
862                                                .map(|expr| ExprOrSpread { expr, spread: None })
863                                        })
864                                        .map(Some)
865                                        .collect::<Vec<_>>();
866
867                                    if elems.is_empty() {
868                                        return None;
869                                    }
870
871                                    return Some(
872                                        ArrayLit {
873                                            span: callee.span,
874                                            elems,
875                                        }
876                                        .into(),
877                                    );
878                                }
879
880                                let args = args
881                                    .take()
882                                    .into_iter()
883                                    .filter_map(|mut arg| self.ignore_return_value(&mut arg.expr))
884                                    .map(Box::new)
885                                    .collect::<Vec<_>>();
886
887                                if args.is_empty() {
888                                    return None;
889                                }
890
891                                return Some(
892                                    SeqExpr {
893                                        span: callee.span,
894                                        exprs: args,
895                                    }
896                                    .into(),
897                                );
898                            }
899                        }
900                    }
901                }
902
903                return Some(e.take());
904            }
905
906            Expr::Assign(AssignExpr {
907                op: op!("="),
908                left: AssignTarget::Simple(SimpleAssignTarget::Ident(i)),
909                right,
910                ..
911            }) => {
912                let old = i.id.to_id();
913                self.store_var_for_inlining(&mut i.id, right, true);
914
915                if i.is_dummy() && self.options.unused {
916                    report_change!("inline: Removed variable ({}{:?})", old.0, old.1);
917                    self.vars.removed.insert(old);
918                }
919
920                if right.is_invalid() {
921                    return None;
922                }
923            }
924
925            Expr::Assign(AssignExpr {
926                op,
927                left: left @ AssignTarget::Simple(_),
928                right,
929                ..
930            }) if !op.may_short_circuit() => {
931                if let AssignTarget::Simple(expr) = left {
932                    if let SimpleAssignTarget::Member(m) = expr {
933                        if !m.obj.may_have_side_effects(self.ctx.expr_ctx)
934                            && (m.obj.is_object()
935                                || m.obj.is_fn_expr()
936                                || m.obj.is_arrow()
937                                || m.obj.is_class())
938                        {
939                            if self.should_preserve_property_access(
940                                &m.obj,
941                                PropertyAccessOpts {
942                                    allow_getter: true,
943                                    only_ident: false,
944                                },
945                            ) {
946                                return Some(e.take());
947                            } else {
948                                report_change!(
949                                    "ignore_return_value: Dropping unused assign target: {}",
950                                    dump(&*expr, false)
951                                );
952                                return Some(*right.take());
953                            }
954                        }
955                    }
956                }
957                return Some(e.take());
958            }
959
960            // We drop `f.g` in
961            //
962            // function f() {
963            //      return f.g, 1
964            // }
965            Expr::Member(MemberExpr { obj, prop, .. })
966                if !prop.is_computed()
967                    && (self.options.top_level() || !self.ctx.in_top_level()) =>
968            {
969                if self.should_preserve_property_access(
970                    obj,
971                    PropertyAccessOpts {
972                        allow_getter: true,
973                        only_ident: true,
974                    },
975                ) {
976                    return Some(e.take());
977                } else {
978                    return None;
979                }
980            }
981
982            // TODO: Check if it is a pure property access.
983            Expr::Member(_) => return Some(e.take()),
984
985            Expr::MetaProp(_)
986            | Expr::Await(_)
987            | Expr::New(..)
988            | Expr::Call(..)
989            | Expr::Yield(_)
990            | Expr::Assign(_)
991            | Expr::PrivateName(_)
992            | Expr::Update(_) => return Some(e.take()),
993
994            // Not supported. (At least at the moment)
995            Expr::JSXMember(_)
996            | Expr::JSXNamespacedName(_)
997            | Expr::JSXEmpty(_)
998            | Expr::JSXElement(_)
999            | Expr::JSXFragment(_)
1000            | Expr::TsTypeAssertion(_)
1001            | Expr::TsConstAssertion(_)
1002            | Expr::TsNonNull(_)
1003            | Expr::TsAs(_) => return Some(e.take()),
1004
1005            Expr::Array(arr) => {
1006                if arr.elems.iter().any(|e| match e {
1007                    Some(ExprOrSpread {
1008                        spread: Some(..), ..
1009                    }) => true,
1010                    _ => false,
1011                }) {
1012                    return Some(
1013                        ArrayLit {
1014                            elems: arr
1015                                .elems
1016                                .take()
1017                                .into_iter()
1018                                .flatten()
1019                                .filter_map(|mut e| {
1020                                    if e.spread.is_some() {
1021                                        return Some(e);
1022                                    }
1023
1024                                    self.ignore_return_value(&mut e.expr)
1025                                        .map(Box::new)
1026                                        .map(|expr| ExprOrSpread { expr, spread: None })
1027                                })
1028                                .map(Some)
1029                                .collect(),
1030                            ..*arr
1031                        }
1032                        .into(),
1033                    );
1034                }
1035
1036                let mut exprs = Vec::new();
1037                self.changed = true;
1038                report_change!("ignore_return_value: Inverting an array literal");
1039                exprs.extend(
1040                    arr.elems
1041                        .take()
1042                        .into_iter()
1043                        .flatten()
1044                        .map(|e| e.expr)
1045                        .filter_map(|mut e| self.ignore_return_value(&mut e))
1046                        .map(Box::new),
1047                );
1048
1049                if exprs.is_empty() {
1050                    return None;
1051                }
1052
1053                return Some(
1054                    SeqExpr {
1055                        span: arr.span,
1056                        exprs,
1057                    }
1058                    .into(),
1059                );
1060            }
1061
1062            Expr::Object(obj) => {
1063                let mut exprs = Vec::new();
1064                self.changed = true;
1065                report_change!("ignore_return_value: Inverting an object literal");
1066                for prop in obj.props.take() {
1067                    match prop {
1068                        PropOrSpread::Spread(mut e) => {
1069                            exprs.extend(self.ignore_return_value(&mut e.expr).map(Box::new));
1070                        }
1071                        PropOrSpread::Prop(prop) => match *prop {
1072                            Prop::KeyValue(KeyValueProp { key, mut value, .. }) => {
1073                                match key {
1074                                    PropName::Ident(_) => {}
1075                                    PropName::Str(_) => {}
1076                                    PropName::Num(_) => {}
1077                                    PropName::Computed(mut key) => {
1078                                        exprs.extend(
1079                                            self.ignore_return_value(&mut key.expr).map(Box::new),
1080                                        );
1081                                    }
1082                                    PropName::BigInt(_) => {}
1083                                    #[cfg(swc_ast_unknown)]
1084                                    _ => panic!("unable to access unknown nodes"),
1085                                }
1086
1087                                exprs.extend(self.ignore_return_value(&mut value).map(Box::new));
1088                            }
1089                            Prop::Getter(GetterProp { key, .. })
1090                            | Prop::Setter(SetterProp { key, .. })
1091                            | Prop::Method(MethodProp { key, .. }) => match key {
1092                                PropName::Ident(_) => {}
1093                                PropName::Str(_) => {}
1094                                PropName::Num(_) => {}
1095                                PropName::Computed(mut key) => {
1096                                    exprs.extend(
1097                                        self.ignore_return_value(&mut key.expr).map(Box::new),
1098                                    );
1099                                }
1100                                PropName::BigInt(_) => {}
1101                                #[cfg(swc_ast_unknown)]
1102                                _ => panic!("unable to access unknown nodes"),
1103                            },
1104
1105                            Prop::Assign(mut prop) => {
1106                                exprs.extend(
1107                                    self.ignore_return_value(&mut prop.value).map(Box::new),
1108                                );
1109                            }
1110
1111                            Prop::Shorthand(_) => {}
1112                            #[cfg(swc_ast_unknown)]
1113                            _ => panic!("unable to access unknown nodes"),
1114                        },
1115                        #[cfg(swc_ast_unknown)]
1116                        _ => panic!("unable to access unknown nodes"),
1117                    }
1118                }
1119
1120                if exprs.is_empty() {
1121                    return None;
1122                }
1123
1124                return Some(
1125                    SeqExpr {
1126                        span: obj.span,
1127                        exprs,
1128                    }
1129                    .into(),
1130                );
1131            }
1132
1133            // Preserves negated iife
1134            Expr::Unary(UnaryExpr {
1135                op: op!("!"), arg, ..
1136            }) if (self.options.negate_iife
1137                || self.options.reduce_vars
1138                || self.options.side_effects)
1139                && !self.ctx.bit_ctx.contains(BitCtx::DontUseNegatedIife)
1140                && match &**arg {
1141                    Expr::Call(arg) => match &arg.callee {
1142                        Callee::Expr(callee) => matches!(&**callee, Expr::Fn(..)),
1143                        _ => false,
1144                    },
1145                    _ => false,
1146                } =>
1147            {
1148                let processed_arg = self.ignore_return_value(arg)?;
1149
1150                *arg = Box::new(processed_arg);
1151
1152                log_abort!("ignore_return_value: Preserving negated iife");
1153                return Some(e.take());
1154            }
1155
1156            // `delete` is handled above
1157            Expr::Unary(expr) => {
1158                self.changed = true;
1159                report_change!("ignore_return_value: Reducing unary ({})", expr.op);
1160
1161                // We can ignore the identifier in case of typeof
1162                if expr.op == op!("typeof") && expr.arg.is_ident() {
1163                    return None;
1164                }
1165                return self.ignore_return_value(&mut expr.arg);
1166            }
1167
1168            Expr::Bin(BinExpr {
1169                span,
1170                left,
1171                right,
1172                #[cfg(feature = "debug")]
1173                op,
1174                ..
1175            }) => {
1176                report_change!("ignore_return_value: Reducing binary ({})", *op);
1177
1178                let left = self.ignore_return_value(left).map(Box::new);
1179                let right = self.ignore_return_value(right).map(Box::new);
1180
1181                let mut seq = SeqExpr {
1182                    span: *span,
1183                    exprs: left.into_iter().chain(right).collect(),
1184                }
1185                .into();
1186                return self.ignore_return_value(&mut seq);
1187            }
1188
1189            Expr::Cond(cond) => {
1190                trace_op!("ignore_return_value: Cond expr");
1191
1192                self.restore_negated_iife(cond);
1193
1194                let ctx = self.ctx.clone().with(
1195                    BitCtx::DontUseNegatedIife,
1196                    self.ctx.bit_ctx.contains(BitCtx::DontUseNegatedIife)
1197                        || self.options.side_effects,
1198                );
1199
1200                let cons_span = cond.cons.span();
1201                let alt_span = cond.alt.span();
1202                let cons = self
1203                    .with_ctx(ctx.clone())
1204                    .ignore_return_value(&mut cond.cons)
1205                    .map(Box::new);
1206                let alt = self
1207                    .with_ctx(ctx.clone())
1208                    .ignore_return_value(&mut cond.alt)
1209                    .map(Box::new);
1210
1211                // TODO: Remove if test is side effect free.
1212
1213                return Some(
1214                    CondExpr {
1215                        span: cond.span,
1216                        test: cond.test.take(),
1217                        cons: cons.unwrap_or_else(|| {
1218                            report_change!("ignore_return_value: Dropped `cons`");
1219                            self.changed = true;
1220                            Expr::undefined(cons_span)
1221                        }),
1222                        alt: alt.unwrap_or_else(|| {
1223                            report_change!("ignore_return_value: Dropped `alt`");
1224                            self.changed = true;
1225                            Expr::undefined(alt_span)
1226                        }),
1227                    }
1228                    .into(),
1229                );
1230            }
1231
1232            Expr::Seq(seq) => {
1233                if seq.exprs.is_empty() {
1234                    return None;
1235                }
1236                //
1237                let mut exprs = seq
1238                    .exprs
1239                    .iter_mut()
1240                    .enumerate()
1241                    .filter_map(|(idx, expr)| {
1242                        let is_injected_zero = match &**expr {
1243                            Expr::Lit(Lit::Num(v)) => v.span.is_dummy(),
1244                            _ => false,
1245                        };
1246
1247                        if idx == 0
1248                            && self.ctx.bit_ctx.contains(BitCtx::IsThisAwareCallee)
1249                            && is_injected_zero
1250                        {
1251                            return Some(*expr.take());
1252                        }
1253                        let ctx = self.ctx.clone().with(BitCtx::DontUseNegatedIife, idx != 0);
1254                        self.with_ctx(ctx).ignore_return_value(expr)
1255                    })
1256                    .map(Box::new)
1257                    .collect::<Vec<_>>();
1258                if exprs.len() <= 1 {
1259                    return exprs.pop().map(|v| *v);
1260                } else {
1261                    let is_last_undefined =
1262                        is_pure_undefined(self.ctx.expr_ctx, exprs.last().unwrap());
1263
1264                    // (foo(), void 0) => void foo()
1265                    if is_last_undefined {
1266                        self.changed = true;
1267                        // Remove `void 0`
1268                        exprs.pop();
1269
1270                        // Make return type undefined.
1271                        if let Some(last) = exprs.last_mut() {
1272                            report_change!("ignore_return_value: Shifting void");
1273                            self.changed = true;
1274                            *last = UnaryExpr {
1275                                span: DUMMY_SP,
1276                                op: op!("void"),
1277                                arg: last.take(),
1278                            }
1279                            .into();
1280                        }
1281                    }
1282
1283                    if exprs.is_empty() {
1284                        report_change!("ignore_return_value: Dropping empty seq");
1285                        return None;
1286                    }
1287
1288                    return Some(
1289                        SeqExpr {
1290                            span: seq.span,
1291                            exprs,
1292                        }
1293                        .into(),
1294                    );
1295                }
1296            }
1297
1298            Expr::Ident(id) if id.ctxt != self.ctx.expr_ctx.unresolved_ctxt => {
1299                report_change!("ignore_return_value: Dropping a declared ident {}", id);
1300                self.changed = true;
1301                return None;
1302            }
1303            _ => {}
1304        }
1305
1306        Some(e.take())
1307    }
1308
1309    fn try_removing_block(&mut self, s: &mut Stmt, allow_fn_decl: bool) {
1310        match s {
1311            Stmt::Block(bs) => {
1312                if bs.stmts.is_empty() {
1313                    report_change!("Converting empty block to empty statement");
1314                    *s = EmptyStmt { span: DUMMY_SP }.into();
1315                    return;
1316                }
1317
1318                // Remove nested blocks
1319                if bs.stmts.len() == 1 {
1320                    if let Stmt::Block(block) = &mut bs.stmts[0] {
1321                        let stmts = &block.stmts;
1322                        if maybe_par!(
1323                            stmts.iter().all(|stmt| !matches!(stmt, Stmt::Decl(..))),
1324                            *crate::LIGHT_TASK_PARALLELS
1325                        ) {
1326                            report_change!("optimizer: Removing nested block");
1327                            self.changed = true;
1328                            bs.stmts = block.stmts.take();
1329                        }
1330                    }
1331                }
1332
1333                for stmt in &mut bs.stmts {
1334                    if let Stmt::Block(block) = &stmt {
1335                        if block.stmts.is_empty() {
1336                            self.changed = true;
1337                            report_change!("optimizer: Removing empty block");
1338                            *stmt = EmptyStmt { span: DUMMY_SP }.into();
1339                            return;
1340                        }
1341                    }
1342                }
1343
1344                if bs.stmts.len() == 1 {
1345                    match &bs.stmts[0] {
1346                        Stmt::Expr(..) | Stmt::If(..) => {
1347                            *s = bs.stmts[0].take();
1348                            report_change!("optimizer: Unwrapping block stmt");
1349                            self.changed = true;
1350                        }
1351                        // Annex B the darkest part of JS
1352                        Stmt::Decl(Decl::Fn(f))
1353                            if allow_fn_decl
1354                                && !self.ctx.expr_ctx.in_strict
1355                                && !f.function.is_generator
1356                                && !f.function.is_async =>
1357                        {
1358                            *s = bs.stmts[0].take();
1359                            report_change!("optimizer: Unwrapping block stmt in non strcit mode");
1360                            self.changed = true;
1361                        }
1362                        Stmt::Decl(Decl::Var(v)) if v.kind == VarDeclKind::Var => {
1363                            report_change!("optimizer: Unwrapping a block with var decl statement");
1364                            self.changed = true;
1365                            *s = bs.stmts[0].take();
1366                            return;
1367                        }
1368                        Stmt::Decl(Decl::Class(_) | Decl::Var(_) | Decl::Fn(_)) => (),
1369                        _ => {
1370                            if bs.ctxt.has_mark(self.marks.fake_block) {
1371                                report_change!("Unwrapping a fake block");
1372                                *s = bs.stmts.take().into_iter().next().unwrap();
1373                                return;
1374                            }
1375                        }
1376                    }
1377                }
1378            }
1379
1380            Stmt::If(s) => {
1381                self.try_removing_block(&mut s.cons, true);
1382                let can_remove_block_of_alt = match &*s.cons {
1383                    Stmt::Expr(..) | Stmt::If(..) => true,
1384                    Stmt::Block(bs) if bs.stmts.len() == 1 => matches!(&bs.stmts[0], Stmt::For(..)),
1385                    _ => false,
1386                };
1387                if can_remove_block_of_alt {
1388                    if let Some(alt) = &mut s.alt {
1389                        self.try_removing_block(alt, false);
1390                    }
1391                }
1392            }
1393
1394            Stmt::ForIn(s) => {
1395                self.try_removing_block(&mut s.body, false);
1396            }
1397
1398            Stmt::For(s) => {
1399                self.try_removing_block(&mut s.body, false);
1400            }
1401
1402            Stmt::ForOf(s) => {
1403                self.try_removing_block(&mut s.body, false);
1404            }
1405
1406            Stmt::While(s) => {
1407                self.try_removing_block(&mut s.body, false);
1408            }
1409
1410            Stmt::DoWhile(s) => {
1411                self.try_removing_block(&mut s.body, false);
1412            }
1413
1414            _ => {}
1415        }
1416
1417        if !self.options.conditionals
1418            && !self.options.sequences()
1419            && !self.options.join_vars
1420            && !self.options.unused
1421        {
1422            return;
1423        }
1424
1425        match s {
1426            Stmt::Block(block) if block.stmts.is_empty() => {
1427                *s = EmptyStmt { span: block.span }.into();
1428            }
1429            Stmt::Block(block)
1430                if block.stmts.len() == 1 && is_fine_for_if_cons(&block.stmts[0]) =>
1431            {
1432                *s = block.stmts.take().into_iter().next().unwrap();
1433            }
1434            _ => {}
1435        }
1436    }
1437
1438    fn compress_if_without_alt(&mut self, s: &mut Stmt) {
1439        if !self.options.conditionals {
1440            return;
1441        }
1442
1443        let stmt = match s {
1444            Stmt::If(v) => v,
1445            _ => return,
1446        };
1447
1448        if stmt.alt.is_none() {
1449            if let Stmt::Expr(cons) = &mut *stmt.cons {
1450                self.changed = true;
1451                report_change!("Converting if statement to a form `test && cons`");
1452                *s = ExprStmt {
1453                    span: stmt.span,
1454                    expr: Box::new(stmt.test.take().make_bin(op!("&&"), *cons.expr.take())),
1455                }
1456                .into();
1457            }
1458        }
1459    }
1460
1461    fn visit_with_prepend<N>(&mut self, n: &mut N)
1462    where
1463        N: VisitMutWith<Self>,
1464    {
1465        let mut old_prepend_stmts = self.prepend_stmts.take();
1466        let old_append_stmts = self.append_stmts.take();
1467        n.visit_mut_with(self);
1468        old_prepend_stmts.append(&mut self.prepend_stmts);
1469        old_prepend_stmts.append(&mut self.append_stmts);
1470
1471        self.prepend_stmts = old_prepend_stmts;
1472        self.append_stmts = old_append_stmts;
1473    }
1474}
1475
1476impl VisitMut for Optimizer<'_> {
1477    noop_visit_mut_type!(fail);
1478
1479    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1480    fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
1481        self.drop_unused_arrow_params(&mut n.params);
1482
1483        let prepend = self.prepend_stmts.take();
1484
1485        {
1486            let ctx = self.ctx.clone().with(BitCtx::InParam, true);
1487            n.params.visit_mut_with(&mut *self.with_ctx(ctx));
1488        }
1489
1490        {
1491            let ctx = Ctx {
1492                bit_ctx: self
1493                    .ctx
1494                    .bit_ctx
1495                    .with(BitCtx::InFnLike, true)
1496                    .with(BitCtx::TopLevel, false),
1497                scope: n.ctxt,
1498                ..self.ctx.clone()
1499            };
1500            n.body.visit_mut_with(&mut *self.with_ctx(ctx));
1501        }
1502
1503        if !self.prepend_stmts.is_empty() {
1504            let mut stmts = self.prepend_stmts.take().take_stmts();
1505            match &mut *n.body {
1506                BlockStmtOrExpr::BlockStmt(v) => {
1507                    prepend_stmts(&mut v.stmts, stmts.into_iter());
1508                }
1509                BlockStmtOrExpr::Expr(v) => {
1510                    self.changed = true;
1511                    report_change!("Converting a body of an arrow expression to BlockStmt");
1512
1513                    stmts.push(
1514                        ReturnStmt {
1515                            span: DUMMY_SP,
1516                            arg: Some(v.take()),
1517                        }
1518                        .into(),
1519                    );
1520                    n.body = Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
1521                        span: DUMMY_SP,
1522                        stmts,
1523                        ..Default::default()
1524                    }));
1525                }
1526                #[cfg(swc_ast_unknown)]
1527                _ => panic!("unable to access unknown nodes"),
1528            }
1529        }
1530
1531        self.prepend_stmts = prepend;
1532
1533        if let BlockStmtOrExpr::BlockStmt(body) = &mut *n.body {
1534            drop_invalid_stmts(&mut body.stmts);
1535        }
1536    }
1537
1538    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1539    fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
1540        {
1541            let ctx = self
1542                .ctx
1543                .clone()
1544                .with(BitCtx::IsLhsOfAssign, true)
1545                .with(BitCtx::IsExactLhsOfAssign, true);
1546            e.left.visit_mut_with(&mut *self.with_ctx(ctx));
1547        }
1548        e.right.visit_mut_with(self);
1549    }
1550
1551    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1552    fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
1553        n.visit_mut_children_with(self);
1554
1555        if let Some(value) = &n.value {
1556            if is_pure_undefined(self.ctx.expr_ctx, value) {
1557                n.value = None;
1558            }
1559        }
1560    }
1561
1562    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1563    fn visit_mut_bin_expr(&mut self, n: &mut BinExpr) {
1564        {
1565            let ctx = self.ctx.clone().with(
1566                BitCtx::InCond,
1567                self.ctx.bit_ctx.contains(BitCtx::InCond) || n.op.may_short_circuit(),
1568            );
1569            n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1570        }
1571
1572        self.compress_typeof_undefined(n);
1573
1574        self.optimize_bin_equal(n);
1575
1576        self.remove_bin_paren(n);
1577
1578        self.optimize_optional_chain_generated(n);
1579
1580        self.optimize_bin_and_or(n);
1581
1582        if n.op == op!(bin, "+") {
1583            if let Known(Type::Str) = n.left.get_type(self.ctx.expr_ctx) {
1584                self.optimize_expr_in_str_ctx(&mut n.right);
1585            }
1586
1587            if let Known(Type::Str) = n.right.get_type(self.ctx.expr_ctx) {
1588                self.optimize_expr_in_str_ctx(&mut n.left);
1589            }
1590        }
1591    }
1592
1593    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1594    fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
1595        let ctx = Ctx {
1596            bit_ctx: self
1597                .ctx
1598                .bit_ctx
1599                .with(BitCtx::TopLevel, false)
1600                .with(BitCtx::InBlock, true)
1601                .with(BitCtx::InParam, false),
1602            scope: n.ctxt,
1603            ..self.ctx.clone()
1604        };
1605        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1606    }
1607
1608    fn visit_mut_block_stmt_or_expr(&mut self, n: &mut BlockStmtOrExpr) {
1609        n.visit_mut_children_with(self);
1610
1611        match n {
1612            BlockStmtOrExpr::BlockStmt(n) => {
1613                self.merge_if_returns(&mut n.stmts, false, true);
1614                self.drop_else_token(&mut n.stmts);
1615            }
1616            BlockStmtOrExpr::Expr(_) => {}
1617            #[cfg(swc_ast_unknown)]
1618            _ => panic!("unable to access unknown nodes"),
1619        }
1620    }
1621
1622    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1623    fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
1624        let is_this_undefined = match &e.callee {
1625            Callee::Super(_) | Callee::Import(_) => false,
1626            Callee::Expr(e) => e.is_ident(),
1627            #[cfg(swc_ast_unknown)]
1628            _ => panic!("unable to access unknown nodes"),
1629        };
1630        {
1631            let ctx = self
1632                .ctx
1633                .clone()
1634                .with(BitCtx::IsCallee, true)
1635                .with(
1636                    BitCtx::IsThisAwareCallee,
1637                    is_this_undefined
1638                        || match &e.callee {
1639                            Callee::Super(_) | Callee::Import(_) => false,
1640                            Callee::Expr(callee) => is_callee_this_aware(callee),
1641                            #[cfg(swc_ast_unknown)]
1642                            _ => panic!("unable to access unknown nodes"),
1643                        },
1644                )
1645                .with(BitCtx::IsLhsOfAssign, false)
1646                .with(BitCtx::IsExactLhsOfAssign, false)
1647                .with(BitCtx::IsUpdateArg, false);
1648            e.callee.visit_mut_with(&mut *self.with_ctx(ctx));
1649        }
1650
1651        if is_this_undefined {
1652            if let Callee::Expr(callee) = &mut e.callee {
1653                if let Expr::Member(..) = &mut **callee {
1654                    let zero = Lit::Num(Number {
1655                        span: DUMMY_SP,
1656                        value: 0.0,
1657                        raw: None,
1658                    })
1659                    .into();
1660                    self.changed = true;
1661                    report_change!("injecting zero to preserve `this` in call");
1662
1663                    *callee = SeqExpr {
1664                        span: callee.span(),
1665                        exprs: vec![zero, callee.take()],
1666                    }
1667                    .into();
1668                }
1669            }
1670        }
1671
1672        {
1673            let ctx = self
1674                .ctx
1675                .clone()
1676                .with(BitCtx::IsThisAwareCallee, false)
1677                .with(BitCtx::IsLhsOfAssign, false)
1678                .with(BitCtx::IsExactLhsOfAssign, false)
1679                .with(BitCtx::IsUpdateArg, false);
1680            // TODO: Prevent inline if callee is unknown.
1681            e.args.visit_mut_with(&mut *self.with_ctx(ctx));
1682        }
1683
1684        self.ignore_unused_args_of_iife(e);
1685        self.inline_args_of_iife(e);
1686    }
1687
1688    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1689    fn visit_mut_class(&mut self, n: &mut Class) {
1690        n.decorators.visit_mut_with(self);
1691
1692        {
1693            let ctx = self
1694                .ctx
1695                .clone()
1696                .with(BitCtx::DontInvokeIife, true)
1697                .with(BitCtx::IsUpdateArg, false);
1698            n.super_class.visit_mut_with(&mut *self.with_ctx(ctx));
1699        }
1700
1701        {
1702            let ctx = Ctx {
1703                bit_ctx: self.ctx.bit_ctx.with(BitCtx::IsUpdateArg, false),
1704                expr_ctx: ExprCtx {
1705                    in_strict: true,
1706                    ..self.ctx.clone().expr_ctx
1707                },
1708                ..self.ctx.clone()
1709            };
1710            n.body.visit_mut_with(&mut *self.with_ctx(ctx));
1711        }
1712    }
1713
1714    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1715    fn visit_mut_class_member(&mut self, n: &mut ClassMember) {
1716        let ctx = self
1717            .ctx
1718            .clone()
1719            .with(BitCtx::InFnLike, false)
1720            .with(BitCtx::InClass, true);
1721
1722        match n {
1723            ClassMember::ClassProp(class_prop) => {
1724                class_prop.key.visit_mut_with(self);
1725                class_prop.value.visit_mut_with(&mut *self.with_ctx(ctx));
1726            }
1727            ClassMember::Method(class_method) => {
1728                class_method.key.visit_mut_with(self);
1729                class_method
1730                    .function
1731                    .visit_mut_with(&mut *self.with_ctx(ctx));
1732            }
1733            ClassMember::AutoAccessor(auto_accessor) => {
1734                auto_accessor.key.visit_mut_with(self);
1735                auto_accessor.value.visit_mut_with(&mut *self.with_ctx(ctx));
1736            }
1737            ClassMember::PrivateProp(private_prop) => {
1738                private_prop.visit_mut_with(&mut *self.with_ctx(ctx));
1739            }
1740            ClassMember::PrivateMethod(private_method) => {
1741                private_method.visit_mut_with(&mut *self.with_ctx(ctx));
1742            }
1743
1744            _ => {
1745                n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1746            }
1747        }
1748    }
1749
1750    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1751    fn visit_mut_class_expr(&mut self, e: &mut ClassExpr) {
1752        if !self.options.keep_classnames {
1753            if e.ident.is_some() && !contains_eval(&e.class, true) {
1754                self.remove_name_if_not_used(&mut e.ident);
1755            }
1756        }
1757
1758        e.visit_mut_children_with(self);
1759    }
1760
1761    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1762    fn visit_mut_decl(&mut self, decl: &mut Decl) {
1763        match decl {
1764            Decl::Class(class_decl) => self.visit_mut_class(&mut class_decl.class),
1765            Decl::Fn(fn_decl) => self.visit_mut_fn_decl(fn_decl),
1766            Decl::Var(var_decl) => self.visit_mut_var_decl(var_decl),
1767            _ => decl.visit_mut_children_with(self),
1768        };
1769
1770        self.drop_unused_decl(decl);
1771        self.store_typeofs(decl);
1772        self.store_decl_for_inlining(decl);
1773    }
1774
1775    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1776    fn visit_mut_default_decl(&mut self, n: &mut DefaultDecl) {
1777        match n {
1778            DefaultDecl::Class(_) => {}
1779            DefaultDecl::Fn(f) => {
1780                self.drop_unused_params(&mut f.function.params);
1781            }
1782            DefaultDecl::TsInterfaceDecl(_) => {}
1783            #[cfg(swc_ast_unknown)]
1784            _ => panic!("unable to access unknown nodes"),
1785        }
1786
1787        n.visit_mut_children_with(self);
1788    }
1789
1790    fn visit_mut_do_while_stmt(&mut self, n: &mut DoWhileStmt) {
1791        {
1792            let ctx = self.ctx.clone().with(BitCtx::ExecutedMultipleTime, true);
1793            n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1794        }
1795    }
1796
1797    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1798    fn visit_mut_export_decl(&mut self, n: &mut ExportDecl) {
1799        if let Decl::Fn(f) = &mut n.decl {
1800            self.drop_unused_params(&mut f.function.params);
1801        }
1802
1803        let ctx = self.ctx.clone().with(BitCtx::IsExported, true);
1804        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1805    }
1806
1807    fn visit_mut_export_default_decl(&mut self, n: &mut ExportDefaultDecl) {
1808        let ctx = self.ctx.clone().with(BitCtx::IsExported, true);
1809        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
1810    }
1811
1812    fn visit_mut_expr(&mut self, e: &mut Expr) {
1813        #[cfg(feature = "trace-ast")]
1814        let _tracing = {
1815            let s = dump(&*e, true);
1816            tracing::span!(
1817                tracing::Level::ERROR,
1818                "visit_mut_expr",
1819                src = tracing::field::display(&s)
1820            )
1821            .entered()
1822        };
1823
1824        let ctx = self
1825            .ctx
1826            .clone()
1827            .with(BitCtx::IsExported, false)
1828            .with(BitCtx::IsCallee, false);
1829        e.visit_mut_children_with(&mut *self.with_ctx(ctx));
1830
1831        #[cfg(feature = "trace-ast")]
1832        let _tracing = {
1833            let s = dump(&*e, true);
1834            tracing::span!(
1835                tracing::Level::ERROR,
1836                "visit_mut_expr_after_children",
1837                src = tracing::field::display(&s)
1838            )
1839            .entered()
1840        };
1841
1842        match e {
1843            Expr::Seq(seq) if seq.exprs.len() == 1 => {
1844                let span = seq.span;
1845                *e = *seq.exprs[0].take();
1846                e.set_span(span);
1847            }
1848
1849            Expr::Assign(AssignExpr {
1850                op: op!("="),
1851                left,
1852                right,
1853                ..
1854            }) => {
1855                if let Some(i) = left.as_ident_mut() {
1856                    let old = i.to_id();
1857
1858                    self.store_var_for_inlining(i, right, false);
1859
1860                    if i.is_dummy() && self.options.unused {
1861                        report_change!("inline: Removed variable ({}, {:?})", old.0, old.1);
1862                        self.vars.removed.insert(old.clone());
1863                    }
1864
1865                    if right.is_invalid() {
1866                        if let Some(lit) = self
1867                            .vars
1868                            .lits
1869                            .get(&old)
1870                            .or_else(|| self.vars.vars_for_inlining.get(&old))
1871                        {
1872                            *e = (**lit).clone();
1873                        }
1874                    }
1875                }
1876
1877                self.lift_seqs_of_assign(e)
1878            }
1879
1880            _ => {}
1881        }
1882
1883        if e.is_seq() {
1884            debug_assert_valid(e);
1885        }
1886
1887        // This is not accurate check but avoid some trivial cases.
1888        if self.changed {
1889            self.remove_invalid_bin(e);
1890        }
1891
1892        if e.is_seq() {
1893            debug_assert_valid(e);
1894        }
1895
1896        self.optimize_str_access_to_arguments(e);
1897
1898        if e.is_seq() {
1899            debug_assert_valid(e);
1900        }
1901
1902        self.replace_props(e);
1903
1904        if e.is_seq() {
1905            debug_assert_valid(e);
1906        }
1907
1908        self.drop_unused_assignments(e);
1909
1910        if e.is_seq() {
1911            debug_assert_valid(e);
1912        }
1913
1914        self.compress_lits(e);
1915
1916        if e.is_seq() {
1917            debug_assert_valid(e);
1918        }
1919
1920        self.compress_typeofs(e);
1921
1922        if e.is_seq() {
1923            debug_assert_valid(e);
1924        }
1925
1926        self.compress_logical_exprs_as_bang_bang(e, false);
1927
1928        if e.is_seq() {
1929            debug_assert_valid(e);
1930        }
1931
1932        self.inline(e);
1933
1934        if e.is_seq() {
1935            debug_assert_valid(e);
1936        }
1937
1938        self.handle_property_access(e);
1939
1940        if e.is_seq() {
1941            debug_assert_valid(e);
1942        }
1943
1944        self.compress_cond_expr_if_similar(e);
1945
1946        if e.is_seq() {
1947            debug_assert_valid(e);
1948        }
1949
1950        if self.options.negate_iife {
1951            self.negate_iife_in_cond(e);
1952
1953            if e.is_seq() {
1954                debug_assert_valid(e);
1955            }
1956        }
1957
1958        if e.is_seq() {
1959            debug_assert_valid(e);
1960        }
1961
1962        self.evaluate(e);
1963
1964        if e.is_seq() {
1965            debug_assert_valid(e);
1966        }
1967
1968        self.invoke_iife(e);
1969
1970        if e.is_seq() {
1971            debug_assert_valid(e);
1972        }
1973
1974        self.optimize_bangbang(e);
1975
1976        if e.is_seq() {
1977            debug_assert_valid(e);
1978        }
1979
1980        match e {
1981            Expr::Seq(s) if s.exprs.is_empty() => {
1982                e.take();
1983            }
1984            _ => {}
1985        }
1986
1987        self.reduce_escaped_newline_for_str_lit(e);
1988
1989        #[cfg(feature = "trace-ast")]
1990        tracing::debug!("Output: {}", dump(e, true));
1991    }
1992
1993    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
1994    fn visit_mut_expr_stmt(&mut self, n: &mut ExprStmt) {
1995        let was_directive = matches!(&*n.expr, Expr::Lit(Lit::Str(..)));
1996
1997        n.visit_mut_children_with(self);
1998
1999        let mut need_ignore_return_value = false;
2000
2001        // If negate_iife is true, it's already handled by
2002        // visit_mut_children_with(self) above.
2003        if !self.options.negate_iife {
2004            // I(kdy1) don't know why this check if required, but there are two test cases
2005            // with `options.expressions` as only difference.
2006            if !self.options.expr && self.negate_iife_in_cond(&mut n.expr) {
2007                need_ignore_return_value = true
2008            }
2009        }
2010
2011        self.negate_iife_ignoring_ret(&mut n.expr);
2012
2013        let is_directive = matches!(&*n.expr, Expr::Lit(Lit::Str(..)));
2014
2015        if is_directive {
2016            if !was_directive {
2017                *n = ExprStmt {
2018                    span: DUMMY_SP,
2019                    expr: Take::dummy(),
2020                };
2021            }
2022            return;
2023        }
2024
2025        if need_ignore_return_value
2026            || self.options.unused
2027            || self.options.side_effects
2028            || self.options.reduce_fns
2029            || (self.options.sequences() && n.expr.is_seq())
2030            || (self.options.conditionals
2031                && matches!(
2032                    &*n.expr,
2033                    Expr::Bin(BinExpr {
2034                        op: op!("||") | op!("&&"),
2035                        ..
2036                    })
2037                ))
2038        {
2039            // Preserve top-level negated iifes.
2040            if let Expr::Unary(unary @ UnaryExpr { op: op!("!"), .. }) = &*n.expr {
2041                if let Expr::Call(CallExpr {
2042                    callee: Callee::Expr(callee),
2043                    ..
2044                }) = &*unary.arg
2045                {
2046                    if let Expr::Fn(..) = &**callee {
2047                        return;
2048                    }
2049                }
2050            }
2051
2052            #[cfg(feature = "debug")]
2053            let start = dump(&n.expr, true);
2054
2055            // Fix https://github.com/swc-project/swc/issues/6422
2056            let is_object_lit_with_spread = n
2057                .expr
2058                .as_object()
2059                .map(|object_lit| object_lit.props.iter().any(|prop| prop.is_spread()))
2060                .unwrap_or(false);
2061
2062            if !is_object_lit_with_spread {
2063                let expr = self.ignore_return_value(&mut n.expr);
2064                n.expr = expr.map(Box::new).unwrap_or_else(|| {
2065                    report_change!("visit_mut_expr_stmt: Dropped an expression statement");
2066                    #[cfg(feature = "debug")]
2067                    dump_change_detail!("Removed {}", start);
2068
2069                    Expr::undefined(DUMMY_SP)
2070                });
2071            }
2072        }
2073
2074        self.normalize_expr(&mut n.expr);
2075
2076        #[cfg(debug_assertions)]
2077        {
2078            n.visit_with(&mut AssertValid);
2079        }
2080    }
2081
2082    fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
2083        #[cfg(feature = "debug")]
2084        let _tracing = tracing::span!(
2085            Level::ERROR,
2086            "visit_mut_fn_decl",
2087            id = tracing::field::display(&f.ident)
2088        )
2089        .entered();
2090
2091        self.functions
2092            .entry(f.ident.to_id())
2093            .or_insert_with(|| FnMetadata::from(&*f.function));
2094
2095        self.drop_unused_params(&mut f.function.params);
2096
2097        let ctx = self
2098            .ctx
2099            .clone()
2100            .with(BitCtx::TopLevel, false)
2101            .with(BitCtx::InFnLike, true)
2102            .with(BitCtx::IsLhsOfAssign, false)
2103            .with(BitCtx::IsExactLhsOfAssign, false);
2104        f.visit_mut_children_with(&mut *self.with_ctx(ctx));
2105    }
2106
2107    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2108    fn visit_mut_fn_expr(&mut self, e: &mut FnExpr) {
2109        if let Some(ident) = &e.ident {
2110            self.functions
2111                .entry(ident.to_id())
2112                .or_insert_with(|| FnMetadata::from(&*e.function));
2113        }
2114
2115        if !self.options.keep_fnames {
2116            if e.ident.is_some() && !contains_eval(&e.function, true) {
2117                self.remove_name_if_not_used(&mut e.ident);
2118            }
2119        }
2120
2121        let ctx = self
2122            .ctx
2123            .clone()
2124            .with(BitCtx::TopLevel, false)
2125            .with(BitCtx::IsLhsOfAssign, false)
2126            .with(BitCtx::IsExactLhsOfAssign, false)
2127            .with(BitCtx::InFnLike, true);
2128        e.visit_mut_children_with(&mut *self.with_ctx(ctx));
2129    }
2130
2131    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2132    fn visit_mut_for_in_stmt(&mut self, n: &mut ForInStmt) {
2133        n.right.visit_mut_with(self);
2134
2135        {
2136            let ctx = self
2137                .ctx
2138                .clone()
2139                .with(BitCtx::InVarDeclOfForInOrOfLoop, true)
2140                .with(BitCtx::IsExactLhsOfAssign, n.left.is_pat());
2141            self.with_ctx(ctx).visit_with_prepend(&mut n.left);
2142        }
2143
2144        {
2145            let ctx = self.ctx.clone().with(BitCtx::ExecutedMultipleTime, true);
2146            n.body.visit_mut_with(&mut *self.with_ctx(ctx));
2147        }
2148    }
2149
2150    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2151    fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) {
2152        n.right.visit_mut_with(self);
2153
2154        {
2155            let ctx = self
2156                .ctx
2157                .clone()
2158                .with(BitCtx::InVarDeclOfForInOrOfLoop, true)
2159                .with(BitCtx::IsExactLhsOfAssign, n.left.is_pat());
2160            self.with_ctx(ctx).visit_with_prepend(&mut n.left);
2161        }
2162
2163        {
2164            let ctx = self.ctx.clone().with(BitCtx::ExecutedMultipleTime, true);
2165            n.body.visit_mut_with(&mut *self.with_ctx(ctx));
2166        }
2167    }
2168
2169    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2170    fn visit_mut_for_stmt(&mut self, s: &mut ForStmt) {
2171        self.visit_with_prepend(&mut s.init);
2172
2173        debug_assert_valid(&s.init);
2174
2175        s.test.visit_mut_with(self);
2176        s.update.visit_mut_with(self);
2177
2178        let ctx = self.ctx.clone().with(BitCtx::ExecutedMultipleTime, true);
2179        s.body.visit_mut_with(&mut *self.with_ctx(ctx.clone()));
2180    }
2181
2182    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2183    fn visit_mut_function(&mut self, n: &mut Function) {
2184        n.decorators.visit_mut_with(self);
2185
2186        let old_in_asm = self.ctx.bit_ctx.contains(BitCtx::InAsm);
2187
2188        {
2189            let ctx = Ctx {
2190                bit_ctx: self
2191                    .ctx
2192                    .bit_ctx
2193                    .with(BitCtx::InFnLike, true)
2194                    .with(BitCtx::TopLevel, false),
2195                scope: n.ctxt,
2196                ..self.ctx.clone()
2197            };
2198            let optimizer = &mut *self.with_ctx(ctx);
2199
2200            n.params.visit_mut_with(optimizer);
2201            if let Some(body) = n.body.as_mut() {
2202                optimizer.handle_stmts(&mut body.stmts, true);
2203                #[cfg(debug_assertions)]
2204                {
2205                    body.visit_with(&mut AssertValid);
2206                }
2207            }
2208        }
2209
2210        #[cfg(debug_assertions)]
2211        {
2212            n.visit_with(&mut AssertValid);
2213        }
2214
2215        if let Some(body) = &mut n.body {
2216            self.merge_if_returns(&mut body.stmts, false, true);
2217
2218            #[cfg(debug_assertions)]
2219            {
2220                body.visit_with(&mut AssertValid);
2221            }
2222
2223            self.drop_else_token(&mut body.stmts);
2224        }
2225
2226        #[cfg(debug_assertions)]
2227        {
2228            n.visit_with(&mut AssertValid);
2229        }
2230
2231        {
2232            self.with_ctx(self.ctx.clone())
2233                .optimize_usage_of_arguments(n);
2234        }
2235
2236        {
2237            self.with_ctx(self.ctx.clone()).drop_unused_rest_params(n);
2238        }
2239
2240        self.ctx.bit_ctx.set(BitCtx::InAsm, old_in_asm);
2241
2242        if let Some(body) = &mut n.body {
2243            drop_invalid_stmts(&mut body.stmts);
2244        }
2245
2246        #[cfg(debug_assertions)]
2247        {
2248            n.visit_with(&mut AssertValid);
2249        }
2250    }
2251
2252    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2253    fn visit_mut_if_stmt(&mut self, n: &mut IfStmt) {
2254        n.test.visit_mut_with(self);
2255
2256        let ctx = self.ctx.clone().with(BitCtx::InCond, true);
2257
2258        n.cons.visit_mut_with(&mut *self.with_ctx(ctx.clone()));
2259
2260        n.alt.visit_mut_with(&mut *self.with_ctx(ctx.clone()));
2261
2262        self.negate_if_stmt(n);
2263    }
2264
2265    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2266    fn visit_mut_labeled_stmt(&mut self, n: &mut LabeledStmt) {
2267        let ctx = self.ctx.clone().with(
2268            BitCtx::DontUsePrependNorAppend,
2269            contains_leaping_continue_with_label(&n.body, n.label.sym.clone()),
2270        );
2271
2272        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
2273
2274        self.try_remove_label(n);
2275    }
2276
2277    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2278    fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) {
2279        {
2280            let ctx = self
2281                .ctx
2282                .clone()
2283                .with(BitCtx::InObjOfNonComputedMember, !n.prop.is_computed())
2284                .with(BitCtx::IsExactLhsOfAssign, false)
2285                .with(BitCtx::IsUpdateArg, false);
2286            n.obj.visit_mut_with(&mut *self.with_ctx(ctx));
2287        }
2288        if let MemberProp::Computed(c) = &mut n.prop {
2289            let ctx = self
2290                .ctx
2291                .clone()
2292                .with(BitCtx::IsExactLhsOfAssign, false)
2293                .with(BitCtx::IsLhsOfAssign, false)
2294                .with(BitCtx::IsUpdateArg, false);
2295
2296            c.visit_mut_with(&mut *self.with_ctx(ctx));
2297        }
2298    }
2299
2300    fn visit_mut_module(&mut self, m: &mut Module) {
2301        self.is_module = true;
2302        m.visit_mut_children_with(self);
2303    }
2304
2305    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2306    fn visit_mut_module_item(&mut self, s: &mut ModuleItem) {
2307        s.visit_mut_children_with(self);
2308
2309        if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2310            decl: Decl::Var(v),
2311            ..
2312        })) = s
2313        {
2314            if v.decls.is_empty() {
2315                s.take();
2316            }
2317        }
2318    }
2319
2320    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2321    fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
2322        let ctx = self.ctx.clone().with(BitCtx::TopLevel, true);
2323        self.with_ctx(ctx).handle_stmt_likes(stmts, true);
2324
2325        if self.vars.inline_with_multi_replacer(stmts) {
2326            self.changed = true;
2327        }
2328
2329        drop_invalid_stmts(stmts);
2330    }
2331
2332    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2333    fn visit_mut_new_expr(&mut self, n: &mut NewExpr) {
2334        {
2335            let ctx = self
2336                .ctx
2337                .clone()
2338                .with(BitCtx::IsCallee, true)
2339                .with(BitCtx::IsExactLhsOfAssign, false)
2340                .with(BitCtx::IsLhsOfAssign, false);
2341            n.callee.visit_mut_with(&mut *self.with_ctx(ctx));
2342        }
2343
2344        {
2345            let ctx = self
2346                .ctx
2347                .clone()
2348                .with(BitCtx::IsExactLhsOfAssign, false)
2349                .with(BitCtx::IsLhsOfAssign, false);
2350            n.args.visit_mut_with(&mut *self.with_ctx(ctx));
2351        }
2352    }
2353
2354    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2355    fn visit_mut_opt_stmt(&mut self, s: &mut Option<Box<Stmt>>) {
2356        s.visit_mut_children_with(self);
2357
2358        if let Some(Stmt::Empty(..)) = s.as_deref() {
2359            self.changed = true;
2360            report_change!("misc: Removing empty statement");
2361            *s = None;
2362        }
2363    }
2364
2365    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2366    fn visit_mut_opt_var_decl_or_expr(&mut self, n: &mut Option<VarDeclOrExpr>) {
2367        n.visit_mut_children_with(self);
2368
2369        match n {
2370            Some(VarDeclOrExpr::Expr(e)) => match &mut **e {
2371                Expr::Invalid(..) => {
2372                    *n = None;
2373                }
2374                Expr::Seq(SeqExpr { exprs, .. }) if exprs.is_empty() => {
2375                    *n = None;
2376                }
2377                _ => {}
2378            },
2379            Some(VarDeclOrExpr::VarDecl(v)) => {
2380                if v.decls.is_empty() {
2381                    *n = None;
2382                }
2383            }
2384            _ => {}
2385        }
2386    }
2387
2388    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2389    fn visit_mut_param(&mut self, n: &mut Param) {
2390        let ctx = Ctx {
2391            bit_ctx: self.ctx.bit_ctx.with(BitCtx::InParam, true),
2392            ..self.ctx.clone()
2393        };
2394        let mut o = self.with_ctx(ctx);
2395        n.visit_mut_children_with(&mut *o);
2396
2397        o.drop_unused_param(&mut n.pat, false);
2398    }
2399
2400    fn visit_mut_params(&mut self, n: &mut Vec<Param>) {
2401        n.visit_mut_children_with(self);
2402
2403        n.retain(|p| !p.pat.is_invalid());
2404    }
2405
2406    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2407    fn visit_mut_return_stmt(&mut self, n: &mut ReturnStmt) {
2408        n.visit_mut_children_with(self);
2409
2410        if let Some(arg) = &mut n.arg {
2411            self.optimize_last_expr_before_termination(arg);
2412        }
2413    }
2414
2415    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2416    fn visit_mut_script(&mut self, s: &mut Script) {
2417        let ctx = self.ctx.clone().with(BitCtx::TopLevel, true);
2418        s.visit_mut_children_with(&mut *self.with_ctx(ctx));
2419
2420        if self.vars.inline_with_multi_replacer(s) {
2421            self.changed = true;
2422        }
2423
2424        drop_invalid_stmts(&mut s.body);
2425    }
2426
2427    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2428    fn visit_mut_seq_expr(&mut self, n: &mut SeqExpr) {
2429        n.visit_mut_children_with(self);
2430
2431        // Enhanced IIFE invocation for arrow functions in sequence expressions
2432        self.invoke_iife_in_seq_expr(n);
2433
2434        self.merge_sequences_in_seq_expr(n);
2435    }
2436
2437    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2438    fn visit_mut_stmt(&mut self, s: &mut Stmt) {
2439        let old_prepend = self.prepend_stmts.take();
2440        let old_append = self.append_stmts.take();
2441
2442        #[cfg(feature = "debug")]
2443        let _tracing = {
2444            let text = dump(&*s, false);
2445
2446            if text.lines().count() < 10 {
2447                Some(span!(Level::ERROR, "visit_mut_stmt", "start" = &*text).entered())
2448            } else {
2449                None
2450            }
2451        };
2452
2453        let ctx = self
2454            .ctx
2455            .clone()
2456            .with(BitCtx::IsCallee, false)
2457            .with(BitCtx::IsDeleteArg, false)
2458            .with(BitCtx::IsUpdateArg, false)
2459            .with(BitCtx::IsLhsOfAssign, false)
2460            .with(BitCtx::InBangArg, false)
2461            .with(BitCtx::IsExported, false)
2462            .with(BitCtx::InObjOfNonComputedMember, false);
2463        s.visit_mut_children_with(&mut *self.with_ctx(ctx));
2464
2465        if self.prepend_stmts.is_empty() && self.append_stmts.is_empty() {
2466            match s {
2467                // We use var decl with no declarator to indicate we dropped an decl.
2468                Stmt::Decl(Decl::Var(v)) if v.decls.is_empty() => {
2469                    *s = EmptyStmt { span: DUMMY_SP }.into();
2470                    self.prepend_stmts = old_prepend;
2471                    self.append_stmts = old_append;
2472                    return;
2473                }
2474                Stmt::Expr(es) => {
2475                    if es.expr.is_invalid() {
2476                        *s = EmptyStmt { span: DUMMY_SP }.into();
2477                        self.prepend_stmts = old_prepend;
2478                        self.append_stmts = old_append;
2479                        return;
2480                    }
2481                }
2482                _ => {}
2483            }
2484
2485            #[cfg(debug_assertions)]
2486            {
2487                s.visit_with(&mut AssertValid);
2488            }
2489        }
2490
2491        match s {
2492            Stmt::Labeled(LabeledStmt {
2493                label: Ident { sym, .. },
2494                body,
2495                ..
2496            }) if sym.is_empty() => {
2497                *s = *body.take();
2498
2499                debug_assert_valid(s);
2500            }
2501            _ => (),
2502        }
2503
2504        self.remove_duplicate_var_decls(s);
2505
2506        match s {
2507            Stmt::Decl(Decl::Var(v)) if v.decls.is_empty() => {
2508                s.take();
2509                if self.prepend_stmts.is_empty() && self.append_stmts.is_empty() {
2510                    self.prepend_stmts = old_prepend;
2511                    self.append_stmts = old_append;
2512                    return;
2513                }
2514            }
2515
2516            _ => {}
2517        }
2518
2519        debug_assert_valid(s);
2520
2521        match s {
2522            Stmt::Expr(e) => {
2523                if let Some(block) = self.invoke_iife_stmt(&mut e.expr, false) {
2524                    *s = Stmt::Block(block)
2525                }
2526            }
2527            Stmt::Return(ReturnStmt { arg: Some(arg), .. }) => {
2528                if let Some(mut block) = self.invoke_iife_stmt(&mut *arg, true) {
2529                    if !block
2530                        .stmts
2531                        .last()
2532                        .map(swc_ecma_utils::StmtExt::terminates)
2533                        .unwrap_or(false)
2534                    {
2535                        block.stmts.push(Stmt::Return(ReturnStmt {
2536                            span: DUMMY_SP,
2537                            arg: None,
2538                        }))
2539                    }
2540                    *s = Stmt::Block(block)
2541                }
2542            }
2543            _ => (),
2544        }
2545
2546        // visit_mut_children_with above may produce easily optimizable block
2547        // statements.
2548        self.try_removing_block(s, false);
2549
2550        debug_assert_valid(s);
2551
2552        // These methods may modify prepend_stmts or append_stmts.
2553        self.optimize_loops_if_cond_is_false(s);
2554
2555        debug_assert_valid(s);
2556
2557        self.optimize_loops_with_break(s);
2558
2559        debug_assert_valid(s);
2560
2561        self.try_removing_block(s, false);
2562
2563        debug_assert_valid(s);
2564
2565        if !self.prepend_stmts.is_empty() || !self.append_stmts.is_empty() {
2566            report_change!("Creating a fake block because of prepend or append");
2567
2568            let span = s.span();
2569            *s = BlockStmt {
2570                span,
2571                ctxt: SyntaxContext::empty().apply_mark(self.marks.fake_block),
2572                stmts: self
2573                    .prepend_stmts
2574                    .take_stmts()
2575                    .into_iter()
2576                    .chain(once(s.take()))
2577                    .chain(self.append_stmts.take_stmts())
2578                    .filter(|s| match s {
2579                        Stmt::Empty(..) => false,
2580                        Stmt::Decl(Decl::Var(v)) => !v.decls.is_empty(),
2581                        _ => true,
2582                    })
2583                    .collect(),
2584            }
2585            .into();
2586
2587            #[cfg(debug_assertions)]
2588            {
2589                s.visit_with(&mut AssertValid);
2590            }
2591        }
2592
2593        self.prepend_stmts = old_prepend;
2594        self.append_stmts = old_append;
2595
2596        let prepend_len = self.prepend_stmts.len();
2597        let append_len = self.append_stmts.len();
2598
2599        debug_assert_eq!(self.prepend_stmts.len(), prepend_len);
2600        debug_assert_eq!(self.append_stmts.len(), append_len);
2601
2602        if let Stmt::Expr(ExprStmt { expr, .. }) = s {
2603            if is_pure_undefined(self.ctx.expr_ctx, expr) {
2604                *s = EmptyStmt { span: DUMMY_SP }.into();
2605                return;
2606            }
2607
2608            let is_directive = matches!(&**expr, Expr::Lit(Lit::Str(..)));
2609
2610            if self.options.directives
2611                && is_directive
2612                && self.ctx.expr_ctx.in_strict
2613                && match &**expr {
2614                    Expr::Lit(Lit::Str(Str { value, .. })) => *value == *"use strict",
2615                    _ => false,
2616                }
2617            {
2618                report_change!("Removing 'use strict'");
2619                *s = EmptyStmt { span: DUMMY_SP }.into();
2620                return;
2621            }
2622
2623            if self.options.unused {
2624                let can_be_removed = !is_directive
2625                    && !expr.is_ident()
2626                    && !expr.may_have_side_effects(self.ctx.expr_ctx);
2627
2628                if can_be_removed {
2629                    self.changed = true;
2630                    report_change!("unused: Dropping an expression without side effect");
2631                    dump_change_detail!("unused: Dropping \n{}\n", dump(&*expr, false));
2632                    *s = EmptyStmt { span: DUMMY_SP }.into();
2633                    return;
2634                }
2635            }
2636        }
2637
2638        debug_assert_eq!(self.prepend_stmts.len(), prepend_len);
2639        debug_assert_eq!(self.append_stmts.len(), append_len);
2640
2641        match s {
2642            // We use var decl with no declarator to indicate we dropped an decl.
2643            Stmt::Decl(Decl::Var(v)) if v.decls.is_empty() => {
2644                *s = EmptyStmt { span: DUMMY_SP }.into();
2645                return;
2646            }
2647            _ => {}
2648        }
2649
2650        debug_assert_eq!(self.prepend_stmts.len(), prepend_len);
2651        debug_assert_eq!(self.append_stmts.len(), append_len);
2652        debug_assert_valid(s);
2653
2654        self.compress_if_without_alt(s);
2655
2656        debug_assert_eq!(self.prepend_stmts.len(), prepend_len);
2657        debug_assert_eq!(self.append_stmts.len(), append_len);
2658        debug_assert_valid(s);
2659
2660        self.compress_if_stmt_as_cond(s);
2661
2662        debug_assert_eq!(self.prepend_stmts.len(), prepend_len);
2663        debug_assert_eq!(self.append_stmts.len(), append_len);
2664        debug_assert_valid(s);
2665    }
2666
2667    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2668    fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
2669        // Skip if `use asm` exists.
2670        if maybe_par!(
2671            stmts.iter().any(|stmt| match stmt.as_stmt() {
2672                Some(Stmt::Expr(stmt)) => match &*stmt.expr {
2673                    Expr::Lit(Lit::Str(Str { raw, .. })) => {
2674                        matches!(raw, Some(value) if value == "\"use asm\"" || value == "'use asm'")
2675                    }
2676                    _ => false,
2677                },
2678                _ => false,
2679            }),
2680            *crate::LIGHT_TASK_PARALLELS
2681        ) {
2682            return;
2683        }
2684
2685        #[cfg(debug_assertions)]
2686        {
2687            stmts.visit_with(&mut AssertValid);
2688        }
2689        self.handle_stmts(stmts, false);
2690
2691        if stmts.len() == 1 {
2692            if let Stmt::Expr(ExprStmt { expr, .. }) = &stmts[0] {
2693                if let Expr::Lit(Lit::Str(s)) = &**expr {
2694                    if s.value == *"use strict" {
2695                        stmts.clear();
2696                    }
2697                }
2698            }
2699        }
2700    }
2701
2702    fn visit_mut_str(&mut self, s: &mut Str) {
2703        s.visit_mut_children_with(self);
2704    }
2705
2706    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2707    fn visit_mut_super_prop_expr(&mut self, n: &mut SuperPropExpr) {
2708        if let SuperProp::Computed(c) = &mut n.prop {
2709            let ctx = self
2710                .ctx
2711                .clone()
2712                .with(BitCtx::IsExactLhsOfAssign, false)
2713                .with(BitCtx::IsLhsOfAssign, false);
2714            c.visit_mut_with(&mut *self.with_ctx(ctx));
2715        }
2716    }
2717
2718    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2719    fn visit_mut_switch_stmt(&mut self, n: &mut SwitchStmt) {
2720        n.discriminant.visit_mut_with(self);
2721
2722        n.cases.visit_mut_with(self);
2723    }
2724
2725    /// We don't optimize [Tpl] contained in [TaggedTpl].
2726    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2727    fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
2728        n.tag.visit_mut_with(
2729            &mut *self.with_ctx(self.ctx.clone().with(BitCtx::IsThisAwareCallee, true)),
2730        );
2731
2732        n.tpl.exprs.visit_mut_with(self);
2733    }
2734
2735    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2736    fn visit_mut_throw_stmt(&mut self, n: &mut ThrowStmt) {
2737        n.visit_mut_children_with(self);
2738
2739        self.optimize_last_expr_before_termination(&mut n.arg);
2740    }
2741
2742    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2743    fn visit_mut_tpl(&mut self, n: &mut Tpl) {
2744        debug_assert_eq!(n.exprs.len() + 1, n.quasis.len());
2745
2746        {
2747            let ctx = self.ctx.clone().with(BitCtx::InTplExpr, true);
2748            let mut o = self.with_ctx(ctx);
2749            n.visit_mut_children_with(&mut *o);
2750        }
2751
2752        n.exprs
2753            .iter_mut()
2754            .for_each(|expr| self.optimize_expr_in_str_ctx(expr));
2755    }
2756
2757    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2758    fn visit_mut_try_stmt(&mut self, n: &mut TryStmt) {
2759        let ctx = self.ctx.clone().with(BitCtx::InTryBlock, true);
2760        n.block.visit_mut_with(&mut *self.with_ctx(ctx));
2761
2762        n.handler.visit_mut_with(self);
2763
2764        n.finalizer.visit_mut_with(self);
2765    }
2766
2767    fn visit_mut_catch_clause(&mut self, n: &mut CatchClause) {
2768        n.visit_mut_children_with(self);
2769
2770        if self.options.ecma < EsVersion::Es2019 || !self.options.unused {
2771            return;
2772        }
2773
2774        if let Some(param) = &mut n.param {
2775            self.take_pat_if_unused(param, None, false);
2776            if param.is_invalid() {
2777                n.param = None;
2778            }
2779        }
2780    }
2781
2782    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2783    fn visit_mut_unary_expr(&mut self, n: &mut UnaryExpr) {
2784        let ctx = self
2785            .ctx
2786            .clone()
2787            .with(BitCtx::InBangArg, n.op == op!("!"))
2788            .with(BitCtx::IsDeleteArg, n.op == op!("delete"));
2789
2790        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
2791
2792        // infinite loop
2793        if n.op == op!("void") {
2794            match &*n.arg {
2795                Expr::Lit(Lit::Num(..)) => {}
2796
2797                _ => {
2798                    let arg = self.ignore_return_value(&mut n.arg);
2799
2800                    n.arg = Box::new(arg.unwrap_or_else(|| {
2801                        report_change!("Ignoring arg of `void`");
2802
2803                        make_number(DUMMY_SP, 0.0)
2804                    }));
2805                }
2806            }
2807        }
2808    }
2809
2810    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2811    fn visit_mut_update_expr(&mut self, n: &mut UpdateExpr) {
2812        let ctx = self.ctx.clone().with(BitCtx::IsUpdateArg, true);
2813
2814        n.visit_mut_children_with(&mut *self.with_ctx(ctx));
2815    }
2816
2817    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2818    fn visit_mut_using_decl(&mut self, n: &mut UsingDecl) {
2819        let ctx = Ctx {
2820            bit_ctx: self
2821                .ctx
2822                .bit_ctx
2823                .with(BitCtx::IsUpdateArg, false)
2824                .with(BitCtx::IsConst, false)
2825                .with(BitCtx::IsLet, false)
2826                .with(BitCtx::IsVar, false),
2827            ..self.ctx.clone()
2828        };
2829
2830        for decl in n.decls.iter_mut() {
2831            decl.init.visit_mut_with(&mut *self.with_ctx(ctx.clone()));
2832        }
2833    }
2834
2835    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2836    fn visit_mut_var_decl(&mut self, n: &mut VarDecl) {
2837        {
2838            let ctx = Ctx {
2839                bit_ctx: self
2840                    .ctx
2841                    .bit_ctx
2842                    .with(BitCtx::IsUpdateArg, false)
2843                    .with(
2844                        BitCtx::IsConst,
2845                        n.kind == VarDeclKind::Const || self.has_const_ann(n.ctxt),
2846                    )
2847                    .with(BitCtx::IsVar, n.kind == VarDeclKind::Var)
2848                    .with(BitCtx::IsLet, n.kind == VarDeclKind::Let),
2849                ..self.ctx.clone()
2850            };
2851
2852            n.visit_mut_children_with(&mut *self.with_ctx(ctx));
2853        }
2854
2855        if n.kind == VarDeclKind::Let {
2856            n.decls.iter_mut().for_each(|var| {
2857                if !var.name.is_ident() {
2858                    return;
2859                }
2860
2861                if let Some(e) = &var.init {
2862                    if is_pure_undefined(self.ctx.expr_ctx, e) {
2863                        self.changed = true;
2864                        report_change!(
2865                            "Dropping explicit initializer which evaluates to `undefined`"
2866                        );
2867
2868                        var.init = None;
2869                    }
2870                }
2871            });
2872        }
2873
2874        if self.options.const_to_let
2875            // Don't change constness of exported variables.
2876            && !self.ctx.bit_ctx.contains(BitCtx::IsExported)
2877            && (self.is_module || !self.ctx.in_top_level())
2878        {
2879            if n.kind == VarDeclKind::Const {
2880                // If a const variable is reassigned, we should not convert it to `let`
2881                let no_reassignment = n.decls.iter().all(|var| {
2882                    let ids = get_ids_of_pat(&var.name);
2883                    for id in ids {
2884                        if let Some(usage) = self.data.vars.get(&id) {
2885                            if usage.flags.contains(VarUsageInfoFlags::REASSIGNED) {
2886                                return false;
2887                            }
2888                        }
2889                    }
2890                    true
2891                });
2892
2893                if no_reassignment {
2894                    n.kind = VarDeclKind::Let;
2895                }
2896            }
2897        }
2898    }
2899
2900    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2901    fn visit_mut_var_declarator(&mut self, var: &mut VarDeclarator) {
2902        var.name.visit_mut_with(self);
2903
2904        var.init.visit_mut_with(self);
2905
2906        debug_assert_valid(&var.init);
2907
2908        self.remove_duplicate_name_of_function(var);
2909
2910        debug_assert_valid(&var.init);
2911
2912        if let VarDeclarator {
2913            name: Pat::Ident(id),
2914            init: Some(init),
2915            definite: false,
2916            ..
2917        } = var
2918        {
2919            self.store_var_for_inlining(&mut id.id, init, false);
2920
2921            if init.is_invalid() {
2922                var.init = None
2923            }
2924
2925            // Dummy check.
2926            if id.sym.is_empty() {
2927                var.name = Pat::dummy();
2928            }
2929        };
2930
2931        self.drop_unused_properties(var);
2932
2933        debug_assert_valid(&var.init);
2934    }
2935
2936    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
2937    fn visit_mut_var_declarators(&mut self, vars: &mut Vec<VarDeclarator>) {
2938        vars.retain_mut(|var| {
2939            if var.name.is_invalid() {
2940                self.changed = true;
2941                return false;
2942            }
2943
2944            true
2945        });
2946
2947        {
2948            // We loop with index to avoid borrow checker issue.
2949            // We use splice so we cannot use for _ in vars
2950            let mut idx = 0;
2951
2952            while idx < vars.len() {
2953                let var = &mut vars[idx];
2954                var.visit_mut_with(self);
2955
2956                // The varaible is dropped.
2957                if var.name.is_invalid() {
2958                    vars.remove(idx);
2959                    continue;
2960                }
2961
2962                let new = self.hoist_props_of_var(var);
2963
2964                if let Some(new) = new {
2965                    let len = new.len();
2966                    vars.splice(idx..=idx, new);
2967                    idx += len;
2968                } else {
2969                    idx += 1;
2970                }
2971            }
2972        }
2973
2974        vars.retain_mut(|var| {
2975            if var.name.is_invalid() {
2976                self.changed = true;
2977                return false;
2978            }
2979
2980            debug_assert_valid(&*var);
2981
2982            true
2983        });
2984
2985        let uses_eval = self
2986            .data
2987            .scopes
2988            .get(&self.ctx.scope)
2989            .unwrap()
2990            .contains(ScopeData::HAS_EVAL_CALL);
2991
2992        if !uses_eval && !self.ctx.bit_ctx.contains(BitCtx::DontUsePrependNorAppend) {
2993            for v in vars.iter_mut() {
2994                if v.init
2995                    .as_deref()
2996                    .map(|e| !e.is_ident() && !e.may_have_side_effects(self.ctx.expr_ctx))
2997                    .unwrap_or(true)
2998                {
2999                    self.drop_unused_var_declarator(v, &mut None);
3000                }
3001            }
3002
3003            let mut can_prepend = true;
3004            let mut side_effects = Vec::new();
3005
3006            for v in vars.iter_mut() {
3007                let mut storage = None;
3008                self.drop_unused_var_declarator(v, &mut storage);
3009                if let Some(expr) = &mut storage {
3010                    expr.visit_mut_with(self);
3011                }
3012                side_effects.extend(storage);
3013
3014                // Dropped. Side effects of the initializer is stored in `side_effects`.
3015                if v.name.is_invalid() {
3016                    continue;
3017                }
3018
3019                // If initializer is none, we can check next item without thinking about side
3020                // effects.
3021                if v.init.is_none() {
3022                    continue;
3023                }
3024
3025                // We can drop the next variable, as we don't have to worry about the side
3026                // effect.
3027                if side_effects.is_empty() {
3028                    can_prepend = false;
3029                    continue;
3030                }
3031
3032                // We now have to handle side effects.
3033
3034                if can_prepend {
3035                    can_prepend = false;
3036
3037                    self.prepend_stmts.push(
3038                        ExprStmt {
3039                            span: DUMMY_SP,
3040                            expr: if side_effects.len() == 1 {
3041                                side_effects.remove(0)
3042                            } else {
3043                                SeqExpr {
3044                                    span: DUMMY_SP,
3045                                    exprs: side_effects.take(),
3046                                }
3047                                .into()
3048                            },
3049                        }
3050                        .into(),
3051                    );
3052                } else {
3053                    // We prepend side effects to the initializer.
3054
3055                    let seq = v.init.as_mut().unwrap().force_seq();
3056                    seq.exprs = side_effects
3057                        .drain(..)
3058                        .chain(seq.exprs.take())
3059                        .filter(|e| !e.is_invalid())
3060                        .collect();
3061                }
3062            }
3063
3064            // We append side effects.
3065            if !side_effects.is_empty() {
3066                self.append_stmts.push(
3067                    ExprStmt {
3068                        span: DUMMY_SP,
3069                        expr: if side_effects.len() == 1 {
3070                            side_effects.remove(0)
3071                        } else {
3072                            SeqExpr {
3073                                span: DUMMY_SP,
3074                                exprs: side_effects,
3075                            }
3076                            .into()
3077                        },
3078                    }
3079                    .into(),
3080                );
3081            }
3082
3083            vars.retain_mut(|var| {
3084                if var.name.is_invalid() {
3085                    self.changed = true;
3086                    return false;
3087                }
3088
3089                if let Some(Expr::Invalid(..)) = var.init.as_deref() {
3090                    if let Pat::Ident(i) = &var.name {
3091                        if let Some(usage) = self.data.vars.get(&i.id.to_id()) {
3092                            if usage
3093                                .flags
3094                                .contains(VarUsageInfoFlags::DECLARED_AS_CATCH_PARAM)
3095                            {
3096                                var.init = None;
3097                                debug_assert_valid(var);
3098                                return true;
3099                            }
3100                        }
3101                    }
3102
3103                    return false;
3104                }
3105
3106                debug_assert_valid(var);
3107
3108                true
3109            });
3110        }
3111    }
3112
3113    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
3114    fn visit_mut_while_stmt(&mut self, n: &mut WhileStmt) {
3115        {
3116            let ctx = self.ctx.clone().with(BitCtx::ExecutedMultipleTime, true);
3117            n.visit_mut_children_with(&mut *self.with_ctx(ctx));
3118        }
3119    }
3120
3121    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
3122    fn visit_mut_with_stmt(&mut self, n: &mut WithStmt) {
3123        n.obj.visit_mut_with(self);
3124
3125        {
3126            let ctx = self.ctx.clone().with(BitCtx::InWithStmt, true);
3127            n.body.visit_mut_with(&mut *self.with_ctx(ctx));
3128        }
3129    }
3130
3131    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
3132    fn visit_mut_yield_expr(&mut self, n: &mut YieldExpr) {
3133        n.visit_mut_children_with(self);
3134
3135        if let Some(arg) = &mut n.arg {
3136            self.compress_undefined(arg);
3137
3138            if !n.delegate && is_pure_undefined(self.ctx.expr_ctx, arg) {
3139                n.arg = None;
3140            }
3141        }
3142    }
3143}
3144
3145/// If true, `0` in `(0, foo.bar)()` is preserved.
3146fn is_callee_this_aware(callee: &Expr) -> bool {
3147    match callee {
3148        Expr::Arrow(..) => return false,
3149        Expr::Seq(..) => return true,
3150        Expr::Member(MemberExpr { obj, .. }) => {
3151            if let Expr::Ident(obj) = &**obj {
3152                if &*obj.sym == "console" {
3153                    return false;
3154                }
3155            }
3156        }
3157
3158        _ => {}
3159    }
3160
3161    true
3162}
3163
3164fn is_expr_access_to_arguments(l: &SimpleAssignTarget) -> bool {
3165    match l {
3166        SimpleAssignTarget::Member(MemberExpr { obj, .. }) => {
3167            matches!(&**obj, Expr::Ident(Ident { sym, .. }) if (&**sym == "arguments"))
3168        }
3169        _ => false,
3170    }
3171}
3172
3173fn is_left_access_to_arguments(l: &AssignTarget) -> bool {
3174    match l {
3175        AssignTarget::Simple(e) => is_expr_access_to_arguments(e),
3176        _ => false,
3177    }
3178}