1use ctx::BitContext;
2use rustc_hash::FxHashMap;
3use swc_common::SyntaxContext;
4use swc_ecma_ast::*;
5use swc_ecma_utils::{
6 find_pat_ids, ident::IdentLike, ExprCtx, ExprExt, IsEmpty, StmtExt, Type, Value,
7};
8use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
9use swc_timer::timer;
10
11pub use self::ctx::Ctx;
12use self::storage::*;
13use crate::{
14 alias::{collect_infects_from, AliasConfig},
15 marks::Marks,
16 util::{can_end_conditionally, get_object_define_property_name_arg},
17};
18
19mod ctx;
20pub mod storage;
21
22pub fn analyze_with_storage<S, N>(n: &N, marks: Option<Marks>) -> S
27where
28 S: Storage,
29 N: VisitWith<UsageAnalyzer<S>>,
30{
31 analyze_with_custom_storage(Default::default(), n, marks)
32}
33
34pub fn analyze_with_custom_storage<S, N>(data: S, n: &N, marks: Option<Marks>) -> S
35where
36 S: Storage,
37 N: VisitWith<UsageAnalyzer<S>>,
38{
39 let _timer = timer!("analyze");
40
41 let mut v = UsageAnalyzer {
42 data,
43 marks,
44 scope: Default::default(),
45 ctx: Default::default(),
46 expr_ctx: ExprCtx {
47 unresolved_ctxt: SyntaxContext::empty()
48 .apply_mark(marks.map(|m| m.unresolved_mark).unwrap_or_default()),
49 is_unresolved_ref_safe: false,
50 in_strict: false,
51 remaining_depth: 3,
52 },
53 used_recursively: FxHashMap::default(),
54 };
55 n.visit_with(&mut v);
56 let top_scope = v.scope;
57 v.data.top_scope().merge(top_scope.clone(), false);
58
59 v.data.scope(SyntaxContext::empty()).merge(top_scope, false);
60
61 v.data
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum ScopeKind {
66 Fn,
67 Block,
68}
69
70#[derive(Debug, Clone)]
71enum RecursiveUsage {
72 FnOrClass,
73 Var { can_ignore: bool },
74}
75
76#[derive(Debug)]
78pub struct UsageAnalyzer<S>
79where
80 S: Storage,
81{
82 data: S,
83 marks: Option<Marks>,
84 scope: S::ScopeData,
85 ctx: Ctx,
86 expr_ctx: ExprCtx,
87 used_recursively: FxHashMap<Id, RecursiveUsage>,
88}
89
90impl<S> UsageAnalyzer<S>
91where
92 S: Storage,
93{
94 fn with_child<F, Ret>(&mut self, child_ctxt: SyntaxContext, kind: ScopeKind, op: F) -> Ret
95 where
96 F: FnOnce(&mut UsageAnalyzer<S>) -> Ret,
97 {
98 let used_recursively = std::mem::take(&mut self.used_recursively);
99
100 let mut child = UsageAnalyzer {
101 data: S::new(S::need_collect_prop_atom(&self.data)),
102 marks: self.marks,
103 ctx: self.ctx.with(BitContext::IsTopLevel, false),
104 expr_ctx: self.expr_ctx,
105 scope: Default::default(),
106 used_recursively,
107 };
108
109 let ret = op(&mut child);
110 {
111 let child_scope = child.data.scope(child_ctxt);
112
113 child_scope.merge(child.scope.clone(), false);
114 }
115
116 self.scope.merge(child.scope, true);
117 self.data.merge(kind, child.data);
118
119 self.used_recursively = child.used_recursively;
120
121 ret
122 }
123
124 fn visit_pat_id(&mut self, i: &Ident) {
125 let in_left_of_for_loop = self.ctx.bit_ctx.contains(BitContext::InLeftOfForLoop);
126 let in_pat_of_param = self.ctx.bit_ctx.contains(BitContext::InPatOfParam);
127 let in_pat_of_var_decl = self.ctx.bit_ctx.contains(BitContext::InPatOfVarDecl);
128 let in_catch_param = self.ctx.bit_ctx.contains(BitContext::InCatchParam);
129
130 if in_pat_of_var_decl || in_pat_of_param || in_catch_param {
131 let v = self.declare_decl(
132 i,
133 self.ctx.in_pat_of_var_decl_with_init,
134 self.ctx.var_decl_kind_of_pat,
135 false,
136 );
137
138 if in_pat_of_param {
139 v.mark_declared_as_fn_param();
140 }
141
142 if in_pat_of_var_decl && in_left_of_for_loop {
143 v.mark_declared_as_for_init();
144 }
145 } else {
146 self.report_usage(i);
147 }
148 }
149
150 fn report_usage(&mut self, i: &Ident) {
151 if i.sym == "arguments" {
152 self.scope.mark_used_arguments();
153 }
154
155 let i = i.to_id();
156
157 if let Some(recr) = self.used_recursively.get(&i) {
158 if let RecursiveUsage::Var { can_ignore: false } = recr {
159 self.data.report_usage(self.ctx, i.clone());
160 self.data.var_or_default(i.clone()).mark_used_above_decl()
161 }
162 self.data.var_or_default(i.clone()).mark_used_recursively();
163 return;
164 }
165
166 self.data.report_usage(self.ctx, i)
167 }
168
169 fn report_assign_pat(&mut self, p: &Pat, is_read_modify: bool) {
170 for id in find_pat_ids(p) {
171 self.data
173 .report_assign(self.ctx, id, is_read_modify, Value::Unknown)
174 }
175
176 if let Pat::Expr(e) = p {
177 self.mark_mutation_if_member(e.as_member());
178 }
179 }
180
181 fn report_assign_expr_if_ident(&mut self, e: Option<&Ident>, is_op: bool, ty: Value<Type>) {
182 if let Some(i) = e {
183 self.data.report_assign(self.ctx, i.to_id(), is_op, ty)
184 }
185 }
186
187 fn declare_decl(
188 &mut self,
189 i: &Ident,
190 init_type: Option<Value<Type>>,
191 kind: Option<VarDeclKind>,
192 is_fn_decl: bool,
193 ) -> &mut S::VarData {
194 self.scope.add_declared_symbol(i);
195
196 let v = self.data.declare_decl(self.ctx, i, init_type, kind);
197
198 if is_fn_decl {
199 v.mark_declared_as_fn_decl();
200 }
201
202 v
203 }
204
205 fn visit_in_cond<T: VisitWith<Self>>(&mut self, t: &T) {
206 let cnt = self.data.get_initialized_cnt();
207 t.visit_with(self);
208 self.data.truncate_initialized_cnt(cnt)
209 }
210
211 fn visit_children_in_cond<T: VisitWith<Self>>(&mut self, t: &T) {
212 let cnt = self.data.get_initialized_cnt();
213 t.visit_children_with(self);
214 self.data.truncate_initialized_cnt(cnt)
215 }
216
217 fn mark_mutation_if_member(&mut self, e: Option<&MemberExpr>) {
218 if let Some(m) = e {
219 for_each_id_ref_in_expr(&m.obj, &mut |id| {
220 self.data.mark_property_mutation(id.to_id())
221 });
222 }
223 }
224}
225
226impl<S> Visit for UsageAnalyzer<S>
227where
228 S: Storage,
229{
230 noop_visit_type!();
231
232 fn visit_array_lit(&mut self, n: &ArrayLit) {
233 let ctx = self.ctx.with(BitContext::IsIdRef, true);
234 n.visit_children_with(&mut *self.with_ctx(ctx));
235 }
236
237 #[cfg_attr(
238 feature = "tracing-spans",
239 tracing::instrument(level = "debug", skip_all)
240 )]
241 fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
242 self.with_child(n.ctxt, ScopeKind::Fn, |child| {
243 {
244 let ctx = child
245 .ctx
246 .with(BitContext::InPatOfParam, true)
247 .with(BitContext::InlinePrevented, true);
248 n.params.visit_with(&mut *child.with_ctx(ctx));
249 }
250
251 match &*n.body {
252 BlockStmtOrExpr::BlockStmt(body) => {
253 body.visit_with(child);
254 }
255 BlockStmtOrExpr::Expr(body) => {
256 body.visit_with(child);
257 }
258 #[cfg(swc_ast_unknown)]
259 _ => panic!("unable to access unknown nodes"),
260 }
261 })
262 }
263
264 #[cfg_attr(
265 feature = "tracing-spans",
266 tracing::instrument(level = "debug", skip_all)
267 )]
268 fn visit_assign_expr(&mut self, n: &AssignExpr) {
269 let is_op_assign = n.op != op!("=");
270 n.left.visit_with(self);
271
272 let ctx = self.ctx.with(
278 BitContext::IsIdRef,
279 matches!(n.op, op!("=") | op!("||=") | op!("&&=") | op!("??=")),
280 );
281 n.right.visit_with(&mut *self.with_ctx(ctx));
282
283 match &n.left {
284 AssignTarget::Pat(p) => {
285 for id in find_pat_ids(p) {
286 self.data.report_assign(
287 self.ctx,
288 id,
289 is_op_assign,
290 n.right.get_type(self.expr_ctx),
291 )
292 }
293 }
294 AssignTarget::Simple(e) => {
295 self.report_assign_expr_if_ident(
296 e.as_ident().map(Ident::from).as_ref(),
297 is_op_assign,
298 n.right.get_type(self.expr_ctx),
299 );
300 self.mark_mutation_if_member(e.as_member())
301 }
302 #[cfg(swc_ast_unknown)]
303 _ => panic!("unable to access unknown nodes"),
304 };
305
306 if n.op == op!("=") {
307 let left = match &n.left {
308 AssignTarget::Simple(left) => left.leftmost().map(Ident::to_id),
309 AssignTarget::Pat(..) => None,
310 #[cfg(swc_ast_unknown)]
311 _ => panic!("unable to access unknown nodes"),
312 };
313
314 if let Some(left) = left {
315 let mut v = None;
316 for id in collect_infects_from(
317 &n.right,
318 AliasConfig {
319 marks: self.marks,
320 ignore_named_child_scope: true,
321 ..Default::default()
322 },
323 ) {
324 if v.is_none() {
325 v = Some(self.data.var_or_default(left.to_id()));
326 }
327
328 v.as_mut().unwrap().add_infects_to(id.clone());
329 }
330 }
331 }
332 }
333
334 fn visit_assign_pat(&mut self, p: &AssignPat) {
335 p.left.visit_with(self);
336
337 {
338 let ctx = Ctx {
339 bit_ctx: self.ctx.bit_ctx.with(BitContext::InPatOfParam, false),
340 var_decl_kind_of_pat: None,
341 ..self.ctx
342 };
343 p.right.visit_with(&mut *self.with_ctx(ctx))
344 }
345 }
346
347 #[cfg_attr(
348 feature = "tracing-spans",
349 tracing::instrument(level = "debug", skip_all)
350 )]
351 fn visit_await_expr(&mut self, n: &AwaitExpr) {
352 let ctx = self.ctx.with(BitContext::InAwaitArg, true);
353 n.visit_children_with(&mut *self.with_ctx(ctx));
354 }
355
356 fn visit_bin_expr(&mut self, e: &BinExpr) {
357 if e.op.may_short_circuit() {
358 let ctx = self.ctx.with(BitContext::IsIdRef, true);
359 e.left.visit_with(&mut *self.with_ctx(ctx));
360 let ctx = self
361 .ctx
362 .with(BitContext::InCond, true)
363 .with(BitContext::IsIdRef, true);
364 self.with_ctx(ctx).visit_in_cond(&e.right);
365 } else {
366 if e.op == op!("in") {
367 for_each_id_ref_in_expr(&e.right, &mut |obj| {
368 let var = self.data.var_or_default(obj.to_id());
369 var.mark_used_as_ref();
370
371 match &*e.left {
372 Expr::Lit(Lit::Str(prop)) => {
373 if prop
374 .value
375 .as_str()
376 .map_or(true, |value| value.parse::<f64>().is_err())
377 {
378 var.add_accessed_property(prop.value.clone());
379 }
380 }
381
382 Expr::Lit(Lit::Num(_)) => {}
383 _ => {
384 var.mark_indexed_with_dynamic_key();
385 }
386 }
387 })
388 }
389
390 let ctx = self.ctx.with(BitContext::IsIdRef, false);
391 e.visit_children_with(&mut *self.with_ctx(ctx));
392 }
393 }
394
395 #[cfg_attr(
396 feature = "tracing-spans",
397 tracing::instrument(level = "debug", skip_all)
398 )]
399 fn visit_binding_ident(&mut self, n: &BindingIdent) {
400 self.visit_pat_id(&Ident::from(n));
401 }
402
403 #[cfg_attr(
404 feature = "tracing-spans",
405 tracing::instrument(level = "debug", skip_all)
406 )]
407 fn visit_block_stmt(&mut self, n: &BlockStmt) {
408 self.with_child(n.ctxt, ScopeKind::Block, |child| {
409 n.visit_children_with(child);
410 });
411 }
412
413 #[cfg_attr(
414 feature = "tracing-spans",
415 tracing::instrument(level = "debug", skip_all)
416 )]
417 fn visit_call_expr(&mut self, n: &CallExpr) {
418 if let Some(prop_name) = get_object_define_property_name_arg(n) {
419 self.data.add_property_atom(prop_name.value.clone());
420 }
421
422 let inline_prevented = self.ctx.bit_ctx.contains(BitContext::InlinePrevented)
423 || self
424 .marks
425 .map(|marks| n.ctxt.has_mark(marks.noinline))
426 .unwrap_or_default();
427
428 {
429 let ctx = self.ctx.with(BitContext::InlinePrevented, inline_prevented);
430 n.callee.visit_with(&mut *self.with_ctx(ctx));
431 }
432
433 if let Callee::Expr(callee) = &n.callee {
434 for_each_id_ref_in_expr(callee, &mut |i| {
435 self.data.var_or_default(i.to_id()).mark_used_as_callee();
436 });
437
438 match &**callee {
439 Expr::Fn(callee) => {
440 for (idx, p) in callee.function.params.iter().enumerate() {
441 if let Some(arg) = n.args.get(idx) {
442 if arg.spread.is_some() {
443 break;
444 }
445
446 if is_safe_to_access_prop(&arg.expr) {
447 if let Pat::Ident(id) = &p.pat {
448 self.data
449 .var_or_default(id.to_id())
450 .mark_initialized_with_safe_value();
451 }
452 }
453 }
454 }
455 }
456
457 Expr::Arrow(callee) => {
458 for (idx, p) in callee.params.iter().enumerate() {
459 if let Some(arg) = n.args.get(idx) {
460 if arg.spread.is_some() {
461 break;
462 }
463
464 if is_safe_to_access_prop(&arg.expr) {
465 if let Pat::Ident(id) = &p {
466 self.data
467 .var_or_default(id.to_id())
468 .mark_initialized_with_safe_value();
469 }
470 }
471 }
472 }
473 }
474
475 _ => {}
476 }
477 }
478
479 {
480 let ctx = self
481 .ctx
482 .with(BitContext::InlinePrevented, inline_prevented)
483 .with(BitContext::IsIdRef, true);
484 n.args.visit_with(&mut *self.with_ctx(ctx));
485
486 let call_may_mutate = match &n.callee {
487 Callee::Expr(e) => call_may_mutate(e, self.expr_ctx),
488 _ => true,
489 };
490
491 if call_may_mutate {
492 for a in &n.args {
493 for_each_id_ref_in_expr(&a.expr, &mut |id| {
494 self.data.mark_property_mutation(id.to_id());
495 });
496 }
497 }
498 }
499
500 for arg in &n.args {
501 for_each_id_ref_in_expr(&arg.expr, &mut |arg| {
502 self.data.var_or_default(arg.to_id()).mark_used_as_arg();
503 })
504 }
505
506 if let Callee::Expr(callee) = &n.callee {
507 match &**callee {
508 Expr::Ident(Ident { sym, .. }) if *sym == *"eval" => {
509 self.scope.mark_eval_called();
510 }
511 Expr::Member(m) if !m.obj.is_ident() => {
512 for_each_id_ref_in_expr(&m.obj, &mut |id| {
513 self.data.var_or_default(id.to_id()).mark_used_as_ref()
514 })
515 }
516 _ => {}
517 }
518 }
519 }
520
521 #[cfg_attr(
522 feature = "tracing-spans",
523 tracing::instrument(level = "debug", skip_all)
524 )]
525 fn visit_catch_clause(&mut self, n: &CatchClause) {
526 {
527 let ctx = self
528 .ctx
529 .with(BitContext::InCond, true)
530 .with(BitContext::InCatchParam, true);
531 n.param.visit_with(&mut *self.with_ctx(ctx));
532 }
533
534 {
535 let ctx = self.ctx.with(BitContext::InCond, true);
536 self.with_ctx(ctx).visit_in_cond(&n.body);
537 }
538 }
539
540 #[cfg_attr(
541 feature = "tracing-spans",
542 tracing::instrument(level = "debug", skip_all)
543 )]
544 fn visit_class(&mut self, n: &Class) {
545 n.decorators.visit_with(self);
546
547 {
548 let ctx = self.ctx.with(BitContext::InlinePrevented, true);
549 n.super_class.visit_with(&mut *self.with_ctx(ctx));
550 }
551
552 self.with_child(n.ctxt, ScopeKind::Fn, |child| n.body.visit_with(child))
553 }
554
555 #[cfg_attr(
556 feature = "tracing-spans",
557 tracing::instrument(level = "debug", skip_all)
558 )]
559 fn visit_class_decl(&mut self, n: &ClassDecl) {
560 self.declare_decl(&n.ident, Some(Value::Unknown), None, false);
561
562 n.visit_children_with(self);
563 }
564
565 #[cfg_attr(
566 feature = "tracing-spans",
567 tracing::instrument(level = "debug", skip_all)
568 )]
569 fn visit_class_expr(&mut self, n: &ClassExpr) {
570 n.visit_children_with(self);
571
572 if let Some(id) = &n.ident {
573 self.declare_decl(id, Some(Value::Unknown), None, false);
574 }
575 }
576
577 #[cfg_attr(
578 feature = "tracing-spans",
579 tracing::instrument(level = "debug", skip_all)
580 )]
581 fn visit_class_method(&mut self, n: &ClassMethod) {
582 n.function.decorators.visit_with(self);
583
584 self.with_child(n.function.ctxt, ScopeKind::Fn, |a| {
585 n.key.visit_with(a);
586 {
587 let ctx = a.ctx.with(BitContext::InPatOfParam, true);
588 n.function.params.visit_with(&mut *a.with_ctx(ctx));
589 }
590
591 n.function.visit_with(a);
592 });
593 }
594
595 #[cfg_attr(
596 feature = "tracing-spans",
597 tracing::instrument(level = "debug", skip_all)
598 )]
599 fn visit_class_prop(&mut self, n: &ClassProp) {
600 let ctx = self.ctx.with(BitContext::IsIdRef, true);
601
602 n.visit_children_with(&mut *self.with_ctx(ctx));
603 }
604
605 #[cfg_attr(
606 feature = "tracing-spans",
607 tracing::instrument(level = "debug", skip_all)
608 )]
609 fn visit_computed_prop_name(&mut self, n: &ComputedPropName) {
610 let ctx = self.ctx.with(BitContext::IsIdRef, true);
611
612 n.visit_children_with(&mut *self.with_ctx(ctx));
613 }
614
615 #[cfg_attr(
616 feature = "tracing-spans",
617 tracing::instrument(level = "debug", skip_all)
618 )]
619 fn visit_cond_expr(&mut self, n: &CondExpr) {
620 {
621 let ctx = self.ctx.with(BitContext::IsIdRef, false);
622
623 n.test.visit_with(&mut *self.with_ctx(ctx));
624 }
625
626 {
627 let ctx = self
628 .ctx
629 .with(BitContext::InCond, true)
630 .with(BitContext::IsIdRef, true);
631 self.with_ctx(ctx).visit_in_cond(&n.cons);
632 self.with_ctx(ctx).visit_in_cond(&n.alt);
633 }
634 }
635
636 #[cfg_attr(
637 feature = "tracing-spans",
638 tracing::instrument(level = "debug", skip_all)
639 )]
640 fn visit_constructor(&mut self, n: &Constructor) {
641 self.with_child(n.ctxt, ScopeKind::Fn, |child| {
642 {
643 let ctx = child.ctx.with(BitContext::InPatOfParam, true);
644 n.params.visit_with(&mut *child.with_ctx(ctx));
645 }
646
647 if let Some(body) = &n.body {
649 body.visit_with(child);
650 }
651 })
652 }
653
654 fn visit_default_decl(&mut self, d: &DefaultDecl) {
655 d.visit_children_with(self);
656
657 match d {
658 DefaultDecl::Class(c) => {
659 if let Some(i) = &c.ident {
660 self.data.var_or_default(i.to_id()).prevent_inline();
661 }
662 }
663 DefaultDecl::Fn(f) => {
664 if let Some(i) = &f.ident {
665 self.data.var_or_default(i.to_id()).prevent_inline();
666 }
667 }
668 _ => {}
669 }
670 }
671
672 #[cfg_attr(
673 feature = "tracing-spans",
674 tracing::instrument(level = "debug", skip_all)
675 )]
676 fn visit_do_while_stmt(&mut self, n: &DoWhileStmt) {
677 n.body
678 .visit_with(&mut *self.with_ctx(self.ctx.with(BitContext::ExecutedMultipleTime, true)));
679 n.test
680 .visit_with(&mut *self.with_ctx(self.ctx.with(BitContext::ExecutedMultipleTime, true)));
681 }
682
683 #[cfg_attr(
684 feature = "tracing-spans",
685 tracing::instrument(level = "debug", skip_all)
686 )]
687 fn visit_export_decl(&mut self, n: &ExportDecl) {
688 n.visit_children_with(self);
689
690 match &n.decl {
691 Decl::Class(c) => {
692 self.data.var_or_default(c.ident.to_id()).prevent_inline();
693 }
694 Decl::Fn(f) => {
695 self.data.var_or_default(f.ident.to_id()).prevent_inline();
696 }
697 Decl::Var(v) => {
698 let ids = find_pat_ids(v);
699
700 for id in ids {
701 self.data.var_or_default(id).mark_as_exported();
702 }
703 }
704 _ => {}
705 }
706 }
707
708 #[cfg_attr(
709 feature = "tracing-spans",
710 tracing::instrument(level = "debug", skip_all)
711 )]
712 fn visit_export_default_expr(&mut self, n: &ExportDefaultExpr) {
713 let ctx = self.ctx.with(BitContext::IsIdRef, true);
714
715 n.visit_children_with(&mut *self.with_ctx(ctx));
716 }
717
718 fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
719 match &n.orig {
720 ModuleExportName::Ident(orig) => {
721 self.report_usage(orig);
722 let v = self.data.var_or_default(orig.to_id());
723 v.prevent_inline();
724 v.mark_used_as_ref();
725 }
726 ModuleExportName::Str(..) => {}
727 #[cfg(swc_ast_unknown)]
728 _ => panic!("unable to access unknown nodes"),
729 };
730 }
731
732 #[cfg_attr(
733 feature = "tracing-spans",
734 tracing::instrument(level = "debug", skip(self, e))
735 )]
736 fn visit_expr(&mut self, e: &Expr) {
737 let ctx = Ctx {
738 bit_ctx: self
739 .ctx
740 .bit_ctx
741 .with(BitContext::InPatOfVarDecl, false)
742 .with(BitContext::InPatOfParam, false)
743 .with(BitContext::InCatchParam, false),
744 var_decl_kind_of_pat: None,
745 in_pat_of_var_decl_with_init: None,
746 ..self.ctx
747 };
748
749 e.visit_children_with(&mut *self.with_ctx(ctx));
750
751 if let Expr::Ident(i) = e {
752 #[cfg(feature = "tracing-spans")]
753 {
754 }
761
762 self.with_ctx(ctx).report_usage(i);
763 }
764 }
765
766 #[cfg_attr(
767 feature = "tracing-spans",
768 tracing::instrument(level = "debug", skip_all)
769 )]
770 fn visit_expr_or_spread(&mut self, e: &ExprOrSpread) {
771 e.visit_children_with(self);
772
773 if e.spread.is_some() {
774 for_each_id_ref_in_expr(&e.expr, &mut |i| {
775 self.data
776 .var_or_default(i.to_id())
777 .mark_indexed_with_dynamic_key();
778 });
779 }
780 }
781
782 #[cfg_attr(
783 feature = "tracing-spans",
784 tracing::instrument(level = "debug", skip_all)
785 )]
786 fn visit_fn_decl(&mut self, n: &FnDecl) {
787 let ctx = self
788 .ctx
789 .with(BitContext::InDeclWithNoSideEffectForMemberAccess, true);
790 self.with_ctx(ctx)
791 .declare_decl(&n.ident, Some(Value::Known(Type::Obj)), None, true);
792
793 if n.function.body.is_empty() {
794 self.data.var_or_default(n.ident.to_id()).mark_as_pure_fn();
795 }
796
797 let id = n.ident.to_id();
798 self.used_recursively
799 .insert(id.clone(), RecursiveUsage::FnOrClass);
800 n.visit_children_with(self);
801 self.used_recursively.remove(&id);
802
803 {
804 let mut v = None;
805 for id in collect_infects_from(
806 &n.function,
807 AliasConfig {
808 marks: self.marks,
809 ignore_named_child_scope: true,
810 ..Default::default()
811 },
812 ) {
813 if v.is_none() {
814 v = Some(self.data.var_or_default(n.ident.to_id()));
815 }
816
817 v.as_mut().unwrap().add_infects_to(id.clone());
818 }
819 }
820 }
821
822 #[cfg_attr(
823 feature = "tracing-spans",
824 tracing::instrument(level = "debug", skip_all)
825 )]
826 fn visit_fn_expr(&mut self, n: &FnExpr) {
827 if let Some(n_id) = &n.ident {
828 self.data
829 .var_or_default(n_id.to_id())
830 .mark_declared_as_fn_expr();
831
832 self.used_recursively
833 .insert(n_id.to_id(), RecursiveUsage::FnOrClass);
834
835 n.visit_children_with(self);
836
837 {
838 let mut v = None;
839 for id in collect_infects_from(
840 &n.function,
841 AliasConfig {
842 marks: self.marks,
843 ignore_named_child_scope: true,
844 ..Default::default()
845 },
846 ) {
847 if v.is_none() {
848 v = Some(self.data.var_or_default(n_id.to_id()));
849 }
850
851 v.as_mut().unwrap().add_infects_to(id);
852 }
853 }
854 self.used_recursively.remove(&n_id.to_id());
855 } else {
856 n.visit_children_with(self);
857 }
858 }
859
860 #[cfg_attr(
861 feature = "tracing-spans",
862 tracing::instrument(level = "debug", skip_all)
863 )]
864 fn visit_for_in_stmt(&mut self, n: &ForInStmt) {
865 n.right.visit_with(self);
866
867 self.with_child(SyntaxContext::empty(), ScopeKind::Block, |child| {
868 let head_ctx = child
869 .ctx
870 .with(BitContext::InLeftOfForLoop, true)
871 .with(BitContext::IsIdRef, true)
872 .with(BitContext::ExecutedMultipleTime, true)
873 .with(BitContext::InCond, true);
874 n.left.visit_with(&mut *child.with_ctx(head_ctx));
875
876 n.right.visit_with(child);
877
878 if let ForHead::Pat(pat) = &n.left {
879 child.with_ctx(head_ctx).report_assign_pat(pat, true)
880 }
881
882 let ctx = child
883 .ctx
884 .with(BitContext::ExecutedMultipleTime, true)
885 .with(BitContext::InCond, true);
886
887 child.with_ctx(ctx).visit_in_cond(&n.body);
888 });
889 }
890
891 #[cfg_attr(
892 feature = "tracing-spans",
893 tracing::instrument(level = "debug", skip_all)
894 )]
895 fn visit_for_of_stmt(&mut self, n: &ForOfStmt) {
896 n.right.visit_with(self);
897
898 self.with_child(SyntaxContext::empty(), ScopeKind::Block, |child| {
899 let head_ctx = child
900 .ctx
901 .with(BitContext::InLeftOfForLoop, true)
902 .with(BitContext::IsIdRef, true)
903 .with(BitContext::ExecutedMultipleTime, true)
904 .with(BitContext::InCond, true);
905 n.left.visit_with(&mut *child.with_ctx(head_ctx));
906
907 if let ForHead::Pat(pat) = &n.left {
908 child.with_ctx(head_ctx).report_assign_pat(pat, true)
909 }
910
911 let ctx = child
912 .ctx
913 .with(BitContext::ExecutedMultipleTime, true)
914 .with(BitContext::InCond, true);
915 child.with_ctx(ctx).visit_in_cond(&n.body);
916 });
917 }
918
919 #[cfg_attr(
920 feature = "tracing-spans",
921 tracing::instrument(level = "debug", skip_all)
922 )]
923 fn visit_for_stmt(&mut self, n: &ForStmt) {
924 n.init.visit_with(self);
925
926 let ctx = self
927 .ctx
928 .with(BitContext::ExecutedMultipleTime, true)
929 .with(BitContext::InCond, true);
930
931 self.with_ctx(ctx).visit_in_cond(&n.test);
932 self.with_ctx(ctx).visit_in_cond(&n.update);
933 self.with_ctx(ctx).visit_in_cond(&n.body);
934 }
935
936 #[cfg_attr(
937 feature = "tracing-spans",
938 tracing::instrument(level = "debug", skip_all)
939 )]
940 fn visit_function(&mut self, n: &Function) {
941 n.decorators.visit_with(self);
942
943 let ctx = Ctx { ..self.ctx };
944
945 self.with_ctx(ctx)
946 .with_child(n.ctxt, ScopeKind::Fn, |child| {
947 n.params.visit_with(child);
948
949 if let Some(body) = &n.body {
950 body.visit_children_with(child);
953 }
954 })
955 }
956
957 #[cfg_attr(
958 feature = "tracing-spans",
959 tracing::instrument(level = "debug", skip_all)
960 )]
961 fn visit_getter_prop(&mut self, n: &GetterProp) {
962 self.with_child(SyntaxContext::empty(), ScopeKind::Fn, |a| {
963 n.key.visit_with(a);
964
965 n.body.visit_with(a);
966 });
967 }
968
969 #[cfg_attr(
970 feature = "tracing-spans",
971 tracing::instrument(level = "debug", skip_all)
972 )]
973 fn visit_if_stmt(&mut self, n: &IfStmt) {
974 let ctx = self.ctx.with(BitContext::InCond, true);
975 n.test.visit_with(self);
976
977 self.with_ctx(ctx).visit_in_cond(&n.cons);
978 self.with_ctx(ctx).visit_in_cond(&n.alt);
979 }
980
981 fn visit_import_default_specifier(&mut self, n: &ImportDefaultSpecifier) {
982 self.declare_decl(&n.local, Some(Value::Unknown), None, false);
983 }
984
985 fn visit_import_named_specifier(&mut self, n: &ImportNamedSpecifier) {
986 self.declare_decl(&n.local, Some(Value::Unknown), None, false);
987 }
988
989 fn visit_import_star_as_specifier(&mut self, n: &ImportStarAsSpecifier) {
990 self.declare_decl(&n.local, Some(Value::Unknown), None, false);
991 }
992
993 #[cfg_attr(
994 feature = "tracing-spans",
995 tracing::instrument(level = "debug", skip_all)
996 )]
997 fn visit_jsx_element_name(&mut self, n: &JSXElementName) {
998 let ctx = Ctx {
999 bit_ctx: self
1000 .ctx
1001 .bit_ctx
1002 .with(BitContext::InPatOfVarDecl, false)
1003 .with(BitContext::InPatOfParam, false)
1004 .with(BitContext::InCatchParam, false),
1005 var_decl_kind_of_pat: None,
1006 in_pat_of_var_decl_with_init: None,
1007 ..self.ctx
1008 };
1009
1010 n.visit_children_with(&mut *self.with_ctx(ctx));
1011
1012 if let JSXElementName::Ident(i) = n {
1013 self.with_ctx(ctx).report_usage(i);
1014 self.data
1015 .var_or_default(i.to_id())
1016 .mark_used_as_jsx_callee();
1017 }
1018 }
1019
1020 #[cfg_attr(
1021 feature = "tracing-spans",
1022 tracing::instrument(level = "debug", skip(self, e))
1023 )]
1024 fn visit_member_expr(&mut self, e: &MemberExpr) {
1025 {
1026 let ctx = self.ctx.with(BitContext::IsIdRef, false);
1027 e.obj.visit_with(&mut *self.with_ctx(ctx));
1028 }
1029
1030 if let MemberProp::Computed(c) = &e.prop {
1031 c.visit_with(self);
1032 }
1033
1034 for_each_id_ref_in_expr(&e.obj, &mut |obj| {
1035 let v = self.data.var_or_default(obj.to_id());
1036 v.mark_has_property_access();
1037
1038 if let MemberProp::Computed(prop) = &e.prop {
1039 match &*prop.expr {
1040 Expr::Lit(Lit::Str(s)) => {
1041 if s.value
1042 .as_str()
1043 .map_or(true, |value| value.parse::<f64>().is_err())
1044 {
1045 v.add_accessed_property(s.value.clone());
1046 }
1047 }
1048
1049 Expr::Lit(Lit::Num(_)) => {}
1050 _ => {
1051 v.mark_indexed_with_dynamic_key();
1052 }
1053 }
1054 }
1055
1056 if let MemberProp::Ident(prop) = &e.prop {
1057 v.add_accessed_property(prop.sym.clone().into());
1058 }
1059 });
1060
1061 fn is_root_of_member_expr_declared(member_expr: &MemberExpr, data: &impl Storage) -> bool {
1062 match &*member_expr.obj {
1063 Expr::Member(member_expr) => is_root_of_member_expr_declared(member_expr, data),
1064 Expr::Ident(ident) => data
1065 .get_var_data(ident.to_id())
1066 .map(|var| var.is_declared())
1067 .unwrap_or(false),
1068
1069 _ => false,
1070 }
1071 }
1072
1073 if is_root_of_member_expr_declared(e, &self.data) {
1074 if let MemberProp::Ident(ident) = &e.prop {
1075 self.data.add_property_atom(ident.sym.clone().into());
1076 }
1077 }
1078 }
1079
1080 #[cfg_attr(
1081 feature = "tracing-spans",
1082 tracing::instrument(level = "debug", skip_all)
1083 )]
1084 fn visit_method_prop(&mut self, n: &MethodProp) {
1085 n.function.decorators.visit_with(self);
1086
1087 self.with_child(n.function.ctxt, ScopeKind::Fn, |a| {
1088 n.key.visit_with(a);
1089 {
1090 let ctx = a.ctx.with(BitContext::InPatOfParam, true);
1091 n.function.params.visit_with(&mut *a.with_ctx(ctx));
1092 }
1093
1094 n.function.visit_with(a);
1095 });
1096 }
1097
1098 fn visit_module(&mut self, n: &Module) {
1099 let ctx = self.ctx.with(BitContext::IsTopLevel, true);
1100 n.visit_children_with(&mut *self.with_ctx(ctx))
1101 }
1102
1103 fn visit_named_export(&mut self, n: &NamedExport) {
1104 if n.src.is_some() {
1105 return;
1106 }
1107 n.visit_children_with(self);
1108 }
1109
1110 #[cfg_attr(
1111 feature = "tracing-spans",
1112 tracing::instrument(level = "debug", skip_all)
1113 )]
1114 fn visit_new_expr(&mut self, n: &NewExpr) {
1115 {
1116 n.callee.visit_with(self);
1117 let ctx = self.ctx.with(BitContext::IsIdRef, true);
1118 n.args.visit_with(&mut *self.with_ctx(ctx));
1119
1120 if call_may_mutate(&n.callee, self.expr_ctx) {
1121 if let Some(args) = &n.args {
1122 for a in args {
1123 for_each_id_ref_in_expr(&a.expr, &mut |id| {
1124 self.data.mark_property_mutation(id.to_id());
1125 });
1126 }
1127 }
1128 }
1129 }
1130 }
1131
1132 #[cfg_attr(
1133 feature = "tracing-spans",
1134 tracing::instrument(level = "debug", skip_all)
1135 )]
1136 fn visit_param(&mut self, n: &Param) {
1137 let ctx = self.ctx.with(BitContext::InPatOfParam, false);
1138 n.decorators.visit_with(&mut *self.with_ctx(ctx));
1139
1140 let ctx = Ctx {
1141 bit_ctx: self
1142 .ctx
1143 .bit_ctx
1144 .with(BitContext::InPatOfParam, true)
1145 .with(BitContext::IsIdRef, true),
1146 var_decl_kind_of_pat: None,
1147 ..self.ctx
1148 };
1149 n.pat.visit_with(&mut *self.with_ctx(ctx));
1150 }
1151
1152 #[cfg_attr(
1153 feature = "tracing-spans",
1154 tracing::instrument(level = "debug", skip_all)
1155 )]
1156 fn visit_pat(&mut self, n: &Pat) {
1157 match n {
1158 Pat::Ident(i) => {
1159 i.visit_with(self);
1160 }
1161 _ => {
1162 let ctx = self
1163 .ctx
1164 .with(BitContext::InDeclWithNoSideEffectForMemberAccess, false);
1165 n.visit_children_with(&mut *self.with_ctx(ctx));
1166 }
1167 }
1168 }
1169
1170 #[cfg_attr(
1171 feature = "tracing-spans",
1172 tracing::instrument(level = "debug", skip_all)
1173 )]
1174 fn visit_private_method(&mut self, n: &PrivateMethod) {
1175 n.function.decorators.visit_with(self);
1176
1177 self.with_child(n.function.ctxt, ScopeKind::Fn, |a| {
1178 n.key.visit_with(a);
1179 {
1180 let ctx = a.ctx.with(BitContext::InPatOfParam, true);
1181 n.function.params.visit_with(&mut *a.with_ctx(ctx));
1182 }
1183
1184 n.function.visit_with(a);
1185 });
1186 }
1187
1188 #[cfg_attr(
1189 feature = "tracing-spans",
1190 tracing::instrument(level = "debug", skip_all)
1191 )]
1192 fn visit_private_prop(&mut self, n: &PrivateProp) {
1193 let ctx = self.ctx.with(BitContext::IsIdRef, true);
1194 n.visit_children_with(&mut *self.with_ctx(ctx));
1195 }
1196
1197 #[cfg_attr(
1198 feature = "tracing-spans",
1199 tracing::instrument(level = "debug", skip_all)
1200 )]
1201 fn visit_prop(&mut self, n: &Prop) {
1202 if let Prop::Shorthand(i) = n {
1203 let ctx = self.ctx.with(BitContext::IsIdRef, true);
1204 self.with_ctx(ctx).report_usage(i);
1205 self.data.add_property_atom(i.sym.clone().into());
1206 } else {
1207 let ctx = self.ctx.with(BitContext::IsIdRef, true);
1208 n.visit_children_with(&mut *self.with_ctx(ctx));
1209 }
1210 }
1211
1212 fn visit_prop_name(&mut self, node: &PropName) {
1213 node.visit_children_with(self);
1214
1215 match node {
1216 PropName::Ident(ident) => {
1217 self.data.add_property_atom(ident.sym.clone().into());
1218 }
1219 PropName::Str(s) => {
1220 self.data.add_property_atom(s.value.clone());
1221 }
1222 _ => {}
1223 };
1224 }
1225
1226 fn visit_script(&mut self, n: &Script) {
1227 let ctx = self.ctx.with(BitContext::IsTopLevel, true);
1228 n.visit_children_with(&mut *self.with_ctx(ctx))
1229 }
1230
1231 #[cfg_attr(
1232 feature = "tracing-spans",
1233 tracing::instrument(level = "debug", skip_all)
1234 )]
1235 fn visit_setter_prop(&mut self, n: &SetterProp) {
1236 self.with_child(SyntaxContext::empty(), ScopeKind::Fn, |a| {
1237 n.key.visit_with(a);
1238 {
1239 let ctx = a.ctx.with(BitContext::InPatOfParam, true);
1240 n.param.visit_with(&mut *a.with_ctx(ctx));
1241 }
1242
1243 n.body.visit_with(a);
1244 });
1245 }
1246
1247 #[cfg_attr(
1248 feature = "tracing-spans",
1249 tracing::instrument(level = "debug", skip_all)
1250 )]
1251 fn visit_spread_element(&mut self, e: &SpreadElement) {
1252 e.visit_children_with(self);
1253
1254 for_each_id_ref_in_expr(&e.expr, &mut |i| {
1255 self.data
1256 .var_or_default(i.to_id())
1257 .mark_indexed_with_dynamic_key();
1258 });
1259 }
1260
1261 #[cfg_attr(
1262 feature = "tracing-spans",
1263 tracing::instrument(level = "debug", skip_all)
1264 )]
1265 fn visit_stmt(&mut self, n: &Stmt) {
1266 let ctx = self
1267 .ctx
1268 .with(BitContext::InAwaitArg, false)
1269 .with(BitContext::IsIdRef, true);
1270 n.visit_children_with(&mut *self.with_ctx(ctx));
1271 }
1272
1273 fn visit_stmts(&mut self, stmts: &[Stmt]) {
1274 let mut had_cond = false;
1275
1276 for stmt in stmts {
1277 let ctx = self
1278 .ctx
1279 .with(
1280 BitContext::InCond,
1281 self.ctx.bit_ctx.contains(BitContext::InCond) || had_cond,
1282 )
1283 .with(BitContext::IsIdRef, true);
1284
1285 stmt.visit_with(&mut *self.with_ctx(ctx));
1286
1287 had_cond |= can_end_conditionally(stmt);
1288 }
1289 }
1290
1291 #[cfg_attr(
1292 feature = "tracing-spans",
1293 tracing::instrument(level = "debug", skip(self, e))
1294 )]
1295 fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
1296 if let SuperProp::Computed(c) = &e.prop {
1297 let ctx = self.ctx.with(BitContext::IsIdRef, false);
1298 c.visit_with(&mut *self.with_ctx(ctx));
1299 }
1300 }
1301
1302 fn visit_switch_case(&mut self, n: &SwitchCase) {
1303 let ctx = self.ctx.with(BitContext::IsIdRef, false);
1304 n.visit_children_with(&mut *self.with_ctx(ctx))
1305 }
1306
1307 #[cfg_attr(
1308 feature = "tracing-spans",
1309 tracing::instrument(level = "debug", skip_all)
1310 )]
1311 fn visit_switch_stmt(&mut self, n: &SwitchStmt) {
1312 n.discriminant.visit_with(self);
1313
1314 let mut fallthrough = false;
1315
1316 for case in n.cases.iter() {
1317 let ctx = self.ctx.with(BitContext::InCond, true);
1318 if fallthrough {
1319 self.with_ctx(ctx).visit_in_cond(&case.test);
1320 self.with_ctx(ctx).visit_in_cond(&case.cons);
1321 } else {
1322 self.with_ctx(ctx).visit_in_cond(case);
1323 }
1324 fallthrough = !case.cons.iter().rev().any(|s| s.terminates())
1325 }
1326 }
1327
1328 #[cfg_attr(
1329 feature = "tracing-spans",
1330 tracing::instrument(level = "debug", skip_all)
1331 )]
1332 fn visit_tagged_tpl(&mut self, n: &TaggedTpl) {
1333 {
1334 let ctx = self.ctx.with(BitContext::IsIdRef, false);
1335 n.tag.visit_with(&mut *self.with_ctx(ctx));
1336 }
1337
1338 {
1339 let ctx = self.ctx.with(BitContext::IsIdRef, true);
1340 n.tpl.visit_children_with(&mut *self.with_ctx(ctx))
1342 }
1343 }
1344
1345 #[cfg_attr(
1346 feature = "tracing-spans",
1347 tracing::instrument(level = "debug", skip_all)
1348 )]
1349 fn visit_tpl(&mut self, n: &Tpl) {
1350 let ctx = self.ctx.with(BitContext::IsIdRef, false);
1351 n.visit_children_with(&mut *self.with_ctx(ctx))
1352 }
1353
1354 #[cfg_attr(
1355 feature = "tracing-spans",
1356 tracing::instrument(level = "debug", skip_all)
1357 )]
1358 fn visit_try_stmt(&mut self, n: &TryStmt) {
1359 let ctx = self.ctx.with(BitContext::InCond, true);
1360 self.with_ctx(ctx).visit_children_in_cond(n);
1361 }
1362
1363 #[cfg_attr(
1364 feature = "tracing-spans",
1365 tracing::instrument(level = "debug", skip_all)
1366 )]
1367 fn visit_unary_expr(&mut self, n: &UnaryExpr) {
1368 if n.op == op!("delete") {
1369 self.mark_mutation_if_member(n.arg.as_member());
1370 }
1371 n.visit_children_with(self);
1372 }
1373
1374 #[cfg_attr(
1375 feature = "tracing-spans",
1376 tracing::instrument(level = "debug", skip_all)
1377 )]
1378 fn visit_update_expr(&mut self, n: &UpdateExpr) {
1379 n.visit_children_with(self);
1380
1381 self.report_assign_expr_if_ident(n.arg.as_ident(), true, Value::Known(Type::Num));
1382 self.mark_mutation_if_member(n.arg.as_member());
1383 }
1384
1385 #[cfg_attr(
1386 feature = "tracing-spans",
1387 tracing::instrument(level = "debug", skip_all)
1388 )]
1389 fn visit_var_decl(&mut self, n: &VarDecl) {
1390 let ctx = Ctx {
1391 var_decl_kind_of_pat: Some(n.kind),
1392 bit_ctx: self.ctx.bit_ctx.with(BitContext::InAwaitArg, false),
1393 ..self.ctx
1394 };
1395 n.visit_children_with(&mut *self.with_ctx(ctx));
1396
1397 for decl in &n.decls {
1398 if let (Pat::Ident(var), Some(init)) = (&decl.name, decl.init.as_deref()) {
1399 let mut v = None;
1400 for id in collect_infects_from(
1401 init,
1402 AliasConfig {
1403 marks: self.marks,
1404 ignore_named_child_scope: true,
1405 ..Default::default()
1406 },
1407 ) {
1408 if v.is_none() {
1409 v = Some(self.data.var_or_default(var.to_id()));
1410 }
1411
1412 v.as_mut().unwrap().add_infects_to(id.clone());
1413 }
1414 }
1415 }
1416 }
1417
1418 #[cfg_attr(
1419 feature = "tracing-spans",
1420 tracing::instrument(level = "debug", skip(self, e))
1421 )]
1422 fn visit_var_declarator(&mut self, e: &VarDeclarator) {
1423 let prevent_inline = matches!(&e.name, Pat::Ident(BindingIdent {
1424 id: Ident { sym: arguments, .. },
1425 ..
1426 }) if (&**arguments == "arguments"));
1427 {
1428 let ctx = Ctx {
1429 bit_ctx: self
1430 .ctx
1431 .bit_ctx
1432 .with(
1433 BitContext::InlinePrevented,
1434 self.ctx.bit_ctx.contains(BitContext::InlinePrevented) || prevent_inline,
1435 )
1436 .with(BitContext::InPatOfVarDecl, true)
1437 .with(
1438 BitContext::InDeclWithNoSideEffectForMemberAccess,
1439 e.init
1440 .as_deref()
1441 .map(is_safe_to_access_prop)
1442 .unwrap_or(false),
1443 ),
1444 in_pat_of_var_decl_with_init: e
1445 .init
1446 .as_ref()
1447 .map(|init| init.get_type(self.expr_ctx)),
1448 ..self.ctx
1449 };
1450 e.name.visit_with(&mut *self.with_ctx(ctx));
1451 }
1452
1453 {
1454 let ctx = self
1455 .ctx
1456 .with(
1457 BitContext::InlinePrevented,
1458 self.ctx.bit_ctx.contains(BitContext::InlinePrevented) || prevent_inline,
1459 )
1460 .with(BitContext::InPatOfVarDecl, false)
1461 .with(BitContext::IsIdRef, true);
1462 if self.marks.is_some() {
1463 match e {
1464 VarDeclarator {
1465 name: Pat::Ident(id),
1466 init: Some(init),
1467 definite: false,
1468 ..
1469 } => {
1470 let id = id.to_id();
1471 self.used_recursively.insert(
1472 id.clone(),
1473 RecursiveUsage::Var {
1474 can_ignore: !init.may_have_side_effects(self.expr_ctx),
1475 },
1476 );
1477 e.init.visit_with(&mut *self.with_ctx(ctx));
1478 self.used_recursively.remove(&id);
1479 return;
1480 }
1481
1482 VarDeclarator {
1483 name: Pat::Ident(id),
1484 init: None,
1485 ..
1486 } => {
1487 self.data.var_or_default(id.to_id()).mark_as_lazy_init();
1488 return;
1489 }
1490 _ => (),
1491 }
1492 }
1493
1494 e.init.visit_with(&mut *self.with_ctx(ctx));
1495 }
1496 }
1497
1498 #[cfg_attr(
1499 feature = "tracing-spans",
1500 tracing::instrument(level = "debug", skip_all)
1501 )]
1502 fn visit_while_stmt(&mut self, n: &WhileStmt) {
1503 n.test
1504 .visit_with(&mut *self.with_ctx(self.ctx.with(BitContext::ExecutedMultipleTime, true)));
1505 let ctx = self
1506 .ctx
1507 .with(BitContext::ExecutedMultipleTime, true)
1508 .with(BitContext::InCond, true);
1509 self.with_ctx(ctx).visit_in_cond(&n.body);
1510 }
1511
1512 #[cfg_attr(
1513 feature = "tracing-spans",
1514 tracing::instrument(level = "debug", skip_all)
1515 )]
1516 fn visit_with_stmt(&mut self, n: &WithStmt) {
1517 self.scope.mark_with_stmt();
1518 n.visit_children_with(self);
1519 }
1520}
1521
1522fn for_each_id_ref_in_expr(e: &Expr, op: &mut impl FnMut(&Ident)) {
1525 match e {
1526 Expr::Ident(i) => op(i),
1527 Expr::Paren(p) => {
1528 for_each_id_ref_in_expr(&p.expr, op);
1529 }
1530 Expr::Cond(c) => {
1531 for_each_id_ref_in_expr(&c.cons, op);
1532 for_each_id_ref_in_expr(&c.alt, op);
1533 }
1534 Expr::Bin(b @ BinExpr { op: bin_op, .. }) if bin_op.may_short_circuit() => {
1535 for_each_id_ref_in_expr(&b.left, op);
1536 for_each_id_ref_in_expr(&b.right, op);
1537 }
1538
1539 Expr::Class(c) => {
1540 for_each_id_ref_in_class(&c.class, op);
1541 }
1542
1543 Expr::Fn(f) => {
1544 for_each_id_ref_in_fn(&f.function, op);
1545 }
1546
1547 Expr::Seq(s) => {
1548 for_each_id_ref_in_expr(s.exprs.last().unwrap(), op);
1549 }
1550
1551 Expr::Array(arr) => {
1552 arr.elems.iter().flatten().for_each(|e| {
1553 for_each_id_ref_in_expr(&e.expr, op);
1554 });
1555 }
1556
1557 Expr::Object(obj) => {
1558 obj.props.iter().for_each(|p| match p {
1559 PropOrSpread::Spread(p) => {
1560 for_each_id_ref_in_expr(&p.expr, op);
1561 }
1562 PropOrSpread::Prop(p) => match &**p {
1563 Prop::Shorthand(p) => {
1564 op(p);
1565 }
1566 Prop::KeyValue(p) => {
1567 for_each_id_ref_in_prop_name(&p.key, op);
1568 for_each_id_ref_in_expr(&p.value, op);
1569 }
1570 Prop::Assign(p) => {
1571 for_each_id_ref_in_expr(&p.value, op);
1572 }
1573 Prop::Getter(p) => {
1574 for_each_id_ref_in_prop_name(&p.key, op);
1575 }
1576 Prop::Setter(p) => {
1577 for_each_id_ref_in_prop_name(&p.key, op);
1578
1579 for_each_id_ref_in_pat(&p.param, op);
1580 }
1581 Prop::Method(p) => {
1582 for_each_id_ref_in_fn(&p.function, op);
1583 }
1584 #[cfg(swc_ast_unknown)]
1585 _ => panic!("unable to access unknown nodes"),
1586 },
1587 #[cfg(swc_ast_unknown)]
1588 _ => panic!("unable to access unknown nodes"),
1589 });
1590 }
1591 _ => {}
1592 }
1593}
1594
1595fn for_each_id_ref_in_class(c: &Class, op: &mut impl FnMut(&Ident)) {
1596 c.body.iter().for_each(|m| match m {
1597 ClassMember::Constructor(m) => {
1598 for_each_id_ref_in_prop_name(&m.key, op);
1599 m.params.iter().for_each(|p| match p {
1600 ParamOrTsParamProp::TsParamProp(..) => {
1601 unreachable!()
1602 }
1603 ParamOrTsParamProp::Param(p) => {
1604 for_each_id_ref_in_pat(&p.pat, op);
1605 }
1606 #[cfg(swc_ast_unknown)]
1607 _ => panic!("unable to access unknown nodes"),
1608 });
1609 }
1610
1611 ClassMember::Method(m) => {
1612 for_each_id_ref_in_prop_name(&m.key, op);
1613 for_each_id_ref_in_fn(&m.function, op);
1614 }
1615
1616 ClassMember::PrivateMethod(m) => {
1617 for_each_id_ref_in_fn(&m.function, op);
1618 }
1619
1620 ClassMember::ClassProp(m) => {
1621 for_each_id_ref_in_prop_name(&m.key, op);
1622 if let Some(value) = &m.value {
1623 for_each_id_ref_in_expr(value, op);
1624 }
1625 }
1626
1627 ClassMember::PrivateProp(m) => {
1628 if let Some(value) = &m.value {
1629 for_each_id_ref_in_expr(value, op);
1630 }
1631 }
1632
1633 ClassMember::AutoAccessor(m) => {
1634 if let Key::Public(key) = &m.key {
1635 for_each_id_ref_in_prop_name(key, op);
1636 }
1637
1638 if let Some(v) = &m.value {
1639 for_each_id_ref_in_expr(v, op);
1640 }
1641 }
1642
1643 ClassMember::Empty(..)
1644 | ClassMember::StaticBlock(..)
1645 | ClassMember::TsIndexSignature(..) => {}
1646 #[cfg(swc_ast_unknown)]
1647 _ => panic!("unable to access unknown nodes"),
1648 });
1649}
1650fn for_each_id_ref_in_prop_name(p: &PropName, op: &mut impl FnMut(&Ident)) {
1651 if let PropName::Computed(p) = p {
1652 for_each_id_ref_in_expr(&p.expr, op);
1653 }
1654}
1655
1656fn for_each_id_ref_in_pat(p: &Pat, op: &mut impl FnMut(&Ident)) {
1657 match p {
1658 Pat::Ident(..) => {
1659 }
1661 Pat::Array(p) => {
1662 p.elems.iter().flatten().for_each(|e| {
1663 for_each_id_ref_in_pat(e, op);
1664 });
1665 }
1666 Pat::Rest(p) => {
1667 for_each_id_ref_in_pat(&p.arg, op);
1668 }
1669 Pat::Object(p) => {
1670 p.props.iter().for_each(|p| match p {
1671 ObjectPatProp::KeyValue(p) => {
1672 for_each_id_ref_in_prop_name(&p.key, op);
1673 for_each_id_ref_in_pat(&p.value, op);
1674 }
1675 ObjectPatProp::Assign(p) => {
1676 if let Some(value) = &p.value {
1679 for_each_id_ref_in_expr(value, op);
1680 }
1681 }
1682 ObjectPatProp::Rest(p) => {
1683 for_each_id_ref_in_pat(&p.arg, op);
1684 }
1685 #[cfg(swc_ast_unknown)]
1686 _ => panic!("unable to access unknown nodes"),
1687 });
1688 }
1689 Pat::Assign(p) => {
1690 for_each_id_ref_in_pat(&p.left, op);
1691 for_each_id_ref_in_expr(&p.right, op);
1692 }
1693 Pat::Invalid(..) => {}
1694 Pat::Expr(p) => {
1695 for_each_id_ref_in_expr(p, op);
1696 }
1697 #[cfg(swc_ast_unknown)]
1698 _ => panic!("unable to access unknown nodes"),
1699 }
1700}
1701
1702fn for_each_id_ref_in_fn(f: &Function, op: &mut impl FnMut(&Ident)) {
1703 for p in &f.params {
1704 for_each_id_ref_in_pat(&p.pat, op);
1705 }
1706}
1707
1708fn is_safe_to_access_prop(e: &Expr) -> bool {
1710 match e {
1711 Expr::Lit(Lit::Null(..)) => false,
1712 Expr::Lit(..) | Expr::Array(..) | Expr::Fn(..) | Expr::Arrow(..) | Expr::Update(..) => true,
1713 _ => false,
1714 }
1715}
1716
1717fn call_may_mutate(expr: &Expr, expr_ctx: ExprCtx) -> bool {
1718 fn is_global_fn_wont_mutate(s: &Ident, unresolved: SyntaxContext) -> bool {
1719 s.ctxt == unresolved
1720 && matches!(
1721 &*s.sym,
1722 "JSON"
1723 | "String"
1725 | "Number"
1727 | "Date"
1728 | "BigInt"
1729 | "Boolean"
1730 | "Math"
1731 | "Error"
1732 | "console"
1733 | "clearInterval"
1734 | "clearTimeout"
1735 | "setInterval"
1736 | "setTimeout"
1737 | "btoa"
1738 | "decodeURI"
1739 | "decodeURIComponent"
1740 | "encodeURI"
1741 | "encodeURIComponent"
1742 | "escape"
1743 | "eval"
1744 | "EvalError"
1745 | "Function"
1746 | "isFinite"
1747 | "isNaN"
1748 | "parseFloat"
1749 | "parseInt"
1750 | "RegExp"
1751 | "RangeError"
1752 | "ReferenceError"
1753 | "SyntaxError"
1754 | "TypeError"
1755 | "unescape"
1756 | "URIError"
1757 | "atob"
1758 | "globalThis"
1759 | "NaN"
1760 | "Symbol"
1761 | "Promise"
1762 )
1763 }
1764
1765 if expr.is_pure_callee(expr_ctx) {
1766 false
1767 } else {
1768 match expr {
1769 Expr::Ident(i) if is_global_fn_wont_mutate(i, expr_ctx.unresolved_ctxt) => false,
1770 Expr::Member(MemberExpr { obj, .. }) => {
1771 !matches!(&**obj, Expr::Ident(i) if is_global_fn_wont_mutate(i, expr_ctx.unresolved_ctxt))
1772 }
1773 _ => true,
1774 }
1775 }
1776}