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
56pub(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#[derive(Debug, Clone)]
101struct Ctx {
102 expr_ctx: ExprCtx,
103
104 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 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 const IsCallee = 1 << 6;
133
134 const InTryBlock = 1 << 7;
137
138 const InCond = 1 << 8;
140
141 const IsDeleteArg = 1 << 9;
143
144 const IsUpdateArg = 1 << 10;
146
147 const IsLhsOfAssign = 1 << 11;
148
149 const IsExactLhsOfAssign = 1 << 12;
151
152 const ExecutedMultipleTime = 1 << 13;
154
155 const InBangArg = 1 << 14;
157
158 const InVarDeclOfForInOrOfLoop = 1 << 15;
159
160 const DontUseNegatedIife = 1 << 16;
161
162 const IsExported = 1 << 17;
164
165 const TopLevel = 1 << 18;
167
168 const InFnLike = 1 << 19;
170
171 const InBlock = 1 << 20;
172
173 const InObjOfNonComputedMember = 1 << 21;
174
175 const InTplExpr = 1 << 22;
176
177 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 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 prepend_stmts: SynthesizedStmts,
227 append_stmts: SynthesizedStmts,
229
230 vars: Vars,
231
232 typeofs: Box<FxHashMap<Id, Atom>>,
233 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 lits: FxHashMap<Id, Box<Expr>>,
251
252 hoisted_props: Box<FxHashMap<(Id, Wtf8Atom), Ident>>,
254
255 lits_for_cmp: FxHashMap<Id, Box<Expr>>,
260
261 lits_for_array_access: FxHashMap<Id, Box<Expr>>,
263
264 simple_functions: FxHashMap<Id, Box<Expr>>,
268 vars_for_inlining: FxHashMap<Id, Box<Expr>>,
269
270 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 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 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 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 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 if i < directive_count {
491 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 drop_invalid_stmts(stmts);
562
563 self.prepend_stmts = prepend_stmts;
565 self.append_stmts = append_stmts;
566 }
567
568 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 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 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 #[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 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 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 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 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 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 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 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 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 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 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 Expr::Unary(expr) => {
1158 self.changed = true;
1159 report_change!("ignore_return_value: Reducing unary ({})", expr.op);
1160
1161 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 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 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 if is_last_undefined {
1266 self.changed = true;
1267 exprs.pop();
1269
1270 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 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 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 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 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 !self.options.negate_iife {
2004 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 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 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 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 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 self.try_removing_block(s, false);
2549
2550 debug_assert_valid(s);
2551
2552 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 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 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 #[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 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 && !self.ctx.bit_ctx.contains(BitCtx::IsExported)
2877 && (self.is_module || !self.ctx.in_top_level())
2878 {
2879 if n.kind == VarDeclKind::Const {
2880 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 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 let mut idx = 0;
2951
2952 while idx < vars.len() {
2953 let var = &mut vars[idx];
2954 var.visit_mut_with(self);
2955
2956 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 if v.name.is_invalid() {
3016 continue;
3017 }
3018
3019 if v.init.is_none() {
3022 continue;
3023 }
3024
3025 if side_effects.is_empty() {
3028 can_prepend = false;
3029 continue;
3030 }
3031
3032 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 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 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
3145fn 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}