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