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