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}