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