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