swc_ecma_transforms_base/hygiene/
mod.rs

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