swc_ecma_utils/
lib.rs

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