swc_ecma_transforms_base/hygiene/mod.rs
1use rustc_hash::FxHashSet;
2use swc_atoms::Atom;
3use swc_common::Mark;
4use swc_ecma_ast::*;
5use swc_ecma_utils::stack_size::maybe_grow_default;
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7
8pub use crate::rename::rename;
9use crate::rename::{renamer, Renamer};
10
11#[cfg(test)]
12mod tests;
13
14#[derive(Debug, Clone, Default)]
15pub struct Config {
16 /// If true, the `hygiene` pass will preserve class names.
17 pub keep_class_names: bool,
18
19 /// The marks derived from this marks will treated as `specified by user`
20 /// and other marks will be treated as `generated by swc`.
21 pub top_level_mark: Mark,
22
23 /// Mangle even if vars are visible to `eval` or `with`.
24 pub ignore_eval: bool,
25
26 /// Used for preventing mangler from renaming variables to reserved names.
27 pub preserved_symbols: FxHashSet<Atom>,
28}
29
30/// See [hygiene_with_config] for doc. Creates a `hygiene` pass with default
31/// value of [Config].
32pub fn hygiene() -> impl Pass + VisitMut {
33 hygiene_with_config(Default::default())
34}
35
36/// The pass actually modifies the identifiers in the way that different
37/// identifier (with respect to span hygiene) becomes different identifier.
38/// (e.g. `a1` for `a#6`, `a2` for `a#23`)
39///
40/// # Implementation details
41///
42/// This document exists For curious people and potential contributors.
43///
44/// `hygiene` consists of three phases.
45///
46/// ## First phase
47///
48/// At first phase, we mark (using [swc_common::Mark]) nodes which can be
49/// considered as a `scope`. e.g. [Function], [BlockStmt], [ArrowExpr]
50///
51/// ## Second phase
52///
53/// At second phase, we analyzes the file and determine identifiers to rename.
54///
55/// Note that we store scoping information for each node, using the fact that
56/// [SyntaxContext] of all `scope` nodes are unique, thanks to the first phase.
57///
58///
59/// ## Third phase
60///
61/// At third phase, we rename all identifiers in the queue.
62pub fn hygiene_with_config(config: Config) -> impl 'static + Pass + VisitMut {
63 (
64 renamer(config, HygieneRenamer),
65 visit_mut_pass(HygieneRemover),
66 )
67}
68
69struct HygieneRenamer;
70
71impl Renamer for HygieneRenamer {
72 const MANGLE: bool = false;
73 const RESET_N: bool = true;
74
75 fn new_name_for(&self, orig: &Id, n: &mut usize) -> swc_atoms::Atom {
76 let res = if *n == 0 {
77 orig.0.clone()
78 } else {
79 format!("{}{}", orig.0, n).into()
80 };
81 *n += 1;
82 res
83 }
84}
85
86struct HygieneRemover;
87
88impl VisitMut for HygieneRemover {
89 noop_visit_mut_type!();
90
91 fn visit_mut_expr(&mut self, n: &mut Expr) {
92 maybe_grow_default(|| n.visit_mut_children_with(self));
93 }
94
95 fn visit_mut_ident(&mut self, i: &mut Ident) {
96 i.ctxt = Default::default();
97 }
98}