1#![deny(clippy::all)]
2#![allow(clippy::boxed_local)]
3#![allow(clippy::mutable_key_type)]
4#![allow(clippy::match_like_matches_macro)]
5#![allow(clippy::vec_box)]
6#![cfg_attr(not(feature = "concurrent"), allow(unused))]
7
8#[doc(hidden)]
9pub extern crate swc_atoms;
10#[doc(hidden)]
11pub extern crate swc_common;
12#[doc(hidden)]
13pub extern crate swc_ecma_ast;
14
15use std::{borrow::Cow, hash::Hash, num::FpCategory, ops::Add};
16
17use number::ToJsString;
18use once_cell::sync::Lazy;
19use parallel::{Parallel, ParallelExt};
20use rustc_hash::{FxHashMap, FxHashSet};
21use swc_atoms::{atom, Atom};
22use swc_common::{util::take::Take, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
23use swc_ecma_ast::*;
24use swc_ecma_visit::{
25 noop_visit_mut_type, noop_visit_type, visit_mut_obj_and_computed, visit_obj_and_computed,
26 Visit, VisitMut, VisitMutWith, VisitWith,
27};
28use tracing::trace;
29
30#[allow(deprecated)]
31pub use self::{
32 factory::{ExprFactory, FunctionFactory, IntoIndirectCall},
33 value::{
34 Merge,
35 Type::{
36 self, Bool as BoolType, Null as NullType, Num as NumberType, Obj as ObjectType,
37 Str as StringType, Symbol as SymbolType, Undefined as UndefinedType,
38 },
39 Value::{self, Known, Unknown},
40 },
41 Purity::{MayBeImpure, Pure},
42};
43use crate::ident::IdentLike;
44
45#[macro_use]
46mod macros;
47pub mod constructor;
48mod factory;
49pub mod function;
50pub mod ident;
51pub mod parallel;
52mod value;
53pub mod var;
54
55mod node_ignore_span;
56pub mod number;
57pub mod stack_size;
58pub mod str;
59pub use node_ignore_span::NodeIgnoringSpan;
60
61pub struct ThisVisitor {
63 found: bool,
64}
65
66pub(crate) static CPU_COUNT: Lazy<usize> = Lazy::new(num_cpus::get);
67pub(crate) static LIGHT_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 100);
68
69impl Visit for ThisVisitor {
70 noop_visit_type!();
71
72 fn visit_constructor(&mut self, _: &Constructor) {}
74
75 fn visit_fn_decl(&mut self, _: &FnDecl) {}
77
78 fn visit_fn_expr(&mut self, _: &FnExpr) {}
80
81 fn visit_function(&mut self, _: &Function) {}
83
84 fn visit_getter_prop(&mut self, n: &GetterProp) {
86 n.key.visit_with(self);
87 }
88
89 fn visit_method_prop(&mut self, n: &MethodProp) {
91 n.key.visit_with(self);
92 n.function.visit_with(self);
93 }
94
95 fn visit_setter_prop(&mut self, n: &SetterProp) {
97 n.key.visit_with(self);
98 n.param.visit_with(self);
99 }
100
101 fn visit_this_expr(&mut self, _: &ThisExpr) {
102 self.found = true;
103 }
104}
105
106pub fn contains_this_expr<N>(body: &N) -> bool
113where
114 N: VisitWith<ThisVisitor>,
115{
116 let mut visitor = ThisVisitor { found: false };
117 body.visit_with(&mut visitor);
118 visitor.found
119}
120
121pub fn contains_ident_ref<'a, N>(body: &N, ident: &'a Ident) -> bool
122where
123 N: VisitWith<IdentRefFinder<'a>>,
124{
125 let mut visitor = IdentRefFinder {
126 found: false,
127 ident,
128 };
129 body.visit_with(&mut visitor);
130 visitor.found
131}
132
133pub struct IdentRefFinder<'a> {
134 ident: &'a Ident,
135 found: bool,
136}
137
138impl Visit for IdentRefFinder<'_> {
139 noop_visit_type!();
140
141 fn visit_expr(&mut self, e: &Expr) {
142 e.visit_children_with(self);
143
144 match *e {
145 Expr::Ident(ref i) if i.ctxt == self.ident.ctxt && i.sym == self.ident.sym => {
146 self.found = true;
147 }
148 _ => {}
149 }
150 }
151}
152
153pub fn contains_arguments<N>(body: &N) -> bool
155where
156 N: VisitWith<ArgumentsFinder>,
157{
158 let mut visitor = ArgumentsFinder { found: false };
159 body.visit_with(&mut visitor);
160 visitor.found
161}
162
163pub struct ArgumentsFinder {
164 found: bool,
165}
166
167impl Visit for ArgumentsFinder {
168 noop_visit_type!();
169
170 fn visit_constructor(&mut self, _: &Constructor) {}
172
173 fn visit_expr(&mut self, e: &Expr) {
174 e.visit_children_with(self);
175
176 if e.is_ident_ref_to("arguments") {
177 self.found = true;
178 }
179 }
180
181 fn visit_function(&mut self, _: &Function) {}
183
184 fn visit_prop(&mut self, n: &Prop) {
185 n.visit_children_with(self);
186
187 if let Prop::Shorthand(i) = n {
188 if &*i.sym == "arguments" {
189 self.found = true;
190 }
191 }
192 }
193}
194
195pub trait StmtOrModuleItem: Send + Sync + Sized {
196 fn into_stmt(self) -> Result<Stmt, ModuleDecl>;
197
198 fn as_stmt(&self) -> Result<&Stmt, &ModuleDecl>;
199
200 fn as_stmt_mut(&mut self) -> Result<&mut Stmt, &mut ModuleDecl>;
201
202 fn from_stmt(stmt: Stmt) -> Self;
203
204 fn try_from_module_decl(decl: ModuleDecl) -> Result<Self, ModuleDecl>;
205}
206
207impl StmtOrModuleItem for Stmt {
208 #[inline]
209 fn into_stmt(self) -> Result<Stmt, ModuleDecl> {
210 Ok(self)
211 }
212
213 #[inline]
214 fn as_stmt(&self) -> Result<&Stmt, &ModuleDecl> {
215 Ok(self)
216 }
217
218 #[inline]
219 fn as_stmt_mut(&mut self) -> Result<&mut Stmt, &mut ModuleDecl> {
220 Ok(self)
221 }
222
223 #[inline]
224 fn from_stmt(stmt: Stmt) -> Self {
225 stmt
226 }
227
228 #[inline]
229 fn try_from_module_decl(decl: ModuleDecl) -> Result<Self, ModuleDecl> {
230 Err(decl)
231 }
232}
233
234impl StmtOrModuleItem for ModuleItem {
235 #[inline]
236 fn into_stmt(self) -> Result<Stmt, ModuleDecl> {
237 match self {
238 ModuleItem::ModuleDecl(v) => Err(v),
239 ModuleItem::Stmt(v) => Ok(v),
240 #[cfg(swc_ast_unknown)]
241 _ => panic!("unable to access unknown nodes"),
242 }
243 }
244
245 #[inline]
246 fn as_stmt(&self) -> Result<&Stmt, &ModuleDecl> {
247 match self {
248 ModuleItem::ModuleDecl(v) => Err(v),
249 ModuleItem::Stmt(v) => Ok(v),
250 #[cfg(swc_ast_unknown)]
251 _ => panic!("unable to access unknown nodes"),
252 }
253 }
254
255 #[inline]
256 fn as_stmt_mut(&mut self) -> Result<&mut Stmt, &mut ModuleDecl> {
257 match self {
258 ModuleItem::ModuleDecl(v) => Err(v),
259 ModuleItem::Stmt(v) => Ok(v),
260 #[cfg(swc_ast_unknown)]
261 _ => panic!("unable to access unknown nodes"),
262 }
263 }
264
265 #[inline]
266 fn from_stmt(stmt: Stmt) -> Self {
267 stmt.into()
268 }
269
270 #[inline]
271 fn try_from_module_decl(decl: ModuleDecl) -> Result<Self, ModuleDecl> {
272 Ok(decl.into())
273 }
274}
275
276pub trait ModuleItemLike: StmtLike {
277 fn try_into_module_decl(self) -> Result<ModuleDecl, Self> {
278 Err(self)
279 }
280 fn try_from_module_decl(decl: ModuleDecl) -> Result<Self, ModuleDecl> {
281 Err(decl)
282 }
283}
284
285pub trait StmtLike: Sized + 'static + Send + Sync + From<Stmt> {
286 fn try_into_stmt(self) -> Result<Stmt, Self>;
287 fn as_stmt(&self) -> Option<&Stmt>;
288 fn as_stmt_mut(&mut self) -> Option<&mut Stmt>;
289}
290
291impl ModuleItemLike for Stmt {}
292
293impl StmtLike for Stmt {
294 #[inline]
295 fn try_into_stmt(self) -> Result<Stmt, Self> {
296 Ok(self)
297 }
298
299 #[inline]
300 fn as_stmt(&self) -> Option<&Stmt> {
301 Some(self)
302 }
303
304 #[inline]
305 fn as_stmt_mut(&mut self) -> Option<&mut Stmt> {
306 Some(self)
307 }
308}
309
310impl ModuleItemLike for ModuleItem {
311 #[inline]
312 fn try_into_module_decl(self) -> Result<ModuleDecl, Self> {
313 match self {
314 ModuleItem::ModuleDecl(decl) => Ok(decl),
315 _ => Err(self),
316 }
317 }
318
319 #[inline]
320 fn try_from_module_decl(decl: ModuleDecl) -> Result<Self, ModuleDecl> {
321 Ok(decl.into())
322 }
323}
324impl StmtLike for ModuleItem {
325 #[inline]
326 fn try_into_stmt(self) -> Result<Stmt, Self> {
327 match self {
328 ModuleItem::Stmt(stmt) => Ok(stmt),
329 _ => Err(self),
330 }
331 }
332
333 #[inline]
334 fn as_stmt(&self) -> Option<&Stmt> {
335 match self {
336 ModuleItem::Stmt(stmt) => Some(stmt),
337 _ => None,
338 }
339 }
340
341 #[inline]
342 fn as_stmt_mut(&mut self) -> Option<&mut Stmt> {
343 match &mut *self {
344 ModuleItem::Stmt(stmt) => Some(stmt),
345 _ => None,
346 }
347 }
348}
349
350pub trait StmtLikeInjector<S>
352where
353 S: StmtLike,
354{
355 fn prepend_stmt(&mut self, insert_with: S);
356 fn prepend_stmts<I>(&mut self, insert_with: I)
357 where
358 I: IntoIterator<Item = S>;
359}
360
361impl<S> StmtLikeInjector<S> for Vec<S>
362where
363 S: StmtLike,
364{
365 fn prepend_stmt(&mut self, insert_with: S) {
367 let directive_pos = self
368 .iter()
369 .position(|stmt| !stmt.as_stmt().is_some_and(is_maybe_branch_directive))
370 .unwrap_or(self.len());
371
372 self.insert(directive_pos, insert_with);
373 }
374
375 fn prepend_stmts<I>(&mut self, insert_with: I)
377 where
378 I: IntoIterator<Item = S>,
379 {
380 let directive_pos = self
381 .iter()
382 .position(|stmt| !stmt.as_stmt().is_some_and(is_maybe_branch_directive))
383 .unwrap_or(self.len());
384
385 self.splice(directive_pos..directive_pos, insert_with);
386 }
387}
388
389pub type BoolValue = Value<bool>;
390
391pub trait IsEmpty {
392 fn is_empty(&self) -> bool;
393}
394
395impl IsEmpty for BlockStmt {
396 fn is_empty(&self) -> bool {
397 self.stmts.is_empty()
398 }
399}
400impl IsEmpty for CatchClause {
401 fn is_empty(&self) -> bool {
402 self.body.stmts.is_empty()
403 }
404}
405impl IsEmpty for Stmt {
406 fn is_empty(&self) -> bool {
407 match *self {
408 Stmt::Empty(_) => true,
409 Stmt::Block(ref b) => b.is_empty(),
410 _ => false,
411 }
412 }
413}
414
415impl<T: IsEmpty> IsEmpty for Option<T> {
416 #[inline]
417 fn is_empty(&self) -> bool {
418 match *self {
419 Some(ref node) => node.is_empty(),
420 None => true,
421 }
422 }
423}
424
425impl<T: IsEmpty> IsEmpty for Box<T> {
426 #[inline]
427 fn is_empty(&self) -> bool {
428 <T as IsEmpty>::is_empty(self)
429 }
430}
431
432impl<T> IsEmpty for Vec<T> {
433 #[inline]
434 fn is_empty(&self) -> bool {
435 self.is_empty()
436 }
437}
438
439pub fn extract_var_ids<T: VisitWith<Hoister>>(node: &T) -> Vec<Ident> {
441 let mut v = Hoister { vars: Vec::new() };
442 node.visit_with(&mut v);
443 v.vars
444}
445
446pub trait StmtExt {
447 fn as_stmt(&self) -> &Stmt;
448
449 fn extract_var_ids(&self) -> Vec<Ident> {
451 extract_var_ids(self.as_stmt())
452 }
453
454 fn extract_var_ids_as_var(&self) -> Option<VarDecl> {
455 let ids = self.extract_var_ids();
456 if ids.is_empty() {
457 return None;
458 }
459
460 Some(VarDecl {
461 kind: VarDeclKind::Var,
462 decls: ids
463 .into_iter()
464 .map(|i| VarDeclarator {
465 span: i.span,
466 name: i.into(),
467 init: None,
468 definite: false,
469 })
470 .collect(),
471 ..Default::default()
472 })
473 }
474
475 fn terminates(&self) -> bool {
477 fn terminates_many(
478 stmts: &[Stmt],
479 in_switch: bool,
480 allow_break: bool,
481 allow_throw: bool,
482 ) -> Result<bool, ()> {
483 stmts
484 .iter()
485 .rev()
486 .map(|s| terminates(s, in_switch, allow_break, allow_throw))
487 .try_fold(false, |acc, x| x.map(|v| acc || v))
488 }
489
490 fn terminates(
491 stmt: &Stmt,
492 in_switch: bool,
493 allow_break: bool,
494 allow_throw: bool,
495 ) -> Result<bool, ()> {
496 Ok(match stmt {
497 Stmt::Break(_) => {
498 if in_switch {
499 return Err(());
516 } else {
517 allow_break
518 }
519 }
520 Stmt::Throw(_) => allow_throw,
521 Stmt::Continue(_) | Stmt::Return(_) => true,
522 Stmt::Block(block) => {
523 terminates_many(&block.stmts, in_switch, allow_break, allow_throw)?
524 }
525 Stmt::If(IfStmt { cons, alt, .. }) => {
526 if let Some(alt) = alt {
527 terminates(cons, in_switch, allow_break, allow_throw)?
528 && terminates(alt, in_switch, allow_break, allow_throw)?
529 } else {
530 terminates(cons, in_switch, allow_break, allow_throw)?;
531
532 false
533 }
534 }
535 Stmt::Switch(s) => {
536 let mut has_default = false;
537 let mut has_non_empty_terminates = false;
538
539 for case in &s.cases {
540 if case.test.is_none() {
541 has_default = true
542 }
543
544 if !case.cons.is_empty() {
545 let t = terminates_many(&case.cons, true, false, allow_throw)
546 .unwrap_or(false);
547
548 if t {
549 has_non_empty_terminates = true
550 } else {
551 return Ok(false);
552 }
553 }
554 }
555
556 has_default && has_non_empty_terminates
557 }
558 Stmt::Try(t) => {
559 if let Some(h) = &t.handler {
560 terminates_many(&t.block.stmts, in_switch, allow_break, false)?
561 && terminates_many(&h.body.stmts, in_switch, allow_break, allow_throw)?
562 } else {
563 terminates_many(&t.block.stmts, in_switch, allow_break, allow_throw)?
564 }
565 }
566 _ => false,
567 })
568 }
569
570 terminates(self.as_stmt(), false, true, true) == Ok(true)
571 }
572
573 fn may_have_side_effects(&self, ctx: ExprCtx) -> bool {
574 fn may_have_side_effects(stmt: &Stmt, ctx: ExprCtx) -> bool {
575 match stmt {
576 Stmt::Block(block_stmt) => block_stmt
577 .stmts
578 .iter()
579 .any(|stmt| stmt.may_have_side_effects(ctx)),
580 Stmt::Empty(_) => false,
581 Stmt::Labeled(labeled_stmt) => labeled_stmt.body.may_have_side_effects(ctx),
582 Stmt::If(if_stmt) => {
583 if_stmt.test.may_have_side_effects(ctx)
584 || if_stmt.cons.may_have_side_effects(ctx)
585 || if_stmt
586 .alt
587 .as_ref()
588 .is_some_and(|stmt| stmt.may_have_side_effects(ctx))
589 }
590 Stmt::Switch(switch_stmt) => {
591 switch_stmt.discriminant.may_have_side_effects(ctx)
592 || switch_stmt.cases.iter().any(|case| {
593 case.test
594 .as_ref()
595 .is_some_and(|expr| expr.may_have_side_effects(ctx))
596 || case.cons.iter().any(|con| con.may_have_side_effects(ctx))
597 })
598 }
599 Stmt::Try(try_stmt) => {
600 try_stmt
601 .block
602 .stmts
603 .iter()
604 .any(|stmt| stmt.may_have_side_effects(ctx))
605 || try_stmt.handler.as_ref().is_some_and(|handler| {
606 handler
607 .body
608 .stmts
609 .iter()
610 .any(|stmt| stmt.may_have_side_effects(ctx))
611 })
612 || try_stmt.finalizer.as_ref().is_some_and(|finalizer| {
613 finalizer
614 .stmts
615 .iter()
616 .any(|stmt| stmt.may_have_side_effects(ctx))
617 })
618 }
619 Stmt::Decl(decl) => match decl {
620 Decl::Class(class_decl) => class_has_side_effect(ctx, &class_decl.class),
621 Decl::Fn(_) => !ctx.in_strict,
622 Decl::Var(var_decl) => var_decl.kind == VarDeclKind::Var,
623 _ => false,
624 },
625 Stmt::Expr(expr_stmt) => expr_stmt.expr.may_have_side_effects(ctx),
626 _ => true,
627 }
628 }
629
630 may_have_side_effects(self.as_stmt(), ctx)
631 }
632}
633
634impl StmtExt for Stmt {
635 fn as_stmt(&self) -> &Stmt {
636 self
637 }
638}
639
640impl StmtExt for Box<Stmt> {
641 fn as_stmt(&self) -> &Stmt {
642 self
643 }
644}
645
646pub struct Hoister {
647 vars: Vec<Ident>,
648}
649
650impl Visit for Hoister {
651 noop_visit_type!();
652
653 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
654
655 fn visit_assign_expr(&mut self, node: &AssignExpr) {
656 node.right.visit_children_with(self);
657 }
658
659 fn visit_assign_pat_prop(&mut self, node: &AssignPatProp) {
660 node.value.visit_with(self);
661
662 self.vars.push(node.key.clone().into());
663 }
664
665 fn visit_constructor(&mut self, _: &Constructor) {}
666
667 fn visit_fn_decl(&mut self, f: &FnDecl) {
668 self.vars.push(f.ident.clone());
669 }
670
671 fn visit_function(&mut self, _: &Function) {}
672
673 fn visit_getter_prop(&mut self, _: &GetterProp) {}
674
675 fn visit_pat(&mut self, p: &Pat) {
676 p.visit_children_with(self);
677
678 if let Pat::Ident(ref i) = *p {
679 self.vars.push(i.clone().into())
680 }
681 }
682
683 fn visit_setter_prop(&mut self, _: &SetterProp) {}
684
685 fn visit_var_decl(&mut self, v: &VarDecl) {
686 if v.kind != VarDeclKind::Var {
687 return;
688 }
689
690 v.visit_children_with(self)
691 }
692}
693
694#[derive(Debug, Clone, Copy)]
695
696pub struct ExprCtx {
697 pub unresolved_ctxt: SyntaxContext,
703
704 pub is_unresolved_ref_safe: bool,
706
707 pub in_strict: bool,
710
711 pub remaining_depth: u8,
716}
717
718pub trait ExprExt {
720 fn as_expr(&self) -> &Expr;
721
722 #[inline(always)]
724 fn is_immutable_value(&self) -> bool {
725 is_immutable_value(self.as_expr())
726 }
727
728 #[inline(always)]
729 fn is_number(&self) -> bool {
730 is_number(self.as_expr())
731 }
732
733 #[inline(always)]
735 fn is_str(&self) -> bool {
736 is_str(self.as_expr())
737 }
738
739 #[inline(always)]
740 fn is_array_lit(&self) -> bool {
741 is_array_lit(self.as_expr())
742 }
743
744 #[inline(always)]
746 fn is_nan(&self) -> bool {
747 is_nan(self.as_expr())
748 }
749
750 #[inline(always)]
751 fn is_undefined(&self, ctx: ExprCtx) -> bool {
752 is_undefined(self.as_expr(), ctx)
753 }
754
755 #[inline(always)]
756 fn is_void(&self) -> bool {
757 is_void(self.as_expr())
758 }
759
760 #[inline(always)]
762 fn is_global_ref_to(&self, ctx: ExprCtx, id: &str) -> bool {
763 is_global_ref_to(self.as_expr(), ctx, id)
764 }
765
766 #[inline(always)]
768 fn is_one_of_global_ref_to(&self, ctx: ExprCtx, ids: &[&str]) -> bool {
769 is_one_of_global_ref_to(self.as_expr(), ctx, ids)
770 }
771
772 #[inline(always)]
773 fn is_pure(&self, ctx: ExprCtx) -> bool {
774 self.as_pure_bool(ctx).is_known()
775 }
776
777 #[inline(always)]
779 fn as_pure_bool(&self, ctx: ExprCtx) -> BoolValue {
780 as_pure_bool(self.as_expr(), ctx)
781 }
782
783 #[inline(always)]
788 fn cast_to_bool(&self, ctx: ExprCtx) -> (Purity, BoolValue) {
789 cast_to_bool(self.as_expr(), ctx)
790 }
791
792 #[inline(always)]
793 fn cast_to_number(&self, ctx: ExprCtx) -> (Purity, Value<f64>) {
794 cast_to_number(self.as_expr(), ctx)
795 }
796
797 #[inline(always)]
801 fn as_pure_number(&self, ctx: ExprCtx) -> Value<f64> {
802 as_pure_number(self.as_expr(), ctx)
803 }
804
805 #[inline(always)]
807 fn as_pure_string(&self, ctx: ExprCtx) -> Value<Cow<'_, str>> {
808 as_pure_string(self.as_expr(), ctx)
809 }
810
811 #[inline(always)]
814 fn get_type(&self, ctx: ExprCtx) -> Value<Type> {
815 get_type(self.as_expr(), ctx)
816 }
817
818 #[inline(always)]
819 fn is_pure_callee(&self, ctx: ExprCtx) -> bool {
820 is_pure_callee(self.as_expr(), ctx)
821 }
822
823 #[inline(always)]
824 fn may_have_side_effects(&self, ctx: ExprCtx) -> bool {
825 may_have_side_effects(self.as_expr(), ctx)
826 }
827}
828
829pub fn class_has_side_effect(expr_ctx: ExprCtx, c: &Class) -> bool {
830 if let Some(e) = &c.super_class {
831 if e.may_have_side_effects(expr_ctx) {
832 return true;
833 }
834 }
835
836 for m in &c.body {
837 match m {
838 ClassMember::Method(p) => {
839 if let PropName::Computed(key) = &p.key {
840 if key.expr.may_have_side_effects(expr_ctx) {
841 return true;
842 }
843 }
844 }
845
846 ClassMember::ClassProp(p) => {
847 if let PropName::Computed(key) = &p.key {
848 if key.expr.may_have_side_effects(expr_ctx) {
849 return true;
850 }
851 }
852
853 if let Some(v) = &p.value {
854 if v.may_have_side_effects(expr_ctx) {
855 return true;
856 }
857 }
858 }
859 ClassMember::PrivateProp(p) => {
860 if let Some(v) = &p.value {
861 if v.may_have_side_effects(expr_ctx) {
862 return true;
863 }
864 }
865 }
866 ClassMember::StaticBlock(s) => {
867 if s.body
868 .stmts
869 .iter()
870 .any(|stmt| stmt.may_have_side_effects(expr_ctx))
871 {
872 return true;
873 }
874 }
875 _ => {}
876 }
877 }
878
879 false
880}
881
882fn and(lt: Value<Type>, rt: Value<Type>) -> Value<Type> {
883 if lt == rt {
884 return lt;
885 }
886 Unknown
887}
888
889fn may_be_str(ty: Value<Type>) -> bool {
891 match ty {
892 Known(BoolType) | Known(NullType) | Known(NumberType) | Known(UndefinedType) => false,
893 Known(ObjectType) | Known(StringType) | Unknown => true,
894 Known(SymbolType) => true,
896 }
897}
898
899pub fn num_from_str(s: &str) -> Value<f64> {
900 if s.contains('\u{000b}') {
901 return Unknown;
902 }
903
904 let s = s.trim();
905
906 if s.is_empty() {
907 return Known(0.0);
908 }
909
910 if s.len() >= 2 {
911 match &s.as_bytes()[..2] {
912 b"0x" | b"0X" => {
913 return match u64::from_str_radix(&s[2..], 16) {
914 Ok(n) => Known(n as f64),
915 Err(_) => Known(f64::NAN),
916 }
917 }
918 b"0o" | b"0O" => {
919 return match u64::from_str_radix(&s[2..], 8) {
920 Ok(n) => Known(n as f64),
921 Err(_) => Known(f64::NAN),
922 };
923 }
924 b"0b" | b"0B" => {
925 return match u64::from_str_radix(&s[2..], 2) {
926 Ok(n) => Known(n as f64),
927 Err(_) => Known(f64::NAN),
928 };
929 }
930 _ => {}
931 }
932 }
933
934 if (s.starts_with('-') || s.starts_with('+'))
935 && (s[1..].starts_with("0x") || s[1..].starts_with("0X"))
936 {
937 return Unknown;
939 }
940
941 match s {
944 "infinity" | "+infinity" | "-infinity" => return Unknown,
945 _ => {}
946 }
947
948 Known(s.parse().ok().unwrap_or(f64::NAN))
949}
950
951impl ExprExt for Box<Expr> {
952 #[inline(always)]
953 fn as_expr(&self) -> &Expr {
954 self
955 }
956}
957
958impl ExprExt for Expr {
959 #[inline(always)]
960 fn as_expr(&self) -> &Expr {
961 self
962 }
963}
964
965#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
966pub enum Purity {
967 MayBeImpure,
969 Pure,
971}
972impl Purity {
973 pub fn is_pure(self) -> bool {
975 self == Pure
976 }
977}
978
979impl Add for Purity {
980 type Output = Self;
981
982 fn add(self, rhs: Self) -> Self {
983 match (self, rhs) {
984 (Pure, Pure) => Pure,
985 _ => MayBeImpure,
986 }
987 }
988}
989
990pub fn to_int32(d: f64) -> i32 {
992 let id = d as i32;
993 if id as f64 == d {
994 return id;
996 }
997
998 if d.is_nan() || d.is_infinite() {
999 return 0;
1000 }
1001
1002 let d = if d >= 0.0 { d.floor() } else { d.ceil() };
1003
1004 const TWO32: f64 = 4_294_967_296.0;
1005 let d = d % TWO32;
1006 let l = d as i64;
1009 l as i32
1012}
1013
1014pub fn has_rest_pat<T: VisitWith<RestPatVisitor>>(node: &T) -> bool {
1034 let mut v = RestPatVisitor { found: false };
1035 node.visit_with(&mut v);
1036 v.found
1037}
1038
1039pub struct RestPatVisitor {
1040 found: bool,
1041}
1042
1043impl Visit for RestPatVisitor {
1044 noop_visit_type!();
1045
1046 fn visit_rest_pat(&mut self, _: &RestPat) {
1047 self.found = true;
1048 }
1049}
1050
1051pub fn is_literal<T>(node: &T) -> bool
1052where
1053 T: VisitWith<LiteralVisitor>,
1054{
1055 let (v, _) = calc_literal_cost(node, true);
1056 v
1057}
1058
1059#[inline(never)]
1060pub fn calc_literal_cost<T>(e: &T, allow_non_json_value: bool) -> (bool, usize)
1061where
1062 T: VisitWith<LiteralVisitor>,
1063{
1064 let mut v = LiteralVisitor {
1065 is_lit: true,
1066 cost: 0,
1067 allow_non_json_value,
1068 };
1069 e.visit_with(&mut v);
1070
1071 (v.is_lit, v.cost)
1072}
1073
1074pub struct LiteralVisitor {
1075 is_lit: bool,
1076 cost: usize,
1077 allow_non_json_value: bool,
1078}
1079
1080impl Visit for LiteralVisitor {
1081 noop_visit_type!();
1082
1083 fn visit_array_lit(&mut self, e: &ArrayLit) {
1084 if !self.is_lit {
1085 return;
1086 }
1087
1088 self.cost += 2 + e.elems.len();
1089
1090 e.visit_children_with(self);
1091
1092 for elem in &e.elems {
1093 if !self.allow_non_json_value && elem.is_none() {
1094 self.is_lit = false;
1095 }
1096 }
1097 }
1098
1099 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {
1100 self.is_lit = false
1101 }
1102
1103 fn visit_assign_expr(&mut self, _: &AssignExpr) {
1104 self.is_lit = false;
1105 }
1106
1107 fn visit_await_expr(&mut self, _: &AwaitExpr) {
1108 self.is_lit = false
1109 }
1110
1111 fn visit_bin_expr(&mut self, _: &BinExpr) {
1112 self.is_lit = false
1113 }
1114
1115 fn visit_call_expr(&mut self, _: &CallExpr) {
1116 self.is_lit = false
1117 }
1118
1119 fn visit_class_expr(&mut self, _: &ClassExpr) {
1120 self.is_lit = false
1121 }
1122
1123 fn visit_cond_expr(&mut self, _: &CondExpr) {
1124 self.is_lit = false;
1125 }
1126
1127 fn visit_expr(&mut self, e: &Expr) {
1128 if !self.is_lit {
1129 return;
1130 }
1131
1132 match *e {
1133 Expr::Ident(..) | Expr::Lit(Lit::Regex(..)) => self.is_lit = false,
1134 Expr::Tpl(ref tpl) if !tpl.exprs.is_empty() => self.is_lit = false,
1135 _ => e.visit_children_with(self),
1136 }
1137 }
1138
1139 fn visit_fn_expr(&mut self, _: &FnExpr) {
1140 self.is_lit = false;
1141 }
1142
1143 fn visit_invalid(&mut self, _: &Invalid) {
1144 self.is_lit = false;
1145 }
1146
1147 fn visit_member_expr(&mut self, m: &MemberExpr) {
1148 if m.obj.is_ident_ref_to("Symbol") {
1149 return;
1150 }
1151
1152 self.is_lit = false;
1153 }
1154
1155 fn visit_meta_prop_expr(&mut self, _: &MetaPropExpr) {
1156 self.is_lit = false
1157 }
1158
1159 fn visit_new_expr(&mut self, _: &NewExpr) {
1160 self.is_lit = false
1161 }
1162
1163 fn visit_number(&mut self, node: &Number) {
1164 if !self.allow_non_json_value && node.value.is_infinite() {
1165 self.is_lit = false;
1166 }
1167 }
1168
1169 fn visit_opt_chain_expr(&mut self, _: &OptChainExpr) {
1170 self.is_lit = false
1171 }
1172
1173 fn visit_private_name(&mut self, _: &PrivateName) {
1174 self.is_lit = false
1175 }
1176
1177 fn visit_prop(&mut self, p: &Prop) {
1178 if !self.is_lit {
1179 return;
1180 }
1181
1182 p.visit_children_with(self);
1183
1184 match p {
1185 Prop::KeyValue(..) => {
1186 self.cost += 1;
1187 }
1188 _ => self.is_lit = false,
1189 }
1190 }
1191
1192 fn visit_prop_name(&mut self, node: &PropName) {
1193 if !self.is_lit {
1194 return;
1195 }
1196
1197 node.visit_children_with(self);
1198
1199 match node {
1200 PropName::Str(ref s) => self.cost += 2 + s.value.len(),
1201 PropName::Ident(ref id) => self.cost += 2 + id.sym.len(),
1202 PropName::Num(..) => {
1203 self.cost += 5;
1205 }
1206 PropName::BigInt(_) => self.is_lit = false,
1207 PropName::Computed(..) => self.is_lit = false,
1208 #[cfg(swc_ast_unknown)]
1209 _ => (),
1210 }
1211 }
1212
1213 fn visit_seq_expr(&mut self, _: &SeqExpr) {
1214 self.is_lit = false
1215 }
1216
1217 fn visit_spread_element(&mut self, _: &SpreadElement) {
1218 self.is_lit = false;
1219 }
1220
1221 fn visit_tagged_tpl(&mut self, _: &TaggedTpl) {
1222 self.is_lit = false
1223 }
1224
1225 fn visit_this_expr(&mut self, _: &ThisExpr) {
1226 self.is_lit = false;
1227 }
1228
1229 fn visit_ts_const_assertion(&mut self, _: &TsConstAssertion) {
1230 self.is_lit = false
1231 }
1232
1233 fn visit_ts_non_null_expr(&mut self, _: &TsNonNullExpr) {
1234 self.is_lit = false
1235 }
1236
1237 fn visit_unary_expr(&mut self, _: &UnaryExpr) {
1238 self.is_lit = false;
1239 }
1240
1241 fn visit_update_expr(&mut self, _: &UpdateExpr) {
1242 self.is_lit = false;
1243 }
1244
1245 fn visit_yield_expr(&mut self, _: &YieldExpr) {
1246 self.is_lit = false
1247 }
1248}
1249
1250pub fn is_simple_pure_expr(expr: &Expr, pure_getters: bool) -> bool {
1251 match expr {
1252 Expr::Ident(..) | Expr::This(..) | Expr::Lit(..) => true,
1253 Expr::Unary(UnaryExpr {
1254 op: op!("void") | op!("!"),
1255 arg,
1256 ..
1257 }) => is_simple_pure_expr(arg, pure_getters),
1258 Expr::Member(m) if pure_getters => is_simple_pure_member_expr(m, pure_getters),
1259 _ => false,
1260 }
1261}
1262
1263pub fn is_simple_pure_member_expr(m: &MemberExpr, pure_getters: bool) -> bool {
1264 match &m.prop {
1265 MemberProp::Ident(..) | MemberProp::PrivateName(..) => {
1266 is_simple_pure_expr(&m.obj, pure_getters)
1267 }
1268 MemberProp::Computed(c) => {
1269 is_simple_pure_expr(&c.expr, pure_getters) && is_simple_pure_expr(&m.obj, pure_getters)
1270 }
1271 #[cfg(swc_ast_unknown)]
1272 _ => false,
1273 }
1274}
1275
1276fn sym_for_expr(expr: &Expr) -> Option<String> {
1277 match expr {
1278 Expr::Lit(Lit::Str(s)) => Some(s.value.to_string()),
1279 Expr::This(_) => Some("this".to_string()),
1280
1281 Expr::Ident(ident)
1282 | Expr::Fn(FnExpr {
1283 ident: Some(ident), ..
1284 })
1285 | Expr::Class(ClassExpr {
1286 ident: Some(ident), ..
1287 }) => Some(ident.sym.to_string()),
1288
1289 Expr::OptChain(OptChainExpr { base, .. }) => match &**base {
1290 OptChainBase::Call(OptCall { callee: expr, .. }) => sym_for_expr(expr),
1291 OptChainBase::Member(MemberExpr {
1292 prop: MemberProp::Ident(ident),
1293 obj,
1294 ..
1295 }) => Some(format!(
1296 "{}_{}",
1297 sym_for_expr(obj).unwrap_or_default(),
1298 ident.sym
1299 )),
1300
1301 OptChainBase::Member(MemberExpr {
1302 prop: MemberProp::Computed(ComputedPropName { expr, .. }),
1303 obj,
1304 ..
1305 }) => Some(format!(
1306 "{}_{}",
1307 sym_for_expr(obj).unwrap_or_default(),
1308 sym_for_expr(expr).unwrap_or_default()
1309 )),
1310 _ => None,
1311 },
1312 Expr::Call(CallExpr {
1313 callee: Callee::Expr(expr),
1314 ..
1315 }) => sym_for_expr(expr),
1316
1317 Expr::SuperProp(SuperPropExpr {
1318 prop: SuperProp::Ident(ident),
1319 ..
1320 }) => Some(format!("super_{}", ident.sym)),
1321
1322 Expr::SuperProp(SuperPropExpr {
1323 prop: SuperProp::Computed(ComputedPropName { expr, .. }),
1324 ..
1325 }) => Some(format!("super_{}", sym_for_expr(expr).unwrap_or_default())),
1326
1327 Expr::Member(MemberExpr {
1328 prop: MemberProp::Ident(ident),
1329 obj,
1330 ..
1331 }) => Some(format!(
1332 "{}_{}",
1333 sym_for_expr(obj).unwrap_or_default(),
1334 ident.sym
1335 )),
1336
1337 Expr::Member(MemberExpr {
1338 prop: MemberProp::Computed(ComputedPropName { expr, .. }),
1339 obj,
1340 ..
1341 }) => Some(format!(
1342 "{}_{}",
1343 sym_for_expr(obj).unwrap_or_default(),
1344 sym_for_expr(expr).unwrap_or_default()
1345 )),
1346
1347 _ => None,
1348 }
1349}
1350
1351pub fn alias_ident_for(expr: &Expr, default: &str) -> Ident {
1353 let ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
1354 let span = expr.span();
1355
1356 let mut sym = sym_for_expr(expr).unwrap_or_else(|| default.to_string());
1357
1358 if let Err(s) = Ident::verify_symbol(&sym) {
1359 sym = s;
1360 }
1361
1362 if !sym.starts_with('_') {
1363 sym = format!("_{sym}")
1364 }
1365 quote_ident!(ctxt, span, sym)
1366}
1367
1368pub fn alias_ident_for_simple_assign_tatget(expr: &SimpleAssignTarget, default: &str) -> Ident {
1370 let ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
1371
1372 let span = expr.span();
1373
1374 let mut sym = match expr {
1375 SimpleAssignTarget::Ident(i) => Some(i.sym.to_string()),
1376
1377 SimpleAssignTarget::SuperProp(SuperPropExpr {
1378 prop: SuperProp::Ident(ident),
1379 ..
1380 }) => Some(format!("super_{}", ident.sym)),
1381
1382 SimpleAssignTarget::SuperProp(SuperPropExpr {
1383 prop: SuperProp::Computed(ComputedPropName { expr, .. }),
1384 ..
1385 }) => Some(format!("super_{}", sym_for_expr(expr).unwrap_or_default())),
1386
1387 SimpleAssignTarget::Member(MemberExpr {
1388 prop: MemberProp::Ident(ident),
1389 obj,
1390 ..
1391 }) => Some(format!(
1392 "{}_{}",
1393 sym_for_expr(obj).unwrap_or_default(),
1394 ident.sym
1395 )),
1396
1397 SimpleAssignTarget::Member(MemberExpr {
1398 prop: MemberProp::Computed(ComputedPropName { expr, .. }),
1399 obj,
1400 ..
1401 }) => Some(format!(
1402 "{}_{}",
1403 sym_for_expr(obj).unwrap_or_default(),
1404 sym_for_expr(expr).unwrap_or_default()
1405 )),
1406 _ => None,
1407 }
1408 .unwrap_or_else(|| default.to_string());
1409
1410 if let Err(s) = Ident::verify_symbol(&sym) {
1411 sym = s;
1412 }
1413
1414 if !sym.starts_with('_') {
1415 sym = format!("_{sym}")
1416 }
1417 quote_ident!(ctxt, span, sym)
1418}
1419
1420pub fn alias_if_required(expr: &Expr, default: &str) -> (Ident, bool) {
1422 if let Expr::Ident(ref i) = *expr {
1423 return (Ident::new(i.sym.clone(), i.span, i.ctxt), false);
1424 }
1425
1426 (alias_ident_for(expr, default), true)
1427}
1428
1429pub fn prop_name_to_expr(p: PropName) -> Expr {
1430 match p {
1431 PropName::Ident(i) => i.into(),
1432 PropName::Str(s) => Lit::Str(s).into(),
1433 PropName::Num(n) => Lit::Num(n).into(),
1434 PropName::BigInt(b) => Lit::BigInt(b).into(),
1435 PropName::Computed(c) => *c.expr,
1436 #[cfg(swc_ast_unknown)]
1437 _ => panic!("unable to access unknown nodes"),
1438 }
1439}
1440pub fn prop_name_to_expr_value(p: PropName) -> Expr {
1444 match p {
1445 PropName::Ident(i) => Lit::Str(Str {
1446 span: i.span,
1447 raw: None,
1448 value: i.sym,
1449 })
1450 .into(),
1451 PropName::Str(s) => Lit::Str(s).into(),
1452 PropName::Num(n) => Lit::Num(n).into(),
1453 PropName::BigInt(b) => Lit::BigInt(b).into(),
1454 PropName::Computed(c) => *c.expr,
1455 #[cfg(swc_ast_unknown)]
1456 _ => panic!("unable to access unknown nodes"),
1457 }
1458}
1459
1460pub fn prop_name_to_member_prop(prop_name: PropName) -> MemberProp {
1461 match prop_name {
1462 PropName::Ident(i) => MemberProp::Ident(i),
1463 PropName::Str(s) => MemberProp::Computed(ComputedPropName {
1464 span: DUMMY_SP,
1465 expr: s.into(),
1466 }),
1467 PropName::Num(n) => MemberProp::Computed(ComputedPropName {
1468 span: DUMMY_SP,
1469 expr: n.into(),
1470 }),
1471 PropName::Computed(c) => MemberProp::Computed(c),
1472 PropName::BigInt(b) => MemberProp::Computed(ComputedPropName {
1473 span: DUMMY_SP,
1474 expr: b.into(),
1475 }),
1476 #[cfg(swc_ast_unknown)]
1477 _ => panic!("unable to access unknown nodes"),
1478 }
1479}
1480
1481pub fn default_constructor_with_span(has_super: bool, super_call_span: Span) -> Constructor {
1484 trace!(has_super = has_super, "Creating a default constructor");
1485 let super_call_span = super_call_span.with_hi(super_call_span.lo);
1486
1487 Constructor {
1488 span: DUMMY_SP,
1489 key: PropName::Ident(atom!("constructor").into()),
1490 is_optional: false,
1491 params: if has_super {
1492 vec![ParamOrTsParamProp::Param(Param {
1493 span: DUMMY_SP,
1494 decorators: Vec::new(),
1495 pat: Pat::Rest(RestPat {
1496 span: DUMMY_SP,
1497 dot3_token: DUMMY_SP,
1498 arg: Box::new(Pat::Ident(quote_ident!("args").into())),
1499 type_ann: Default::default(),
1500 }),
1501 })]
1502 } else {
1503 Vec::new()
1504 },
1505 body: Some(BlockStmt {
1506 stmts: if has_super {
1507 vec![CallExpr {
1508 span: super_call_span,
1509 callee: Callee::Super(Super { span: DUMMY_SP }),
1510 args: vec![ExprOrSpread {
1511 spread: Some(DUMMY_SP),
1512 expr: Box::new(Expr::Ident(quote_ident!("args").into())),
1513 }],
1514 ..Default::default()
1515 }
1516 .into_stmt()]
1517 } else {
1518 Vec::new()
1519 },
1520 ..Default::default()
1521 }),
1522 ..Default::default()
1523 }
1524}
1525
1526pub fn is_rest_arguments(e: &ExprOrSpread) -> bool {
1528 if e.spread.is_none() {
1529 return false;
1530 }
1531
1532 e.expr.is_ident_ref_to("arguments")
1533}
1534
1535pub fn opt_chain_test(
1536 left: Box<Expr>,
1537 right: Box<Expr>,
1538 span: Span,
1539 no_document_all: bool,
1540) -> Expr {
1541 if no_document_all {
1542 BinExpr {
1543 span,
1544 left,
1545 op: op!("=="),
1546 right: Lit::Null(Null { span: DUMMY_SP }).into(),
1547 }
1548 .into()
1549 } else {
1550 BinExpr {
1551 span,
1552 left: BinExpr {
1553 span: DUMMY_SP,
1554 left,
1555 op: op!("==="),
1556 right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
1557 }
1558 .into(),
1559 op: op!("||"),
1560 right: BinExpr {
1561 span: DUMMY_SP,
1562 left: right,
1563 op: op!("==="),
1564 right: Expr::undefined(DUMMY_SP),
1565 }
1566 .into(),
1567 }
1568 .into()
1569 }
1570}
1571
1572#[inline]
1574pub fn prepend_stmt<T: StmtLike>(stmts: &mut Vec<T>, stmt: T) {
1575 stmts.prepend_stmt(stmt);
1576}
1577
1578pub fn is_maybe_branch_directive(stmt: &Stmt) -> bool {
1580 match stmt {
1581 Stmt::Expr(ExprStmt { ref expr, .. }) if matches!(&**expr, Expr::Lit(Lit::Str(..))) => true,
1582 _ => false,
1583 }
1584}
1585
1586#[inline]
1588pub fn prepend_stmts<T: StmtLike>(to: &mut Vec<T>, stmts: impl ExactSizeIterator<Item = T>) {
1589 to.prepend_stmts(stmts);
1590}
1591
1592pub trait IsDirective {
1593 fn as_ref(&self) -> Option<&Stmt>;
1594
1595 fn directive_continue(&self) -> bool {
1596 self.as_ref().is_some_and(Stmt::can_precede_directive)
1597 }
1598 fn is_use_strict(&self) -> bool {
1599 self.as_ref().is_some_and(Stmt::is_use_strict)
1600 }
1601}
1602
1603impl IsDirective for Stmt {
1604 fn as_ref(&self) -> Option<&Stmt> {
1605 Some(self)
1606 }
1607}
1608
1609impl IsDirective for ModuleItem {
1610 fn as_ref(&self) -> Option<&Stmt> {
1611 self.as_stmt()
1612 }
1613}
1614
1615impl IsDirective for &ModuleItem {
1616 fn as_ref(&self) -> Option<&Stmt> {
1617 self.as_stmt()
1618 }
1619}
1620
1621pub struct DestructuringFinder<I: IdentLike> {
1623 pub found: Vec<I>,
1624}
1625
1626pub fn find_pat_ids<T, I: IdentLike>(node: &T) -> Vec<I>
1630where
1631 T: VisitWith<DestructuringFinder<I>>,
1632{
1633 let mut v = DestructuringFinder { found: Vec::new() };
1634 node.visit_with(&mut v);
1635
1636 v.found
1637}
1638
1639impl<I: IdentLike> Visit for DestructuringFinder<I> {
1640 fn visit_expr(&mut self, _: &Expr) {}
1642
1643 fn visit_ident(&mut self, i: &Ident) {
1644 self.found.push(I::from_ident(i));
1645 }
1646
1647 fn visit_jsx_member_expr(&mut self, n: &JSXMemberExpr) {
1648 n.obj.visit_with(self);
1649 }
1650
1651 fn visit_prop_name(&mut self, _: &PropName) {}
1653
1654 fn visit_ts_type(&mut self, _: &TsType) {}
1655}
1656
1657pub struct BindingIdentifierVisitor<F>
1659where
1660 F: for<'a> FnMut(&'a BindingIdent),
1661{
1662 op: F,
1663}
1664
1665pub fn for_each_binding_ident<T, F>(node: &T, op: F)
1668where
1669 T: VisitWith<BindingIdentifierVisitor<F>>,
1670 F: for<'a> FnMut(&'a BindingIdent),
1671{
1672 let mut v = BindingIdentifierVisitor { op };
1673 node.visit_with(&mut v);
1674}
1675
1676impl<F> Visit for BindingIdentifierVisitor<F>
1677where
1678 F: for<'a> FnMut(&'a BindingIdent),
1679{
1680 noop_visit_type!();
1681
1682 fn visit_expr(&mut self, _: &Expr) {}
1684
1685 fn visit_binding_ident(&mut self, i: &BindingIdent) {
1686 (self.op)(i);
1687 }
1688}
1689
1690pub fn is_valid_ident(s: &Atom) -> bool {
1691 if s.is_empty() {
1692 return false;
1693 }
1694
1695 Ident::verify_symbol(s).is_ok()
1696}
1697
1698pub fn is_valid_prop_ident(s: &str) -> bool {
1699 s.starts_with(Ident::is_valid_start) && s.chars().all(Ident::is_valid_continue)
1700}
1701
1702pub fn drop_span<T>(mut t: T) -> T
1703where
1704 T: VisitMutWith<DropSpan>,
1705{
1706 t.visit_mut_with(&mut DropSpan {});
1707 t
1708}
1709
1710pub struct DropSpan;
1711
1712impl Pass for DropSpan {
1713 fn process(&mut self, program: &mut Program) {
1714 program.visit_mut_with(self);
1715 }
1716}
1717
1718impl VisitMut for DropSpan {
1719 fn visit_mut_span(&mut self, span: &mut Span) {
1720 *span = DUMMY_SP;
1721 }
1722}
1723
1724pub struct IdentUsageFinder<'a> {
1726 ident: &'a Ident,
1727 found: bool,
1728}
1729
1730impl Parallel for IdentUsageFinder<'_> {
1731 fn create(&self) -> Self {
1732 Self {
1733 ident: self.ident,
1734 found: self.found,
1735 }
1736 }
1737
1738 fn merge(&mut self, other: Self) {
1739 self.found = self.found || other.found;
1740 }
1741}
1742
1743impl Visit for IdentUsageFinder<'_> {
1744 noop_visit_type!();
1745
1746 visit_obj_and_computed!();
1747
1748 fn visit_ident(&mut self, i: &Ident) {
1749 if i.ctxt == self.ident.ctxt && i.sym == self.ident.sym {
1750 self.found = true;
1751 }
1752 }
1753
1754 fn visit_class_members(&mut self, n: &[ClassMember]) {
1755 self.maybe_par(*LIGHT_TASK_PARALLELS, n, |v, item| {
1756 item.visit_with(v);
1757 });
1758 }
1759
1760 fn visit_expr_or_spreads(&mut self, n: &[ExprOrSpread]) {
1761 self.maybe_par(*LIGHT_TASK_PARALLELS, n, |v, item| {
1762 item.visit_with(v);
1763 });
1764 }
1765
1766 fn visit_exprs(&mut self, exprs: &[Box<Expr>]) {
1767 self.maybe_par(*LIGHT_TASK_PARALLELS, exprs, |v, expr| {
1768 expr.visit_with(v);
1769 });
1770 }
1771
1772 fn visit_module_items(&mut self, n: &[ModuleItem]) {
1773 self.maybe_par(*LIGHT_TASK_PARALLELS, n, |v, item| {
1774 item.visit_with(v);
1775 });
1776 }
1777
1778 fn visit_opt_vec_expr_or_spreads(&mut self, n: &[Option<ExprOrSpread>]) {
1779 self.maybe_par(*LIGHT_TASK_PARALLELS, n, |v, item| {
1780 if let Some(e) = item {
1781 e.visit_with(v);
1782 }
1783 });
1784 }
1785
1786 fn visit_stmts(&mut self, stmts: &[Stmt]) {
1787 self.maybe_par(*LIGHT_TASK_PARALLELS, stmts, |v, stmt| {
1788 stmt.visit_with(v);
1789 });
1790 }
1791
1792 fn visit_var_declarators(&mut self, n: &[VarDeclarator]) {
1793 self.maybe_par(*LIGHT_TASK_PARALLELS, n, |v, item| {
1794 item.visit_with(v);
1795 });
1796 }
1797}
1798
1799impl<'a> IdentUsageFinder<'a> {
1800 pub fn find<N>(ident: &'a Ident, node: &N) -> bool
1801 where
1802 N: VisitWith<Self>,
1803 {
1804 let mut v = IdentUsageFinder {
1805 ident,
1806 found: false,
1807 };
1808 node.visit_with(&mut v);
1809 v.found
1810 }
1811}
1812
1813impl ExprCtx {
1814 pub fn consume_depth(self) -> Option<Self> {
1815 if self.remaining_depth == 0 {
1816 return None;
1817 }
1818
1819 Some(Self {
1820 remaining_depth: self.remaining_depth - 1,
1821 ..self
1822 })
1823 }
1824
1825 pub fn preserve_effects<I>(self, span: Span, val: Box<Expr>, exprs: I) -> Box<Expr>
1828 where
1829 I: IntoIterator<Item = Box<Expr>>,
1830 {
1831 let mut exprs = exprs.into_iter().fold(Vec::new(), |mut v, e| {
1832 self.extract_side_effects_to(&mut v, *e);
1833 v
1834 });
1835
1836 if exprs.is_empty() {
1837 val
1838 } else {
1839 exprs.push(val);
1840
1841 SeqExpr { exprs, span }.into()
1842 }
1843 }
1844
1845 #[allow(clippy::vec_box)]
1850 pub fn extract_side_effects_to(self, to: &mut Vec<Box<Expr>>, expr: Expr) {
1851 match expr {
1852 Expr::Lit(..)
1853 | Expr::This(..)
1854 | Expr::Fn(..)
1855 | Expr::Arrow(..)
1856 | Expr::PrivateName(..) => {}
1857
1858 Expr::Ident(..) => {
1859 if expr.may_have_side_effects(self) {
1860 to.push(Box::new(expr));
1861 }
1862 }
1863
1864 Expr::Update(_) | Expr::Assign(_) | Expr::Yield(_) | Expr::Await(_) => {
1866 to.push(Box::new(expr))
1867 }
1868
1869 Expr::MetaProp(_) => to.push(Box::new(expr)),
1871
1872 Expr::Call(_) => to.push(Box::new(expr)),
1873 Expr::New(e) => {
1874 if let Expr::Ident(Ident { ref sym, .. }) = *e.callee {
1876 if *sym == "Date" && e.args.is_empty() {
1877 return;
1878 }
1879 }
1880
1881 to.push(e.into())
1882 }
1883 Expr::Member(_) | Expr::SuperProp(_) => to.push(Box::new(expr)),
1884
1885 Expr::Cond(_) => to.push(Box::new(expr)),
1888
1889 Expr::Unary(UnaryExpr {
1890 op: op!("typeof"),
1891 arg,
1892 ..
1893 }) => {
1894 if arg.is_ident() {
1900 return;
1901 }
1902 self.extract_side_effects_to(to, *arg)
1903 }
1904
1905 Expr::Unary(UnaryExpr { arg, .. }) => self.extract_side_effects_to(to, *arg),
1906
1907 Expr::Bin(BinExpr { op, .. }) if op.may_short_circuit() => {
1908 to.push(Box::new(expr));
1909 }
1910 Expr::Bin(BinExpr { left, right, .. }) => {
1911 self.extract_side_effects_to(to, *left);
1912 self.extract_side_effects_to(to, *right);
1913 }
1914 Expr::Seq(SeqExpr { exprs, .. }) => exprs
1915 .into_iter()
1916 .for_each(|e| self.extract_side_effects_to(to, *e)),
1917
1918 Expr::Paren(e) => self.extract_side_effects_to(to, *e.expr),
1919
1920 Expr::Object(ObjectLit {
1921 span, mut props, ..
1922 }) => {
1923 let mut has_spread = false;
1925 props.retain(|node| match node {
1926 PropOrSpread::Prop(node) => match &**node {
1927 Prop::Shorthand(..) => false,
1928 Prop::KeyValue(KeyValueProp { key, value }) => {
1929 if let PropName::Computed(e) = key {
1930 if e.expr.may_have_side_effects(self) {
1931 return true;
1932 }
1933 }
1934
1935 value.may_have_side_effects(self)
1936 }
1937 Prop::Getter(GetterProp { key, .. })
1938 | Prop::Setter(SetterProp { key, .. })
1939 | Prop::Method(MethodProp { key, .. }) => {
1940 if let PropName::Computed(e) = key {
1941 e.expr.may_have_side_effects(self)
1942 } else {
1943 false
1944 }
1945 }
1946 Prop::Assign(..) => {
1947 unreachable!("assign property in object literal is not a valid syntax")
1948 }
1949 #[cfg(swc_ast_unknown)]
1950 _ => true,
1951 },
1952 PropOrSpread::Spread(SpreadElement { .. }) => {
1953 has_spread = true;
1954 true
1955 }
1956 #[cfg(swc_ast_unknown)]
1957 _ => true,
1958 });
1959
1960 if has_spread {
1961 to.push(ObjectLit { span, props }.into())
1962 } else {
1963 props.into_iter().for_each(|prop| match prop {
1964 PropOrSpread::Prop(node) => match *node {
1965 Prop::Shorthand(..) => {}
1966 Prop::KeyValue(KeyValueProp { key, value }) => {
1967 if let PropName::Computed(e) = key {
1968 self.extract_side_effects_to(to, *e.expr);
1969 }
1970
1971 self.extract_side_effects_to(to, *value)
1972 }
1973 Prop::Getter(GetterProp { key, .. })
1974 | Prop::Setter(SetterProp { key, .. })
1975 | Prop::Method(MethodProp { key, .. }) => {
1976 if let PropName::Computed(e) = key {
1977 self.extract_side_effects_to(to, *e.expr)
1978 }
1979 }
1980 Prop::Assign(..) => {
1981 unreachable!(
1982 "assign property in object literal is not a valid syntax"
1983 )
1984 }
1985 #[cfg(swc_ast_unknown)]
1986 _ => panic!("unable to access unknown nodes"),
1987 },
1988 _ => unreachable!(),
1989 })
1990 }
1991 }
1992
1993 Expr::Array(ArrayLit { elems, .. }) => {
1994 elems.into_iter().flatten().fold(to, |v, e| {
1995 self.extract_side_effects_to(v, *e.expr);
1996
1997 v
1998 });
1999 }
2000
2001 Expr::TaggedTpl(TaggedTpl { tag, tpl, .. }) => {
2002 self.extract_side_effects_to(to, *tag);
2003
2004 tpl.exprs
2005 .into_iter()
2006 .for_each(|e| self.extract_side_effects_to(to, *e));
2007 }
2008 Expr::Tpl(Tpl { exprs, .. }) => {
2009 exprs
2010 .into_iter()
2011 .for_each(|e| self.extract_side_effects_to(to, *e));
2012 }
2013 Expr::Class(ClassExpr { .. }) => unimplemented!("add_effects for class expression"),
2014
2015 Expr::JSXMember(..)
2016 | Expr::JSXNamespacedName(..)
2017 | Expr::JSXEmpty(..)
2018 | Expr::JSXElement(..)
2019 | Expr::JSXFragment(..) => to.push(Box::new(expr)),
2020
2021 Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
2022 | Expr::TsNonNull(TsNonNullExpr { expr, .. })
2023 | Expr::TsAs(TsAsExpr { expr, .. })
2024 | Expr::TsConstAssertion(TsConstAssertion { expr, .. })
2025 | Expr::TsInstantiation(TsInstantiation { expr, .. })
2026 | Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) => {
2027 self.extract_side_effects_to(to, *expr)
2028 }
2029 Expr::OptChain(..) => to.push(Box::new(expr)),
2030
2031 Expr::Invalid(..) => unreachable!(),
2032 #[cfg(swc_ast_unknown)]
2033 _ => to.push(Box::new(expr)),
2034 }
2035 }
2036}
2037
2038pub fn prop_name_eq(p: &PropName, key: &str) -> bool {
2039 match p {
2040 PropName::Ident(i) => i.sym == *key,
2041 PropName::Str(s) => s.value == *key,
2042 PropName::Num(n) => n.value.to_string() == *key,
2043 PropName::BigInt(_) => false,
2044 PropName::Computed(e) => match &*e.expr {
2045 Expr::Lit(Lit::Str(Str { value, .. })) => *value == *key,
2046 _ => false,
2047 },
2048 #[cfg(swc_ast_unknown)]
2049 _ => false,
2050 }
2051}
2052
2053pub fn replace_ident<T>(node: &mut T, from: Id, to: &Ident)
2061where
2062 T: for<'any> VisitMutWith<IdentReplacer<'any>>,
2063{
2064 node.visit_mut_with(&mut IdentReplacer { from, to })
2065}
2066
2067pub struct IdentReplacer<'a> {
2068 from: Id,
2069 to: &'a Ident,
2070}
2071
2072impl VisitMut for IdentReplacer<'_> {
2073 noop_visit_mut_type!();
2074
2075 visit_mut_obj_and_computed!();
2076
2077 fn visit_mut_prop(&mut self, node: &mut Prop) {
2078 match node {
2079 Prop::Shorthand(i) => {
2080 let cloned = i.clone();
2081 i.visit_mut_with(self);
2082 if i.sym != cloned.sym || i.ctxt != cloned.ctxt {
2083 *node = Prop::KeyValue(KeyValueProp {
2084 key: PropName::Ident(IdentName::new(cloned.sym, cloned.span)),
2085 value: i.clone().into(),
2086 });
2087 }
2088 }
2089 _ => {
2090 node.visit_mut_children_with(self);
2091 }
2092 }
2093 }
2094
2095 fn visit_mut_ident(&mut self, node: &mut Ident) {
2096 if node.sym == self.from.0 && node.ctxt == self.from.1 {
2097 *node = self.to.clone();
2098 }
2099 }
2100}
2101
2102pub struct BindingCollector<I>
2103where
2104 I: IdentLike + Eq + Hash + Send + Sync,
2105{
2106 only: Option<SyntaxContext>,
2107 bindings: FxHashSet<I>,
2108 is_pat_decl: bool,
2109}
2110
2111impl<I> BindingCollector<I>
2112where
2113 I: IdentLike + Eq + Hash + Send + Sync,
2114{
2115 fn add(&mut self, i: &Ident) {
2116 if let Some(only) = self.only {
2117 if only != i.ctxt {
2118 return;
2119 }
2120 }
2121
2122 self.bindings.insert(I::from_ident(i));
2123 }
2124}
2125
2126impl<I> Visit for BindingCollector<I>
2127where
2128 I: IdentLike + Eq + Hash + Send + Sync,
2129{
2130 noop_visit_type!();
2131
2132 fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
2133 let old = self.is_pat_decl;
2134
2135 for p in &n.params {
2136 self.is_pat_decl = true;
2137 p.visit_with(self);
2138 }
2139
2140 n.body.visit_with(self);
2141 self.is_pat_decl = old;
2142 }
2143
2144 fn visit_assign_pat_prop(&mut self, node: &AssignPatProp) {
2145 node.value.visit_with(self);
2146
2147 if self.is_pat_decl {
2148 self.add(&node.key.clone().into());
2149 }
2150 }
2151
2152 fn visit_class_decl(&mut self, node: &ClassDecl) {
2153 node.visit_children_with(self);
2154
2155 self.add(&node.ident);
2156 }
2157
2158 fn visit_expr(&mut self, node: &Expr) {
2159 let old = self.is_pat_decl;
2160 self.is_pat_decl = false;
2161 node.visit_children_with(self);
2162 self.is_pat_decl = old;
2163 }
2164
2165 fn visit_export_default_decl(&mut self, e: &ExportDefaultDecl) {
2166 match &e.decl {
2167 DefaultDecl::Class(ClassExpr {
2168 ident: Some(ident), ..
2169 }) => {
2170 self.add(ident);
2171 }
2172 DefaultDecl::Fn(FnExpr {
2173 ident: Some(ident),
2174 function: f,
2175 }) if f.body.is_some() => {
2176 self.add(ident);
2177 }
2178 _ => {}
2179 }
2180 e.visit_children_with(self);
2181 }
2182
2183 fn visit_fn_decl(&mut self, node: &FnDecl) {
2184 node.visit_children_with(self);
2185
2186 self.add(&node.ident);
2187 }
2188
2189 fn visit_import_default_specifier(&mut self, node: &ImportDefaultSpecifier) {
2190 self.add(&node.local);
2191 }
2192
2193 fn visit_import_named_specifier(&mut self, node: &ImportNamedSpecifier) {
2194 self.add(&node.local);
2195 }
2196
2197 fn visit_import_star_as_specifier(&mut self, node: &ImportStarAsSpecifier) {
2198 self.add(&node.local);
2199 }
2200
2201 fn visit_param(&mut self, node: &Param) {
2202 let old = self.is_pat_decl;
2203 self.is_pat_decl = true;
2204 node.visit_children_with(self);
2205 self.is_pat_decl = old;
2206 }
2207
2208 fn visit_pat(&mut self, node: &Pat) {
2209 node.visit_children_with(self);
2210
2211 if self.is_pat_decl {
2212 if let Pat::Ident(i) = node {
2213 self.add(&i.clone().into())
2214 }
2215 }
2216 }
2217
2218 fn visit_var_declarator(&mut self, node: &VarDeclarator) {
2219 let old = self.is_pat_decl;
2220 self.is_pat_decl = true;
2221 node.name.visit_with(self);
2222
2223 self.is_pat_decl = false;
2224 node.init.visit_with(self);
2225 self.is_pat_decl = old;
2226 }
2227}
2228
2229pub fn collect_decls<I, N>(n: &N) -> FxHashSet<I>
2231where
2232 I: IdentLike + Eq + Hash + Send + Sync,
2233 N: VisitWith<BindingCollector<I>>,
2234{
2235 let mut v = BindingCollector {
2236 only: None,
2237 bindings: Default::default(),
2238 is_pat_decl: false,
2239 };
2240 n.visit_with(&mut v);
2241 v.bindings
2242}
2243
2244pub fn collect_decls_with_ctxt<I, N>(n: &N, ctxt: SyntaxContext) -> FxHashSet<I>
2247where
2248 I: IdentLike + Eq + Hash + Send + Sync,
2249 N: VisitWith<BindingCollector<I>>,
2250{
2251 let mut v = BindingCollector {
2252 only: Some(ctxt),
2253 bindings: Default::default(),
2254 is_pat_decl: false,
2255 };
2256 n.visit_with(&mut v);
2257 v.bindings
2258}
2259
2260pub struct TopLevelAwait {
2261 found: bool,
2262}
2263
2264impl Visit for TopLevelAwait {
2265 noop_visit_type!();
2266
2267 fn visit_stmt(&mut self, n: &Stmt) {
2268 if !self.found {
2269 n.visit_children_with(self);
2270 }
2271 }
2272
2273 fn visit_param(&mut self, _: &Param) {}
2274
2275 fn visit_function(&mut self, _: &Function) {}
2276
2277 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
2278
2279 fn visit_class_member(&mut self, prop: &ClassMember) {
2280 match prop {
2281 ClassMember::ClassProp(ClassProp {
2282 key: PropName::Computed(computed),
2283 ..
2284 })
2285 | ClassMember::Method(ClassMethod {
2286 key: PropName::Computed(computed),
2287 ..
2288 }) => computed.visit_children_with(self),
2289 _ => (),
2290 };
2291 }
2292
2293 fn visit_prop(&mut self, prop: &Prop) {
2294 match prop {
2295 Prop::KeyValue(KeyValueProp {
2296 key: PropName::Computed(computed),
2297 ..
2298 })
2299 | Prop::Getter(GetterProp {
2300 key: PropName::Computed(computed),
2301 ..
2302 })
2303 | Prop::Setter(SetterProp {
2304 key: PropName::Computed(computed),
2305 ..
2306 })
2307 | Prop::Method(MethodProp {
2308 key: PropName::Computed(computed),
2309 ..
2310 }) => computed.visit_children_with(self),
2311 _ => {}
2312 }
2313 }
2314
2315 fn visit_for_of_stmt(&mut self, for_of_stmt: &ForOfStmt) {
2316 if for_of_stmt.is_await {
2317 self.found = true;
2318 return;
2319 }
2320
2321 for_of_stmt.visit_children_with(self);
2322 }
2323
2324 fn visit_await_expr(&mut self, _: &AwaitExpr) {
2325 self.found = true;
2326 }
2327}
2328
2329pub fn contains_top_level_await<V: VisitWith<TopLevelAwait>>(t: &V) -> bool {
2330 let mut finder = TopLevelAwait { found: false };
2331
2332 t.visit_with(&mut finder);
2333
2334 finder.found
2335}
2336
2337pub struct Remapper<'a> {
2342 vars: &'a FxHashMap<Id, SyntaxContext>,
2343}
2344
2345impl<'a> Remapper<'a> {
2346 pub fn new(vars: &'a FxHashMap<Id, SyntaxContext>) -> Self {
2347 Self { vars }
2348 }
2349}
2350
2351impl VisitMut for Remapper<'_> {
2352 noop_visit_mut_type!(fail);
2353
2354 fn visit_mut_ident(&mut self, i: &mut Ident) {
2355 if let Some(new_ctxt) = self.vars.get(&i.to_id()).copied() {
2356 i.ctxt = new_ctxt;
2357 }
2358 }
2359}
2360
2361pub struct IdentRenamer<'a> {
2363 map: &'a FxHashMap<Id, Id>,
2364}
2365
2366impl<'a> IdentRenamer<'a> {
2367 pub fn new(map: &'a FxHashMap<Id, Id>) -> Self {
2368 Self { map }
2369 }
2370}
2371
2372impl VisitMut for IdentRenamer<'_> {
2373 noop_visit_mut_type!();
2374
2375 visit_mut_obj_and_computed!();
2376
2377 fn visit_mut_export_named_specifier(&mut self, node: &mut ExportNamedSpecifier) {
2378 if node.exported.is_some() {
2379 node.orig.visit_mut_children_with(self);
2380 return;
2381 }
2382
2383 match &mut node.orig {
2384 ModuleExportName::Ident(orig) => {
2385 if let Some(new) = self.map.get(&orig.to_id()) {
2386 node.exported = Some(ModuleExportName::Ident(orig.clone()));
2387
2388 orig.sym = new.0.clone();
2389 orig.ctxt = new.1;
2390 }
2391 }
2392 ModuleExportName::Str(_) => {}
2393 #[cfg(swc_ast_unknown)]
2394 _ => {}
2395 }
2396 }
2397
2398 fn visit_mut_ident(&mut self, node: &mut Ident) {
2399 if let Some(new) = self.map.get(&node.to_id()) {
2400 node.sym = new.0.clone();
2401 node.ctxt = new.1;
2402 }
2403 }
2404
2405 fn visit_mut_object_pat_prop(&mut self, i: &mut ObjectPatProp) {
2406 match i {
2407 ObjectPatProp::Assign(p) => {
2408 p.value.visit_mut_with(self);
2409
2410 let orig = p.key.clone();
2411 p.key.visit_mut_with(self);
2412
2413 if orig.ctxt == p.key.ctxt && orig.sym == p.key.sym {
2414 return;
2415 }
2416
2417 match p.value.take() {
2418 Some(default) => {
2419 *i = ObjectPatProp::KeyValue(KeyValuePatProp {
2420 key: PropName::Ident(orig.clone().into()),
2421 value: AssignPat {
2422 span: DUMMY_SP,
2423 left: p.key.clone().into(),
2424 right: default,
2425 }
2426 .into(),
2427 });
2428 }
2429 None => {
2430 *i = ObjectPatProp::KeyValue(KeyValuePatProp {
2431 key: PropName::Ident(orig.clone().into()),
2432 value: p.key.clone().into(),
2433 });
2434 }
2435 }
2436 }
2437
2438 _ => {
2439 i.visit_mut_children_with(self);
2440 }
2441 }
2442 }
2443
2444 fn visit_mut_prop(&mut self, node: &mut Prop) {
2445 match node {
2446 Prop::Shorthand(i) => {
2447 let cloned = i.clone();
2448 i.visit_mut_with(self);
2449 if i.sym != cloned.sym || i.ctxt != cloned.ctxt {
2450 *node = Prop::KeyValue(KeyValueProp {
2451 key: PropName::Ident(IdentName::new(cloned.sym, cloned.span)),
2452 value: i.clone().into(),
2453 });
2454 }
2455 }
2456 _ => {
2457 node.visit_mut_children_with(self);
2458 }
2459 }
2460 }
2461}
2462
2463pub trait QueryRef {
2464 fn query_ref(&self, _ident: &Ident) -> Option<Box<Expr>> {
2465 None
2466 }
2467 fn query_lhs(&self, _ident: &Ident) -> Option<Box<Expr>> {
2468 None
2469 }
2470
2471 fn query_jsx(&self, _ident: &Ident) -> Option<JSXElementName> {
2473 None
2474 }
2475
2476 fn should_fix_this(&self, _ident: &Ident) -> bool {
2479 false
2480 }
2481}
2482
2483pub struct RefRewriter<T>
2485where
2486 T: QueryRef,
2487{
2488 pub query: T,
2489}
2490
2491impl<T> RefRewriter<T>
2492where
2493 T: QueryRef,
2494{
2495 pub fn exit_prop(&mut self, n: &mut Prop) {
2496 if let Prop::Shorthand(shorthand) = n {
2497 if let Some(expr) = self.query.query_ref(shorthand) {
2498 *n = KeyValueProp {
2499 key: shorthand.take().into(),
2500 value: expr,
2501 }
2502 .into()
2503 }
2504 }
2505 }
2506
2507 pub fn exit_pat(&mut self, n: &mut Pat) {
2508 if let Pat::Ident(id) = n {
2509 if let Some(expr) = self.query.query_lhs(&id.clone().into()) {
2510 *n = expr.into();
2511 }
2512 }
2513 }
2514
2515 pub fn exit_expr(&mut self, n: &mut Expr) {
2516 if let Expr::Ident(ref_ident) = n {
2517 if let Some(expr) = self.query.query_ref(ref_ident) {
2518 *n = *expr;
2519 }
2520 };
2521 }
2522
2523 pub fn exit_simple_assign_target(&mut self, n: &mut SimpleAssignTarget) {
2524 if let SimpleAssignTarget::Ident(ref_ident) = n {
2525 if let Some(expr) = self.query.query_lhs(&ref_ident.clone().into()) {
2526 *n = expr.try_into().unwrap();
2527 }
2528 };
2529 }
2530
2531 pub fn exit_jsx_element_name(&mut self, n: &mut JSXElementName) {
2532 if let JSXElementName::Ident(ident) = n {
2533 if let Some(expr) = self.query.query_jsx(ident) {
2534 *n = expr;
2535 }
2536 }
2537 }
2538
2539 pub fn exit_jsx_object(&mut self, n: &mut JSXObject) {
2540 if let JSXObject::Ident(ident) = n {
2541 if let Some(expr) = self.query.query_jsx(ident) {
2542 *n = match expr {
2543 JSXElementName::Ident(ident) => ident.into(),
2544 JSXElementName::JSXMemberExpr(expr) => Box::new(expr).into(),
2545 JSXElementName::JSXNamespacedName(..) => unimplemented!(),
2546 #[cfg(swc_ast_unknown)]
2547 _ => return,
2548 }
2549 }
2550 }
2551 }
2552
2553 pub fn exit_object_pat_prop(&mut self, n: &mut ObjectPatProp) {
2554 if let ObjectPatProp::Assign(AssignPatProp { key, value, .. }) = n {
2555 if let Some(expr) = self.query.query_lhs(&key.id) {
2556 let value = value
2557 .take()
2558 .map(|default_value| {
2559 let left = expr.clone().try_into().unwrap();
2560 Box::new(default_value.make_assign_to(op!("="), left))
2561 })
2562 .unwrap_or(expr);
2563
2564 *n = ObjectPatProp::KeyValue(KeyValuePatProp {
2565 key: PropName::Ident(key.take().into()),
2566 value: value.into(),
2567 });
2568 }
2569 }
2570 }
2571}
2572
2573impl<T> VisitMut for RefRewriter<T>
2574where
2575 T: QueryRef,
2576{
2577 fn visit_mut_prop(&mut self, n: &mut Prop) {
2587 n.visit_mut_children_with(self);
2588 self.exit_prop(n);
2589 }
2590
2591 fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
2592 if !n.name.is_ident() {
2593 n.name.visit_mut_with(self);
2594 }
2595
2596 n.init.visit_mut_with(self);
2598 }
2599
2600 fn visit_mut_pat(&mut self, n: &mut Pat) {
2601 n.visit_mut_children_with(self);
2602 self.exit_pat(n);
2603 }
2604
2605 fn visit_mut_expr(&mut self, n: &mut Expr) {
2606 n.visit_mut_children_with(self);
2607 self.exit_expr(n);
2608 }
2609
2610 fn visit_mut_simple_assign_target(&mut self, n: &mut SimpleAssignTarget) {
2611 n.visit_mut_children_with(self);
2612 self.exit_simple_assign_target(n);
2613 }
2614
2615 fn visit_mut_callee(&mut self, n: &mut Callee) {
2616 match n {
2617 Callee::Expr(e)
2618 if e.as_ident()
2619 .map(|ident| self.query.should_fix_this(ident))
2620 .unwrap_or_default() =>
2621 {
2622 e.visit_mut_with(self);
2623
2624 if e.is_member() {
2625 *n = n.take().into_indirect()
2626 }
2627 }
2628
2629 _ => n.visit_mut_children_with(self),
2630 }
2631 }
2632
2633 fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
2634 let should_fix_this = n
2635 .tag
2636 .as_ident()
2637 .map(|ident| self.query.should_fix_this(ident))
2638 .unwrap_or_default();
2639
2640 n.visit_mut_children_with(self);
2641
2642 if should_fix_this && n.tag.is_member() {
2643 *n = n.take().into_indirect()
2644 }
2645 }
2646
2647 fn visit_mut_jsx_element_name(&mut self, n: &mut JSXElementName) {
2648 n.visit_mut_children_with(self);
2649
2650 self.exit_jsx_element_name(n);
2651 }
2652
2653 fn visit_mut_jsx_object(&mut self, n: &mut JSXObject) {
2654 n.visit_mut_children_with(self);
2655
2656 self.exit_jsx_object(n);
2657 }
2658}
2659
2660fn is_immutable_value(expr: &Expr) -> bool {
2661 match *expr {
2670 Expr::Lit(Lit::Bool(..))
2671 | Expr::Lit(Lit::Str(..))
2672 | Expr::Lit(Lit::Num(..))
2673 | Expr::Lit(Lit::Null(..)) => true,
2674
2675 Expr::Unary(UnaryExpr {
2676 op: op!("!"),
2677 ref arg,
2678 ..
2679 })
2680 | Expr::Unary(UnaryExpr {
2681 op: op!("~"),
2682 ref arg,
2683 ..
2684 })
2685 | Expr::Unary(UnaryExpr {
2686 op: op!("void"),
2687 ref arg,
2688 ..
2689 }) => arg.is_immutable_value(),
2690
2691 Expr::Ident(ref i) => i.sym == "undefined" || i.sym == "Infinity" || i.sym == "NaN",
2692
2693 Expr::Tpl(Tpl { ref exprs, .. }) => exprs.iter().all(|e| e.is_immutable_value()),
2694
2695 _ => false,
2696 }
2697}
2698
2699fn is_number(expr: &Expr) -> bool {
2700 matches!(*expr, Expr::Lit(Lit::Num(..)))
2701}
2702
2703fn is_str(expr: &Expr) -> bool {
2704 match expr {
2705 Expr::Lit(Lit::Str(..)) | Expr::Tpl(_) => true,
2706 Expr::Unary(UnaryExpr {
2707 op: op!("typeof"), ..
2708 }) => true,
2709 Expr::Bin(BinExpr {
2710 op: op!(bin, "+"),
2711 left,
2712 right,
2713 ..
2714 }) => left.is_str() || right.is_str(),
2715 Expr::Assign(AssignExpr {
2716 op: op!("=") | op!("+="),
2717 right,
2718 ..
2719 }) => right.is_str(),
2720 Expr::Seq(s) => s.exprs.last().unwrap().is_str(),
2721 Expr::Cond(CondExpr { cons, alt, .. }) => cons.is_str() && alt.is_str(),
2722 _ => false,
2723 }
2724}
2725
2726fn is_array_lit(expr: &Expr) -> bool {
2727 matches!(*expr, Expr::Array(..))
2728}
2729
2730fn is_nan(expr: &Expr) -> bool {
2731 expr.is_ident_ref_to("NaN")
2733}
2734
2735fn is_undefined(expr: &Expr, ctx: ExprCtx) -> bool {
2736 expr.is_global_ref_to(ctx, "undefined")
2737}
2738
2739fn is_void(expr: &Expr) -> bool {
2740 matches!(
2741 *expr,
2742 Expr::Unary(UnaryExpr {
2743 op: op!("void"),
2744 ..
2745 })
2746 )
2747}
2748
2749fn is_global_ref_to(expr: &Expr, ctx: ExprCtx, id: &str) -> bool {
2750 match expr {
2751 Expr::Ident(i) => i.ctxt == ctx.unresolved_ctxt && &*i.sym == id,
2752 _ => false,
2753 }
2754}
2755
2756fn is_one_of_global_ref_to(expr: &Expr, ctx: ExprCtx, ids: &[&str]) -> bool {
2757 match expr {
2758 Expr::Ident(i) => i.ctxt == ctx.unresolved_ctxt && ids.contains(&&*i.sym),
2759 _ => false,
2760 }
2761}
2762
2763fn as_pure_bool(expr: &Expr, ctx: ExprCtx) -> BoolValue {
2764 match expr.cast_to_bool(ctx) {
2765 (Pure, Known(b)) => Known(b),
2766 _ => Unknown,
2767 }
2768}
2769
2770fn cast_to_bool(expr: &Expr, ctx: ExprCtx) -> (Purity, BoolValue) {
2771 let Some(ctx) = ctx.consume_depth() else {
2772 return (MayBeImpure, Unknown);
2773 };
2774
2775 if expr.is_global_ref_to(ctx, "undefined") {
2776 return (Pure, Known(false));
2777 }
2778 if expr.is_nan() {
2779 return (Pure, Known(false));
2780 }
2781
2782 let val = match expr {
2783 Expr::Paren(ref e) => return e.expr.cast_to_bool(ctx),
2784
2785 Expr::Assign(AssignExpr {
2786 ref right,
2787 op: op!("="),
2788 ..
2789 }) => {
2790 let (_, v) = right.cast_to_bool(ctx);
2791 return (MayBeImpure, v);
2792 }
2793
2794 Expr::Unary(UnaryExpr {
2795 op: op!(unary, "-"),
2796 arg,
2797 ..
2798 }) => {
2799 let v = arg.as_pure_number(ctx);
2800 match v {
2801 Known(n) => Known(!matches!(n.classify(), FpCategory::Nan | FpCategory::Zero)),
2802 Unknown => return (MayBeImpure, Unknown),
2803 }
2804 }
2805
2806 Expr::Unary(UnaryExpr {
2807 op: op!("!"),
2808 ref arg,
2809 ..
2810 }) => {
2811 let (p, v) = arg.cast_to_bool(ctx);
2812 return (p, !v);
2813 }
2814 Expr::Seq(SeqExpr { exprs, .. }) => exprs.last().unwrap().cast_to_bool(ctx).1,
2815
2816 Expr::Bin(BinExpr {
2817 left,
2818 op: op!(bin, "-"),
2819 right,
2820 ..
2821 }) => {
2822 let (lp, ln) = left.cast_to_number(ctx);
2823 let (rp, rn) = right.cast_to_number(ctx);
2824
2825 return (
2826 lp + rp,
2827 match (ln, rn) {
2828 (Known(ln), Known(rn)) => {
2829 if ln == rn {
2830 Known(false)
2831 } else {
2832 Known(true)
2833 }
2834 }
2835 _ => Unknown,
2836 },
2837 );
2838 }
2839
2840 Expr::Bin(BinExpr {
2841 left,
2842 op: op!("/"),
2843 right,
2844 ..
2845 }) => {
2846 let lv = left.as_pure_number(ctx);
2847 let rv = right.as_pure_number(ctx);
2848
2849 match (lv, rv) {
2850 (Known(lv), Known(rv)) => {
2851 if lv == 0.0 && rv == 0.0 {
2853 return (Pure, Known(false));
2854 }
2855 if rv == 0.0 {
2857 return (Pure, Known(true));
2858 }
2859 let v = lv / rv;
2860
2861 return (Pure, Known(v != 0.0));
2862 }
2863 _ => Unknown,
2864 }
2865 }
2866
2867 Expr::Bin(BinExpr {
2868 ref left,
2869 op: op @ op!("&"),
2870 ref right,
2871 ..
2872 })
2873 | Expr::Bin(BinExpr {
2874 ref left,
2875 op: op @ op!("|"),
2876 ref right,
2877 ..
2878 }) => {
2879 if left.get_type(ctx) != Known(BoolType) || right.get_type(ctx) != Known(BoolType) {
2880 return (MayBeImpure, Unknown);
2881 }
2882
2883 let (lp, lv) = left.cast_to_bool(ctx);
2886 let (rp, rv) = right.cast_to_bool(ctx);
2887
2888 let v = if *op == op!("&") {
2889 lv.and(rv)
2890 } else {
2891 lv.or(rv)
2892 };
2893
2894 if lp + rp == Pure {
2895 return (Pure, v);
2896 }
2897
2898 v
2899 }
2900
2901 Expr::Bin(BinExpr {
2902 ref left,
2903 op: op!("||"),
2904 ref right,
2905 ..
2906 }) => {
2907 let (lp, lv) = left.cast_to_bool(ctx);
2908 if let Known(true) = lv {
2909 return (lp, lv);
2910 }
2911
2912 let (rp, rv) = right.cast_to_bool(ctx);
2913 if let Known(true) = rv {
2914 return (lp + rp, rv);
2915 }
2916
2917 Unknown
2918 }
2919
2920 Expr::Bin(BinExpr {
2921 ref left,
2922 op: op!("&&"),
2923 ref right,
2924 ..
2925 }) => {
2926 let (lp, lv) = left.cast_to_bool(ctx);
2927 if let Known(false) = lv {
2928 return (lp, lv);
2929 }
2930
2931 let (rp, rv) = right.cast_to_bool(ctx);
2932 if let Known(false) = rv {
2933 return (lp + rp, rv);
2934 }
2935
2936 Unknown
2937 }
2938
2939 Expr::Bin(BinExpr {
2940 left,
2941 op: op!(bin, "+"),
2942 right,
2943 ..
2944 }) => {
2945 match &**left {
2946 Expr::Lit(Lit::Str(s)) if !s.value.is_empty() => return (MayBeImpure, Known(true)),
2947 _ => {}
2948 }
2949
2950 match &**right {
2951 Expr::Lit(Lit::Str(s)) if !s.value.is_empty() => return (MayBeImpure, Known(true)),
2952 _ => {}
2953 }
2954
2955 Unknown
2956 }
2957
2958 Expr::Fn(..) | Expr::Class(..) | Expr::New(..) | Expr::Array(..) | Expr::Object(..) => {
2959 Known(true)
2960 }
2961
2962 Expr::Unary(UnaryExpr {
2963 op: op!("void"), ..
2964 }) => Known(false),
2965
2966 Expr::Lit(ref lit) => {
2967 return (
2968 Pure,
2969 Known(match *lit {
2970 Lit::Num(Number { value: n, .. }) => {
2971 !matches!(n.classify(), FpCategory::Nan | FpCategory::Zero)
2972 }
2973 Lit::BigInt(ref v) => v
2974 .value
2975 .to_string()
2976 .contains(|c: char| matches!(c, '1'..='9')),
2977 Lit::Bool(b) => b.value,
2978 Lit::Str(Str { ref value, .. }) => !value.is_empty(),
2979 Lit::Null(..) => false,
2980 Lit::Regex(..) => true,
2981 Lit::JSXText(..) => unreachable!("as_bool() for JSXText"),
2982 #[cfg(swc_ast_unknown)]
2983 _ => return (Pure, Unknown),
2984 }),
2985 );
2986 }
2987
2988 _ => Unknown,
2990 };
2991
2992 if expr.may_have_side_effects(ctx) {
2993 (MayBeImpure, val)
2994 } else {
2995 (Pure, val)
2996 }
2997}
2998
2999fn cast_to_number(expr: &Expr, ctx: ExprCtx) -> (Purity, Value<f64>) {
3000 let Some(ctx) = ctx.consume_depth() else {
3001 return (MayBeImpure, Unknown);
3002 };
3003
3004 let v = match expr {
3005 Expr::Lit(l) => match l {
3006 Lit::Bool(Bool { value: true, .. }) => 1.0,
3007 Lit::Bool(Bool { value: false, .. }) | Lit::Null(..) => 0.0,
3008 Lit::Num(Number { value: n, .. }) => *n,
3009 Lit::Str(Str { value, .. }) => return (Pure, num_from_str(value)),
3010 _ => return (Pure, Unknown),
3011 },
3012 Expr::Array(..) => {
3013 let Known(s) = expr.as_pure_string(ctx) else {
3014 return (Pure, Unknown);
3015 };
3016
3017 return (Pure, num_from_str(&s));
3018 }
3019 Expr::Ident(Ident { sym, ctxt, .. }) => match &**sym {
3020 "undefined" | "NaN" if *ctxt == ctx.unresolved_ctxt => f64::NAN,
3021 "Infinity" if *ctxt == ctx.unresolved_ctxt => f64::INFINITY,
3022 _ => return (Pure, Unknown),
3023 },
3024 Expr::Unary(UnaryExpr {
3025 op: op!(unary, "-"),
3026 arg,
3027 ..
3028 }) => match arg.cast_to_number(ctx) {
3029 (Pure, Known(v)) => -v,
3030 _ => return (MayBeImpure, Unknown),
3031 },
3032 Expr::Unary(UnaryExpr {
3033 op: op!("!"),
3034 ref arg,
3035 ..
3036 }) => match arg.cast_to_bool(ctx) {
3037 (Pure, Known(v)) => {
3038 if v {
3039 0.0
3040 } else {
3041 1.0
3042 }
3043 }
3044 _ => return (MayBeImpure, Unknown),
3045 },
3046 Expr::Unary(UnaryExpr {
3047 op: op!("void"),
3048 ref arg,
3049 ..
3050 }) => {
3051 if arg.may_have_side_effects(ctx) {
3052 return (MayBeImpure, Known(f64::NAN));
3053 } else {
3054 f64::NAN
3055 }
3056 }
3057
3058 Expr::Tpl(..) => {
3059 return (
3060 Pure,
3061 num_from_str(&match expr.as_pure_string(ctx) {
3062 Known(v) => v,
3063 Unknown => return (MayBeImpure, Unknown),
3064 }),
3065 );
3066 }
3067
3068 Expr::Seq(seq) => {
3069 if let Some(last) = seq.exprs.last() {
3070 let (_, v) = last.cast_to_number(ctx);
3071
3072 return (MayBeImpure, v);
3074 }
3075
3076 return (MayBeImpure, Unknown);
3077 }
3078
3079 _ => return (MayBeImpure, Unknown),
3080 };
3081
3082 (Purity::Pure, Known(v))
3083}
3084
3085fn as_pure_number(expr: &Expr, ctx: ExprCtx) -> Value<f64> {
3086 let (purity, v) = expr.cast_to_number(ctx);
3087 if !purity.is_pure() {
3088 return Unknown;
3089 }
3090
3091 v
3092}
3093
3094fn as_pure_string(expr: &Expr, ctx: ExprCtx) -> Value<Cow<'_, str>> {
3095 let Some(ctx) = ctx.consume_depth() else {
3096 return Unknown;
3097 };
3098
3099 match *expr {
3100 Expr::Lit(ref l) => match *l {
3101 Lit::Str(Str { ref value, .. }) => Known(Cow::Borrowed(value)),
3102 Lit::Num(ref n) => {
3103 if n.value == -0.0 {
3104 return Known(Cow::Borrowed("0"));
3105 }
3106
3107 Known(Cow::Owned(n.value.to_js_string()))
3108 }
3109 Lit::Bool(Bool { value: true, .. }) => Known(Cow::Borrowed("true")),
3110 Lit::Bool(Bool { value: false, .. }) => Known(Cow::Borrowed("false")),
3111 Lit::Null(..) => Known(Cow::Borrowed("null")),
3112 _ => Unknown,
3113 },
3114 Expr::Tpl(_) => {
3115 Value::Unknown
3116 }
3121 Expr::Ident(Ident { ref sym, ctxt, .. }) => match &**sym {
3122 "undefined" | "Infinity" | "NaN" if ctxt == ctx.unresolved_ctxt => {
3123 Known(Cow::Borrowed(&**sym))
3124 }
3125 _ => Unknown,
3126 },
3127 Expr::Unary(UnaryExpr {
3128 op: op!("void"), ..
3129 }) => Known(Cow::Borrowed("undefined")),
3130 Expr::Unary(UnaryExpr {
3131 op: op!("!"),
3132 ref arg,
3133 ..
3134 }) => Known(Cow::Borrowed(match arg.as_pure_bool(ctx) {
3135 Known(v) => {
3136 if v {
3137 "false"
3138 } else {
3139 "true"
3140 }
3141 }
3142 Unknown => return Value::Unknown,
3143 })),
3144 Expr::Array(ArrayLit { ref elems, .. }) => {
3145 let mut buf = String::new();
3146 let len = elems.len();
3147 for (idx, elem) in elems.iter().enumerate() {
3149 let last = idx == len - 1;
3150 let e = match *elem {
3151 Some(ref elem) => {
3152 let ExprOrSpread { ref expr, .. } = *elem;
3153 match &**expr {
3154 Expr::Lit(Lit::Null(..)) => Cow::Borrowed(""),
3155 Expr::Unary(UnaryExpr {
3156 op: op!("void"),
3157 arg,
3158 ..
3159 }) => {
3160 if arg.may_have_side_effects(ctx) {
3161 return Value::Unknown;
3162 }
3163 Cow::Borrowed("")
3164 }
3165 Expr::Ident(Ident { sym: undefined, .. })
3166 if &**undefined == "undefined" =>
3167 {
3168 Cow::Borrowed("")
3169 }
3170 _ => match expr.as_pure_string(ctx) {
3171 Known(v) => v,
3172 Unknown => return Value::Unknown,
3173 },
3174 }
3175 }
3176 None => Cow::Borrowed(""),
3177 };
3178 buf.push_str(&e);
3179
3180 if !last {
3181 buf.push(',');
3182 }
3183 }
3184 Known(buf.into())
3185 }
3186 _ => Unknown,
3187 }
3188}
3189
3190fn get_type(expr: &Expr, ctx: ExprCtx) -> Value<Type> {
3191 let Some(ctx) = ctx.consume_depth() else {
3192 return Unknown;
3193 };
3194
3195 match expr {
3196 Expr::Assign(AssignExpr {
3197 ref right,
3198 op: op!("="),
3199 ..
3200 }) => right.get_type(ctx),
3201
3202 Expr::Member(MemberExpr {
3203 obj,
3204 prop: MemberProp::Ident(IdentName { sym: length, .. }),
3205 ..
3206 }) if &**length == "length" => match &**obj {
3207 Expr::Array(ArrayLit { .. }) | Expr::Lit(Lit::Str(..)) => Known(Type::Num),
3208 Expr::Ident(Ident { sym: arguments, .. }) if &**arguments == "arguments" => {
3209 Known(Type::Num)
3210 }
3211 _ => Unknown,
3212 },
3213
3214 Expr::Seq(SeqExpr { ref exprs, .. }) => exprs
3215 .last()
3216 .expect("sequence expression should not be empty")
3217 .get_type(ctx),
3218
3219 Expr::Bin(BinExpr {
3220 ref left,
3221 op: op!("&&"),
3222 ref right,
3223 ..
3224 })
3225 | Expr::Bin(BinExpr {
3226 ref left,
3227 op: op!("||"),
3228 ref right,
3229 ..
3230 })
3231 | Expr::Cond(CondExpr {
3232 cons: ref left,
3233 alt: ref right,
3234 ..
3235 }) => and(left.get_type(ctx), right.get_type(ctx)),
3236
3237 Expr::Bin(BinExpr {
3238 ref left,
3239 op: op!(bin, "+"),
3240 ref right,
3241 ..
3242 }) => {
3243 let rt = right.get_type(ctx);
3244 if rt == Known(StringType) {
3245 return Known(StringType);
3246 }
3247
3248 let lt = left.get_type(ctx);
3249 if lt == Known(StringType) {
3250 return Known(StringType);
3251 }
3252
3253 if lt == Known(ObjectType) || rt == Known(ObjectType) {
3257 return Unknown;
3258 }
3259
3260 if !may_be_str(lt) && !may_be_str(rt) {
3261 return Known(NumberType);
3264 }
3265
3266 Unknown
3270 }
3271
3272 Expr::Assign(AssignExpr {
3273 op: op!("+="),
3274 ref right,
3275 ..
3276 }) => {
3277 if right.get_type(ctx) == Known(StringType) {
3278 return Known(StringType);
3279 }
3280 Unknown
3281 }
3282
3283 Expr::Ident(Ident { ref sym, .. }) => Known(match &**sym {
3284 "undefined" => UndefinedType,
3285 "NaN" | "Infinity" => NumberType,
3286 _ => return Unknown,
3287 }),
3288
3289 Expr::Lit(Lit::Num(..))
3290 | Expr::Assign(AssignExpr { op: op!("&="), .. })
3291 | Expr::Assign(AssignExpr { op: op!("^="), .. })
3292 | Expr::Assign(AssignExpr { op: op!("|="), .. })
3293 | Expr::Assign(AssignExpr { op: op!("<<="), .. })
3294 | Expr::Assign(AssignExpr { op: op!(">>="), .. })
3295 | Expr::Assign(AssignExpr {
3296 op: op!(">>>="), ..
3297 })
3298 | Expr::Assign(AssignExpr { op: op!("-="), .. })
3299 | Expr::Assign(AssignExpr { op: op!("*="), .. })
3300 | Expr::Assign(AssignExpr { op: op!("**="), .. })
3301 | Expr::Assign(AssignExpr { op: op!("/="), .. })
3302 | Expr::Assign(AssignExpr { op: op!("%="), .. })
3303 | Expr::Unary(UnaryExpr { op: op!("~"), .. })
3304 | Expr::Bin(BinExpr { op: op!("|"), .. })
3305 | Expr::Bin(BinExpr { op: op!("^"), .. })
3306 | Expr::Bin(BinExpr { op: op!("&"), .. })
3307 | Expr::Bin(BinExpr { op: op!("<<"), .. })
3308 | Expr::Bin(BinExpr { op: op!(">>"), .. })
3309 | Expr::Bin(BinExpr { op: op!(">>>"), .. })
3310 | Expr::Bin(BinExpr {
3311 op: op!(bin, "-"), ..
3312 })
3313 | Expr::Bin(BinExpr { op: op!("*"), .. })
3314 | Expr::Bin(BinExpr { op: op!("%"), .. })
3315 | Expr::Bin(BinExpr { op: op!("/"), .. })
3316 | Expr::Bin(BinExpr { op: op!("**"), .. })
3317 | Expr::Update(UpdateExpr { op: op!("++"), .. })
3318 | Expr::Update(UpdateExpr { op: op!("--"), .. })
3319 | Expr::Unary(UnaryExpr {
3320 op: op!(unary, "+"),
3321 ..
3322 })
3323 | Expr::Unary(UnaryExpr {
3324 op: op!(unary, "-"),
3325 ..
3326 }) => Known(NumberType),
3327
3328 Expr::Lit(Lit::Bool(..))
3330 | Expr::Bin(BinExpr { op: op!("=="), .. })
3331 | Expr::Bin(BinExpr { op: op!("!="), .. })
3332 | Expr::Bin(BinExpr { op: op!("==="), .. })
3333 | Expr::Bin(BinExpr { op: op!("!=="), .. })
3334 | Expr::Bin(BinExpr { op: op!("<"), .. })
3335 | Expr::Bin(BinExpr { op: op!("<="), .. })
3336 | Expr::Bin(BinExpr { op: op!(">"), .. })
3337 | Expr::Bin(BinExpr { op: op!(">="), .. })
3338 | Expr::Bin(BinExpr { op: op!("in"), .. })
3339 | Expr::Bin(BinExpr {
3340 op: op!("instanceof"),
3341 ..
3342 })
3343 | Expr::Unary(UnaryExpr { op: op!("!"), .. })
3344 | Expr::Unary(UnaryExpr {
3345 op: op!("delete"), ..
3346 }) => Known(BoolType),
3347
3348 Expr::Unary(UnaryExpr {
3349 op: op!("typeof"), ..
3350 })
3351 | Expr::Lit(Lit::Str { .. })
3352 | Expr::Tpl(..) => Known(StringType),
3353
3354 Expr::Lit(Lit::Null(..)) => Known(NullType),
3355
3356 Expr::Unary(UnaryExpr {
3357 op: op!("void"), ..
3358 }) => Known(UndefinedType),
3359
3360 Expr::Fn(..)
3361 | Expr::New(NewExpr { .. })
3362 | Expr::Array(ArrayLit { .. })
3363 | Expr::Object(ObjectLit { .. })
3364 | Expr::Lit(Lit::Regex(..)) => Known(ObjectType),
3365
3366 _ => Unknown,
3367 }
3368}
3369
3370fn is_pure_callee(expr: &Expr, ctx: ExprCtx) -> bool {
3371 if expr.is_global_ref_to(ctx, "Date") {
3372 return true;
3373 }
3374
3375 match expr {
3376 Expr::Member(MemberExpr {
3377 obj,
3378 prop: MemberProp::Ident(prop),
3379 ..
3380 }) => {
3381 fn is_pure_str_method(method: &str) -> bool {
3383 matches!(
3384 method,
3385 "charAt"
3386 | "charCodeAt"
3387 | "concat"
3388 | "endsWith"
3389 | "includes"
3390 | "indexOf"
3391 | "lastIndexOf"
3392 | "localeCompare"
3393 | "slice"
3394 | "split"
3395 | "startsWith"
3396 | "substr"
3397 | "substring"
3398 | "toLocaleLowerCase"
3399 | "toLocaleUpperCase"
3400 | "toLowerCase"
3401 | "toString"
3402 | "toUpperCase"
3403 | "trim"
3404 | "trimEnd"
3405 | "trimStart"
3406 )
3407 }
3408
3409 obj.is_global_ref_to(ctx, "Math")
3410 || match &**obj {
3411 Expr::Ident(Ident {
3413 ctxt, sym: math, ..
3414 }) => &**math == "Math" && *ctxt == SyntaxContext::empty(),
3415
3416 Expr::Lit(Lit::Str(..)) => is_pure_str_method(&prop.sym),
3417 Expr::Tpl(Tpl { exprs, .. }) if exprs.is_empty() => {
3418 is_pure_str_method(&prop.sym)
3419 }
3420
3421 _ => false,
3422 }
3423 }
3424
3425 Expr::Fn(FnExpr { function: f, .. })
3426 if f.params.iter().all(|p| p.pat.is_ident())
3427 && f.body.is_some()
3428 && f.body.as_ref().unwrap().stmts.is_empty() =>
3429 {
3430 true
3431 }
3432
3433 _ => false,
3434 }
3435}
3436
3437fn may_have_side_effects(expr: &Expr, ctx: ExprCtx) -> bool {
3438 let Some(ctx) = ctx.consume_depth() else {
3439 return true;
3440 };
3441
3442 if expr.is_pure_callee(ctx) {
3443 return false;
3444 }
3445
3446 match expr {
3447 Expr::Ident(i) => {
3448 if ctx.is_unresolved_ref_safe {
3449 return false;
3450 }
3451
3452 if i.ctxt == ctx.unresolved_ctxt {
3453 !matches!(
3454 &*i.sym,
3455 "Infinity"
3456 | "NaN"
3457 | "Math"
3458 | "undefined"
3459 | "Object"
3460 | "Array"
3461 | "Promise"
3462 | "Boolean"
3463 | "Number"
3464 | "String"
3465 | "BigInt"
3466 | "Error"
3467 | "RegExp"
3468 | "Function"
3469 | "document"
3470 )
3471 } else {
3472 false
3473 }
3474 }
3475
3476 Expr::Lit(..) | Expr::This(..) | Expr::PrivateName(..) | Expr::TsConstAssertion(..) => {
3477 false
3478 }
3479
3480 Expr::Paren(e) => e.expr.may_have_side_effects(ctx),
3481
3482 Expr::Fn(..) | Expr::Arrow(..) => false,
3484
3485 Expr::Class(c) => class_has_side_effect(ctx, &c.class),
3487 Expr::Array(ArrayLit { elems, .. }) => elems
3488 .iter()
3489 .filter_map(|e| e.as_ref())
3490 .any(|e| e.spread.is_some() || e.expr.may_have_side_effects(ctx)),
3491 Expr::Unary(UnaryExpr {
3492 op: op!("delete"), ..
3493 }) => true,
3494 Expr::Unary(UnaryExpr { arg, .. }) => arg.may_have_side_effects(ctx),
3495 Expr::Bin(BinExpr { left, right, .. }) => {
3496 left.may_have_side_effects(ctx) || right.may_have_side_effects(ctx)
3497 }
3498
3499 Expr::Member(MemberExpr { obj, prop, .. })
3500 if obj.is_object() || obj.is_fn_expr() || obj.is_arrow() || obj.is_class() =>
3501 {
3502 if obj.may_have_side_effects(ctx) {
3503 return true;
3504 }
3505 match &**obj {
3506 Expr::Class(c) => {
3507 let is_static_accessor = |member: &ClassMember| {
3508 if let ClassMember::Method(ClassMethod {
3509 kind: MethodKind::Getter | MethodKind::Setter,
3510 is_static: true,
3511 ..
3512 }) = member
3513 {
3514 true
3515 } else {
3516 false
3517 }
3518 };
3519 if c.class.body.iter().any(is_static_accessor) {
3520 return true;
3521 }
3522 }
3523 Expr::Object(obj) => {
3524 let can_have_side_effect = |prop: &PropOrSpread| match prop {
3525 PropOrSpread::Spread(_) => true,
3526 PropOrSpread::Prop(prop) => match prop.as_ref() {
3527 Prop::Getter(_) | Prop::Setter(_) | Prop::Method(_) => true,
3528 Prop::Shorthand(Ident { sym, .. })
3529 | Prop::KeyValue(KeyValueProp {
3530 key:
3531 PropName::Ident(IdentName { sym, .. })
3532 | PropName::Str(Str { value: sym, .. }),
3533 ..
3534 }) => &**sym == "__proto__",
3535 Prop::KeyValue(KeyValueProp {
3536 key: PropName::Computed(_),
3537 ..
3538 }) => true,
3539 _ => false,
3540 },
3541 #[cfg(swc_ast_unknown)]
3542 _ => true,
3543 };
3544 if obj.props.iter().any(can_have_side_effect) {
3545 return true;
3546 }
3547 }
3548 _ => {}
3549 };
3550
3551 match prop {
3552 MemberProp::Computed(c) => c.expr.may_have_side_effects(ctx),
3553 MemberProp::Ident(_) | MemberProp::PrivateName(_) => false,
3554 #[cfg(swc_ast_unknown)]
3555 _ => true,
3556 }
3557 }
3558
3559 Expr::Tpl(_) => true,
3561 Expr::TaggedTpl(_) => true,
3562 Expr::MetaProp(_) => true,
3563
3564 Expr::Await(_)
3565 | Expr::Yield(_)
3566 | Expr::Member(_)
3567 | Expr::SuperProp(_)
3568 | Expr::Update(_)
3569 | Expr::Assign(_) => true,
3570
3571 Expr::OptChain(OptChainExpr { base, .. }) if matches!(&**base, OptChainBase::Member(_)) => {
3572 true
3573 }
3574
3575 Expr::New(_) => true,
3577
3578 Expr::Call(CallExpr {
3579 callee: Callee::Expr(callee),
3580 ref args,
3581 ..
3582 }) if callee.is_pure_callee(ctx) => {
3583 args.iter().any(|arg| arg.expr.may_have_side_effects(ctx))
3584 }
3585 Expr::OptChain(OptChainExpr { base, .. })
3586 if matches!(&**base, OptChainBase::Call(..))
3587 && OptChainBase::as_call(base)
3588 .unwrap()
3589 .callee
3590 .is_pure_callee(ctx) =>
3591 {
3592 OptChainBase::as_call(base)
3593 .unwrap()
3594 .args
3595 .iter()
3596 .any(|arg| arg.expr.may_have_side_effects(ctx))
3597 }
3598
3599 Expr::Call(_) | Expr::OptChain(..) => true,
3600
3601 Expr::Seq(SeqExpr { exprs, .. }) => exprs.iter().any(|e| e.may_have_side_effects(ctx)),
3602
3603 Expr::Cond(CondExpr {
3604 test, cons, alt, ..
3605 }) => {
3606 test.may_have_side_effects(ctx)
3607 || cons.may_have_side_effects(ctx)
3608 || alt.may_have_side_effects(ctx)
3609 }
3610
3611 Expr::Object(ObjectLit { props, .. }) => props.iter().any(|node| match node {
3612 PropOrSpread::Prop(node) => match &**node {
3613 Prop::Shorthand(..) => false,
3614 Prop::KeyValue(KeyValueProp { key, value }) => {
3615 let k = match key {
3616 PropName::Computed(e) => e.expr.may_have_side_effects(ctx),
3617 _ => false,
3618 };
3619
3620 k || value.may_have_side_effects(ctx)
3621 }
3622 Prop::Getter(GetterProp { key, .. })
3623 | Prop::Setter(SetterProp { key, .. })
3624 | Prop::Method(MethodProp { key, .. }) => match key {
3625 PropName::Computed(e) => e.expr.may_have_side_effects(ctx),
3626 _ => false,
3627 },
3628 Prop::Assign(_) => true,
3629 #[cfg(swc_ast_unknown)]
3630 _ => true,
3631 },
3632 PropOrSpread::Spread(_) => true,
3634 #[cfg(swc_ast_unknown)]
3635 _ => true,
3636 }),
3637
3638 Expr::JSXMember(..)
3639 | Expr::JSXNamespacedName(..)
3640 | Expr::JSXEmpty(..)
3641 | Expr::JSXElement(..)
3642 | Expr::JSXFragment(..) => true,
3643
3644 Expr::TsAs(TsAsExpr { ref expr, .. })
3645 | Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
3646 | Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
3647 | Expr::TsInstantiation(TsInstantiation { ref expr, .. })
3648 | Expr::TsSatisfies(TsSatisfiesExpr { ref expr, .. }) => expr.may_have_side_effects(ctx),
3649
3650 Expr::Invalid(..) => true,
3651 #[cfg(swc_ast_unknown)]
3652 _ => true,
3653 }
3654}
3655
3656#[cfg(test)]
3657mod tests {
3658 use swc_common::{input::StringInput, BytePos};
3659 use swc_ecma_parser::{Parser, Syntax};
3660
3661 use super::*;
3662
3663 #[test]
3664 fn test_collect_decls() {
3665 run_collect_decls(
3666 "const { a, b = 1, inner: { c }, ...d } = {};",
3667 &["a", "b", "c", "d"],
3668 );
3669 run_collect_decls("const [ a, b = 1, [c], ...d ] = [];", &["a", "b", "c", "d"]);
3670 }
3671
3672 #[test]
3673 fn test_collect_export_default_expr() {
3674 run_collect_decls("export default function foo(){}", &["foo"]);
3675 run_collect_decls("export default class Foo{}", &["Foo"]);
3676 }
3677
3678 fn run_collect_decls(text: &str, expected_names: &[&str]) {
3679 let module = parse_module(text);
3680 let decls: FxHashSet<Id> = collect_decls(&module);
3681 let mut names = decls.iter().map(|d| d.0.to_string()).collect::<Vec<_>>();
3682 names.sort();
3683 assert_eq!(names, expected_names);
3684 }
3685
3686 #[test]
3687 fn test_extract_var_ids() {
3688 run_extract_var_ids(
3689 "var { a, b = 1, inner: { c }, ...d } = {};",
3690 &["a", "b", "c", "d"],
3691 );
3692 run_extract_var_ids("var [ a, b = 1, [c], ...d ] = [];", &["a", "b", "c", "d"]);
3693 }
3694
3695 fn run_extract_var_ids(text: &str, expected_names: &[&str]) {
3696 let module = parse_module(text);
3697 let decls = extract_var_ids(&module);
3698 let mut names = decls.iter().map(|d| d.sym.to_string()).collect::<Vec<_>>();
3699 names.sort();
3700 assert_eq!(names, expected_names);
3701 }
3702
3703 fn parse_module(text: &str) -> Module {
3704 let syntax = Syntax::Es(Default::default());
3705 let mut p = Parser::new(
3706 syntax,
3707 StringInput::new(text, BytePos(0), BytePos(text.len() as u32)),
3708 None,
3709 );
3710 p.parse_module().unwrap()
3711 }
3712
3713 fn has_top_level_await(text: &str) -> bool {
3714 let module = parse_module(text);
3715 contains_top_level_await(&module)
3716 }
3717
3718 #[test]
3719 fn top_level_await_block() {
3720 assert!(has_top_level_await("if (maybe) { await test; }"))
3721 }
3722
3723 #[test]
3724 fn top_level_await_for_of() {
3725 assert!(has_top_level_await("for await (let iter of []){}"))
3726 }
3727
3728 #[test]
3729 fn top_level_export_await() {
3730 assert!(has_top_level_await("export const foo = await 1;"));
3731 assert!(has_top_level_await("export default await 1;"));
3732 }
3733}
3734
3735#[cfg(test)]
3736mod ident_usage_finder_parallel_tests {
3737 use swc_atoms::Atom;
3738 use swc_common::SyntaxContext;
3739 use swc_ecma_ast::*;
3740
3741 use super::*;
3742
3743 fn make_id(name: &str) -> Ident {
3744 Ident::new(Atom::from(name), Span::dummy(), SyntaxContext::empty())
3745 }
3746
3747 #[test]
3748 fn test_visit_class_members() {
3749 let id = make_id("foo");
3750 let member = ClassMember::ClassProp(ClassProp {
3751 key: PropName::Ident(quote_ident!("foo")),
3752 value: Some(Box::new(Expr::Ident(quote_ident!("foo").into()))),
3753 ..Default::default()
3754 });
3755 let found = IdentUsageFinder::find(&id, &vec![member.clone()]);
3756 assert!(found);
3757 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![member]);
3758 assert!(!not_found);
3759 }
3760
3761 #[test]
3762 fn test_visit_expr_or_spreads() {
3763 let id = make_id("foo");
3764 let expr = ExprOrSpread {
3765 spread: None,
3766 expr: Box::new(Expr::Ident(quote_ident!("foo").into())),
3767 };
3768 let found = IdentUsageFinder::find(&id, &vec![expr.clone()]);
3769 assert!(found);
3770 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![expr]);
3771 assert!(!not_found);
3772 }
3773
3774 #[test]
3775 fn test_visit_module_items() {
3776 let id = make_id("foo");
3777 let item = ModuleItem::Stmt(Stmt::Expr(ExprStmt {
3778 span: DUMMY_SP,
3779 expr: Box::new(Expr::Ident(quote_ident!("foo").into())),
3780 }));
3781 let found = IdentUsageFinder::find(&id, &vec![item.clone()]);
3782 assert!(found);
3783 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![item]);
3784 assert!(!not_found);
3785 }
3786
3787 #[test]
3788 fn test_visit_stmts() {
3789 let id = make_id("foo");
3790 let stmt = Stmt::Expr(ExprStmt {
3791 span: DUMMY_SP,
3792 expr: Box::new(Expr::Ident(quote_ident!("foo").into())),
3793 });
3794 let found = IdentUsageFinder::find(&id, &vec![stmt.clone()]);
3795 assert!(found);
3796 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![stmt]);
3797 assert!(!not_found);
3798 }
3799
3800 #[test]
3801 fn test_visit_opt_vec_expr_or_spreads() {
3802 let id = make_id("foo");
3803 let expr = Some(ExprOrSpread {
3804 spread: None,
3805 expr: Box::new(Expr::Ident(quote_ident!("foo").into())),
3806 });
3807 let found = IdentUsageFinder::find(&id, &vec![expr.clone()]);
3808 assert!(found);
3809 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![expr]);
3810 assert!(!not_found);
3811 }
3812
3813 #[test]
3814 fn test_visit_var_declarators() {
3815 let id = make_id("foo");
3816 let decl = VarDeclarator {
3817 span: DUMMY_SP,
3818 name: Pat::Ident(quote_ident!("foo").into()),
3819 init: None,
3820 definite: false,
3821 };
3822 let found = IdentUsageFinder::find(&id, &vec![decl.clone()]);
3823 assert!(found);
3824 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![decl]);
3825 assert!(!not_found);
3826 }
3827
3828 #[test]
3829 fn test_visit_exprs() {
3830 let id = make_id("foo");
3831 let expr = Box::new(Expr::Ident(quote_ident!("foo").into()));
3832 let found = IdentUsageFinder::find(&id, &vec![expr.clone()]);
3833 assert!(found);
3834 let not_found = IdentUsageFinder::find(&make_id("bar"), &vec![expr]);
3835 assert!(!not_found);
3836 }
3837}