swc_ecma_transforms_base/rename/
analyer_and_collector.rs

1use std::hash::Hash;
2
3use rustc_hash::FxHashSet;
4use swc_atoms::Atom;
5use swc_common::{Mark, SyntaxContext};
6use swc_ecma_ast::*;
7use swc_ecma_utils::{ident::IdentLike, stack_size::maybe_grow_default};
8use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
9
10use super::{analyzer::scope::Scope, Analyzer};
11
12struct IdCollector {
13    ids: FxHashSet<Id>,
14    stopped: bool,
15}
16
17impl IdCollector {
18    fn handle_ident(&mut self, n: &Ident) {
19        if !self.stopped && n.ctxt != SyntaxContext::empty() {
20            self.ids.insert(n.to_id());
21        }
22    }
23}
24
25struct CustomBindingCollector<I>
26where
27    I: IdentLike + Eq + Hash + Send + Sync,
28{
29    bindings: FxHashSet<I>,
30    preserved: FxHashSet<I>,
31    is_pat_decl: bool,
32
33    /// [None] if there's no `eval`.
34    pub top_level_for_eval: Option<SyntaxContext>,
35}
36
37impl<I> CustomBindingCollector<I>
38where
39    I: IdentLike + Eq + Hash + Send + Sync,
40{
41    fn add(&mut self, i: &Ident) {
42        if let Some(top_level_ctxt) = self.top_level_for_eval {
43            if i.ctxt == top_level_ctxt {
44                self.preserved.insert(I::from_ident(i));
45                return;
46            }
47        }
48
49        self.bindings.insert(I::from_ident(i));
50    }
51
52    fn handle_assign_pat_prop(&mut self, node: &AssignPatProp) {
53        if self.is_pat_decl {
54            self.add(&Ident::from(&node.key));
55        }
56    }
57
58    fn handle_binding_ident(&mut self, node: &BindingIdent) {
59        if self.is_pat_decl {
60            self.add(&Ident::from(node));
61        }
62    }
63
64    fn handle_class_expr(&mut self, node: &ClassExpr) {
65        if let Some(id) = &node.ident {
66            self.add(id);
67        }
68    }
69
70    fn handle_fn_expr(&mut self, node: &FnExpr) {
71        if let Some(id) = &node.ident {
72            self.add(id);
73        }
74    }
75}
76
77pub(super) struct AnalyzerAndCollector {
78    analyzer: Analyzer,
79    id_collector: IdCollector,
80    decl_collector: CustomBindingCollector<Id>,
81}
82
83impl Visit for AnalyzerAndCollector {
84    noop_visit_type!();
85
86    fn visit_arrow_expr(&mut self, node: &ArrowExpr) {
87        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
88        self.decl_collector.is_pat_decl = true;
89
90        let old_analyzer = self.analyzer.enter_fn_scope();
91        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
92        self.analyzer.is_pat_decl = true;
93
94        node.params.visit_with(self);
95
96        // FIXME: Tests failing after this change - needs investigation
97        // self.decl_collector.is_pat_decl = false;
98        self.analyzer.is_pat_decl = false;
99
100        match node.body.as_ref() {
101            BlockStmtOrExpr::BlockStmt(n) => n.visit_children_with(self),
102            BlockStmtOrExpr::Expr(n) => n.visit_with(self),
103            #[cfg(swc_ast_unknown)]
104            _ => (),
105        }
106
107        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
108        self.analyzer.exit_scope(old_analyzer);
109
110        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
111    }
112
113    fn visit_assign_target(&mut self, node: &AssignTarget) {
114        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
115        self.analyzer.is_pat_decl = false;
116
117        node.visit_children_with(self);
118
119        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
120    }
121
122    fn visit_assign_pat_prop(&mut self, node: &AssignPatProp) {
123        node.visit_children_with(self);
124
125        self.decl_collector.handle_assign_pat_prop(node);
126    }
127
128    fn visit_binding_ident(&mut self, node: &BindingIdent) {
129        node.visit_children_with(self);
130
131        self.decl_collector.handle_binding_ident(node);
132        self.analyzer.handle_binding_ident(node);
133    }
134
135    fn visit_block_stmt(&mut self, node: &BlockStmt) {
136        let old_analyzer = self.analyzer.enter_block_scope();
137
138        node.visit_children_with(self);
139
140        self.analyzer.exit_scope(old_analyzer);
141    }
142
143    fn visit_bin_expr(&mut self, node: &BinExpr) {
144        maybe_grow_default(|| node.visit_children_with(self));
145    }
146
147    fn visit_catch_clause(&mut self, node: &CatchClause) {
148        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
149
150        let old_analyzer = self.analyzer.enter_block_scope();
151        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
152        let old_analyzer_in_catch_params = self.analyzer.in_catch_params;
153
154        self.decl_collector.is_pat_decl = false;
155        self.analyzer.in_catch_params = false;
156        self.analyzer.is_pat_decl = false;
157
158        node.body.visit_children_with(self);
159
160        self.decl_collector.is_pat_decl = true;
161        self.analyzer.is_pat_decl = true;
162        self.analyzer.in_catch_params = true;
163
164        node.param.visit_with(self);
165
166        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
167
168        self.analyzer.in_catch_params = old_analyzer_in_catch_params;
169        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
170        self.analyzer.exit_scope(old_analyzer);
171    }
172
173    fn visit_class_decl(&mut self, node: &ClassDecl) {
174        self.analyzer.handle_class_decl(node);
175
176        node.visit_children_with(self);
177
178        self.decl_collector.add(&node.ident);
179    }
180
181    fn visit_class_expr(&mut self, node: &ClassExpr) {
182        let old_analyzer = self.analyzer.enter_block_scope();
183
184        self.analyzer.handle_class_expr(node);
185
186        node.visit_children_with(self);
187
188        self.decl_collector.handle_class_expr(node);
189
190        self.analyzer.exit_scope(old_analyzer);
191    }
192
193    fn visit_class_method(&mut self, node: &ClassMethod) {
194        node.key.visit_with(self);
195
196        let old_analyzer = self.analyzer.enter_fn_scope();
197
198        node.function.decorators.visit_with(self);
199        node.function.params.visit_with(self);
200        if let Some(body) = &node.function.body {
201            body.visit_children_with(self);
202        }
203
204        self.analyzer.exit_scope(old_analyzer);
205    }
206
207    fn visit_constructor(&mut self, node: &Constructor) {
208        let old_analyzer = self.analyzer.enter_fn_scope();
209        node.key.visit_with(self);
210        node.params.visit_with(self);
211        if let Some(body) = &node.body {
212            body.visit_children_with(self);
213        }
214
215        self.analyzer.exit_scope(old_analyzer);
216    }
217
218    fn visit_default_decl(&mut self, node: &DefaultDecl) {
219        match node {
220            DefaultDecl::Class(c) => {
221                self.analyzer.handle_class_expr(c);
222                self.decl_collector.handle_class_expr(c);
223
224                let old_analyzer = self.analyzer.enter_fn_scope();
225                c.visit_children_with(self);
226                self.analyzer.exit_scope(old_analyzer);
227            }
228            DefaultDecl::Fn(f) => {
229                self.analyzer.handle_fn_expr(f);
230
231                f.visit_with(self);
232            }
233            DefaultDecl::TsInterfaceDecl(_) => {}
234            #[cfg(swc_ast_unknown)]
235            _ => {}
236        }
237    }
238
239    fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
240        let old_id_collector_stopped = self.id_collector.stopped;
241        self.id_collector.stopped = true;
242
243        self.analyzer.handle_export_named_specifier(n);
244
245        n.visit_children_with(self);
246
247        self.id_collector.stopped = old_id_collector_stopped;
248    }
249
250    fn visit_expr(&mut self, node: &Expr) {
251        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
252        self.decl_collector.is_pat_decl = false;
253
254        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
255        self.analyzer.is_pat_decl = false;
256
257        self.analyzer.handle_expr(node);
258
259        node.visit_children_with(self);
260
261        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
262        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
263    }
264
265    fn visit_fn_decl(&mut self, node: &FnDecl) {
266        // https://github.com/swc-project/swc/issues/6819
267        //
268        // We need to check for assign pattern because safari has a bug.
269        // https://github.com/swc-project/swc/issues/9015
270        let has_rest = node
271            .function
272            .params
273            .iter()
274            .any(|p| p.pat.is_rest() || p.pat.is_assign());
275        let need_skip_analyzer_record =
276            self.analyzer.is_first_node && self.analyzer.skip_first_fn_or_class_decl;
277        self.analyzer.is_first_node = false;
278
279        if !need_skip_analyzer_record {
280            self.analyzer.add_decl(node.ident.to_id(), true);
281
282            if has_rest {
283                self.analyzer.add_usage(node.ident.to_id());
284            }
285        }
286
287        node.ident.visit_with(self);
288
289        let old_analyzer = self.analyzer.enter_fn_scope();
290
291        if !need_skip_analyzer_record && has_rest {
292            // self.analyer is different from above because we are in a function scope
293            self.analyzer.add_usage(node.ident.to_id());
294        }
295
296        node.function.decorators.visit_with(self);
297        node.function.params.visit_with(self);
298        if let Some(body) = &node.function.body {
299            body.visit_children_with(self);
300        }
301
302        self.analyzer.exit_scope(old_analyzer);
303        self.decl_collector.add(&node.ident);
304    }
305
306    fn visit_fn_expr(&mut self, node: &FnExpr) {
307        if let Some(id) = &node.ident {
308            let old_analyzer0 = self.analyzer.enter_fn_scope();
309            self.analyzer.add_decl(id.to_id(), true);
310            let old_analyzer1 = self.analyzer.enter_fn_scope();
311            // https://github.com/swc-project/swc/issues/6819
312            //
313            // We need to check for assign pattern because safari has a bug.
314            // https://github.com/swc-project/swc/issues/9015
315            if node
316                .function
317                .params
318                .iter()
319                .any(|p| p.pat.is_rest() || p.pat.is_assign())
320            {
321                self.analyzer.add_usage(id.to_id());
322            }
323            node.function.decorators.visit_with(self);
324            node.function.params.visit_with(self);
325            if let Some(body) = &node.function.body {
326                body.visit_children_with(self);
327            }
328            self.analyzer.exit_scope(old_analyzer1);
329            self.analyzer.exit_scope(old_analyzer0);
330        } else {
331            node.visit_children_with(self)
332        }
333
334        self.decl_collector.handle_fn_expr(node);
335    }
336
337    fn visit_for_in_stmt(&mut self, node: &ForInStmt) {
338        let old_analyzer0 = self.analyzer.enter_block_scope();
339        node.left.visit_with(self);
340        node.right.visit_with(self);
341        let old_analyzer1 = self.analyzer.enter_block_scope();
342        match node.body.as_ref() {
343            Stmt::Block(n) => n.visit_children_with(self),
344            _ => node.body.visit_with(self),
345        }
346        self.analyzer.exit_scope(old_analyzer1);
347        self.analyzer.exit_scope(old_analyzer0);
348    }
349
350    fn visit_for_of_stmt(&mut self, node: &ForOfStmt) {
351        let old_analyzer0 = self.analyzer.enter_block_scope();
352        node.left.visit_with(self);
353        node.right.visit_with(self);
354        let old_analyzer1 = self.analyzer.enter_block_scope();
355        match node.body.as_ref() {
356            Stmt::Block(n) => n.visit_children_with(self),
357            _ => node.body.visit_with(self),
358        }
359        self.analyzer.exit_scope(old_analyzer1);
360        self.analyzer.exit_scope(old_analyzer0);
361    }
362
363    fn visit_for_stmt(&mut self, node: &ForStmt) {
364        let old_analyzer0 = self.analyzer.enter_block_scope();
365        node.init.visit_with(self);
366        node.test.visit_with(self);
367        node.update.visit_with(self);
368        let old_analyzer1 = self.analyzer.enter_block_scope();
369        match node.body.as_ref() {
370            Stmt::Block(n) => n.visit_children_with(self),
371            _ => node.body.visit_with(self),
372        }
373        self.analyzer.exit_scope(old_analyzer1);
374        self.analyzer.exit_scope(old_analyzer0);
375    }
376
377    fn visit_function(&mut self, node: &Function) {
378        let old_analyzer = self.analyzer.enter_fn_scope();
379
380        node.decorators.visit_with(self);
381        node.params.visit_with(self);
382        if let Some(body) = &node.body {
383            body.visit_children_with(self);
384        }
385
386        self.analyzer.exit_scope(old_analyzer);
387    }
388
389    fn visit_import_default_specifier(&mut self, node: &ImportDefaultSpecifier) {
390        node.visit_children_with(self);
391
392        self.analyzer.add_decl(node.local.to_id(), true);
393        self.decl_collector.add(&node.local);
394    }
395
396    fn visit_import_named_specifier(&mut self, node: &ImportNamedSpecifier) {
397        node.visit_children_with(self);
398
399        self.analyzer.add_decl(node.local.to_id(), true);
400        self.decl_collector.add(&node.local);
401    }
402
403    fn visit_import_star_as_specifier(&mut self, node: &ImportStarAsSpecifier) {
404        node.visit_children_with(self);
405
406        self.analyzer.add_decl(node.local.to_id(), true);
407        self.decl_collector.add(&node.local);
408    }
409
410    fn visit_param(&mut self, node: &Param) {
411        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
412        self.decl_collector.is_pat_decl = true;
413
414        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
415        let old_analyzer_need_hoist = self.analyzer.var_belong_to_fn_scope;
416
417        // Params belong to function scope.
418        // Params in catch clause belong to block scope
419        self.analyzer.var_belong_to_fn_scope = !self.analyzer.in_catch_params;
420        self.analyzer.is_pat_decl = false;
421        node.decorators.visit_with(self);
422
423        self.analyzer.is_pat_decl = true;
424        node.pat.visit_with(self);
425
426        self.analyzer.var_belong_to_fn_scope = old_analyzer_need_hoist;
427        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
428        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
429    }
430
431    fn visit_prop(&mut self, node: &Prop) {
432        node.visit_children_with(self);
433        self.analyzer.handle_prop(node);
434    }
435
436    fn visit_static_block(&mut self, node: &StaticBlock) {
437        let old_analyzer = self.analyzer.enter_fn_scope();
438
439        node.body.visit_children_with(self);
440
441        self.analyzer.exit_scope(old_analyzer);
442    }
443
444    fn visit_stmt(&mut self, node: &Stmt) {
445        self.analyzer.is_first_node = false;
446        node.visit_children_with(self);
447    }
448
449    fn visit_ts_param_prop(&mut self, p: &TsParamProp) {
450        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
451        self.decl_collector.is_pat_decl = true;
452
453        p.visit_children_with(self);
454
455        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
456    }
457
458    fn visit_var_decl(&mut self, node: &VarDecl) {
459        let old_analyzer_need_hoisted = self.analyzer.var_belong_to_fn_scope;
460        self.analyzer.var_belong_to_fn_scope = node.kind == VarDeclKind::Var;
461
462        node.visit_children_with(self);
463
464        self.analyzer.var_belong_to_fn_scope = old_analyzer_need_hoisted;
465    }
466
467    fn visit_var_declarator(&mut self, node: &VarDeclarator) {
468        let old_decl_collector_is_pat_decl = self.decl_collector.is_pat_decl;
469        self.decl_collector.is_pat_decl = true;
470
471        let old_analyzer_is_pat_decl = self.analyzer.is_pat_decl;
472        self.analyzer.is_pat_decl = true;
473
474        node.name.visit_with(self);
475
476        self.decl_collector.is_pat_decl = false;
477        self.analyzer.is_pat_decl = false;
478
479        node.init.visit_with(self);
480
481        self.analyzer.is_pat_decl = old_analyzer_is_pat_decl;
482        self.decl_collector.is_pat_decl = old_decl_collector_is_pat_decl;
483    }
484
485    fn visit_super_prop(&mut self, node: &SuperProp) {
486        let old_id_collector_stopped = self.id_collector.stopped;
487        if node.is_ident() {
488            self.id_collector.stopped = true;
489        }
490
491        node.visit_children_with(self);
492
493        if node.is_ident() {
494            self.id_collector.stopped = old_id_collector_stopped;
495        }
496    }
497
498    fn visit_export_default_specifier(&mut self, n: &ExportDefaultSpecifier) {
499        let old_id_collector_stopped = self.id_collector.stopped;
500        self.id_collector.stopped = true;
501
502        n.visit_children_with(self);
503
504        self.id_collector.stopped = old_id_collector_stopped;
505    }
506
507    fn visit_export_namespace_specifier(&mut self, n: &ExportNamespaceSpecifier) {
508        let old_id_collector_stopped = self.id_collector.stopped;
509        self.id_collector.stopped = true;
510
511        n.visit_children_with(self);
512
513        self.id_collector.stopped = old_id_collector_stopped;
514    }
515
516    fn visit_ident(&mut self, id: &Ident) {
517        self.id_collector.handle_ident(id);
518    }
519
520    fn visit_named_export(&mut self, node: &NamedExport) {
521        let old_id_collector_stopped = self.id_collector.stopped;
522        if node.src.is_some() {
523            self.id_collector.stopped = true;
524        }
525
526        node.visit_children_with(self);
527
528        if node.src.is_some() {
529            self.id_collector.stopped = old_id_collector_stopped;
530        }
531    }
532}
533
534pub(super) fn analyzer_and_collect_unresolved<N>(
535    n: &N,
536    has_eval: bool,
537    top_level_mark: Mark,
538    skip_first_fn_or_class_decl: bool,
539) -> (Scope, FxHashSet<Atom>)
540where
541    N: VisitWith<AnalyzerAndCollector>,
542{
543    let analyzer = Analyzer::new(has_eval, top_level_mark, skip_first_fn_or_class_decl);
544    let id_collector = IdCollector {
545        ids: Default::default(),
546        stopped: false,
547    };
548    let decl_collector = CustomBindingCollector {
549        bindings: Default::default(),
550        preserved: Default::default(),
551        is_pat_decl: false,
552        top_level_for_eval: has_eval
553            .then_some(top_level_mark)
554            .map(|m| SyntaxContext::empty().apply_mark(m)),
555    };
556
557    let mut v = AnalyzerAndCollector {
558        analyzer,
559        id_collector,
560        decl_collector,
561    };
562
563    n.visit_with(&mut v);
564
565    let (usages, decls, preserved) = (
566        v.id_collector.ids,
567        v.decl_collector.bindings,
568        v.decl_collector.preserved,
569    );
570
571    let unresolved = usages
572        .into_iter()
573        .filter(|used_id| !decls.contains(used_id))
574        .map(|v| v.0)
575        .chain(preserved.into_iter().map(|v| v.0))
576        .collect::<FxHashSet<_>>();
577
578    (v.analyzer.scope, unresolved)
579}