swc_coverage_instrument/macros/
create_instrumentation_visitor.rs

1/// Expand given struct to contain necessary common filed for the coverage visitor
2/// with common utility functions.
3///
4/// This does not impl actual visitors (VisitMut) as each visitor may have different
5/// visitor logics.
6#[macro_export]
7macro_rules! create_instrumentation_visitor {
8    ($name:ident { $($vis: vis $field:ident: $t:ty),* $(,)? }) => {
9        #[allow(unused)]
10        use swc_core::{common::{Span, Spanned}, ecma::ast::*};
11
12        // Declare a struct, expand fields commonly used for any instrumentation visitor.
13        pub struct $name<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> {
14            // We may not need Arc in the plugin context - this is only to preserve isomorphic interface
15            // between plugin & custom transform pass.
16            source_map: std::sync::Arc<S>,
17            comments: C,
18            cov: std::rc::Rc<std::cell::RefCell<crate::SourceCoverage>>,
19            cov_fn_ident: Ident,
20            cov_fn_temp_ident: Ident,
21            instrument_options: crate::InstrumentOptions,
22            // Current visitor state to hold stmts to be prepended by parent node.
23            #[allow(dead_code)] pub before: Vec<Stmt>,
24            nodes: Vec<crate::Node>,
25            should_ignore: Option<crate::hint_comments::IgnoreScope>,
26            $($vis $field: $t,)*
27        }
28
29        impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> $name<C, S> {
30            pub fn new(
31                source_map: std::sync::Arc<S>,
32                comments: C,
33                cov: std::rc::Rc<std::cell::RefCell<crate::SourceCoverage>>,
34                instrument_options: crate::InstrumentOptions,
35                nodes: Vec<crate::Node>,
36                should_ignore: Option<crate::hint_comments::IgnoreScope>,
37                $($field: $t,)*
38            ) -> $name<C, S> {
39                $name {
40                    source_map: source_map,
41                    comments: comments,
42                    cov: cov,
43                    cov_fn_ident: crate::COVERAGE_FN_IDENT.get().expect("Coverage fn Ident should be initialized already").clone(),
44                    cov_fn_temp_ident: crate::COVERAGE_FN_TRUE_TEMP_IDENT.get().expect("Coverage fn Ident should be initialized already").clone(),
45                    instrument_options: instrument_options,
46                    before: vec![],
47                    nodes: nodes,
48                    should_ignore,
49                    $($field,)*
50                }
51            }
52
53            // Display current nodes.
54            fn print_node(&self) -> String {
55                if self.nodes.len() > 0 {
56                    format!(
57                        "{}",
58                        self.nodes
59                            .iter()
60                            .map(|n| n.to_string())
61                            .collect::<Vec<String>>()
62                            .join(":")
63                    )
64                } else {
65                    "".to_string()
66                }
67            }
68
69            fn on_enter_with_span(&mut self, span: Option<&Span>) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
70                let old = self.should_ignore;
71                let ret = match old {
72                    Some(crate::hint_comments::IgnoreScope::Next) => old,
73                    _ => {
74                        self.should_ignore = crate::hint_comments::should_ignore(&self.comments, span);
75                        self.should_ignore
76                    }
77                };
78
79                (old, ret)
80            }
81
82            fn on_exit(&mut self, old: Option<crate::hint_comments::IgnoreScope>) {
83                self.should_ignore = old;
84                self.nodes.pop();
85            }
86        }
87
88
89        /// A trait expands to the ast types we want to use to determine if we need to ignore
90        /// certain section of the code for the instrumentation.
91        /// TODO: Can a macro like `on_visit_mut_expr` expands on_enter / exit automatically?
92        /// `on_visit_mut_expr!(|expr| {self.xxx})` doesn't seem to work.
93        trait CoverageInstrumentationMutVisitEnter<N> {
94            fn on_enter(&mut self, n: &mut N) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>);
95        }
96
97        // Macro generates trait impl for the type can access span directly.
98        macro_rules! on_enter {
99            ($N: tt) => {
100                impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<$N> for $name<C, S> {
101                    #[inline]
102                    fn on_enter(&mut self, n: &mut swc_core::ecma::ast::$N) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
103                        self.nodes.push(crate::Node::$N);
104                        self.on_enter_with_span(Some(&n.span))
105                    }
106                 }
107            }
108        }
109
110        impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<Expr> for $name<C, S> {
111            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::Expr) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
112                self.nodes.push(crate::Node::Expr);
113                let span = n.span();
114                self.on_enter_with_span(Some(&span))
115            }
116         }
117
118         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<Stmt> for $name<C, S> {
119            fn on_enter(&mut self, n: &mut Stmt) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
120                self.nodes.push(crate::Node::Stmt);
121                self.on_enter_with_span(Some(&n.span()))
122            }
123         }
124
125         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<ModuleDecl> for $name<C, S> {
126            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::ModuleDecl) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
127                self.nodes.push(crate::Node::ModuleDecl);
128                let span = n.span();
129
130                self.on_enter_with_span(Some(&span))
131            }
132         }
133
134         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<ClassDecl> for $name<C, S> {
135            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::ClassDecl) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
136                self.nodes.push(crate::Node::ClassDecl);
137                self.on_enter_with_span(Some(&n.class.span))
138            }
139         }
140
141         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<FnExpr> for $name<C, S> {
142            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::FnExpr) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
143                self.nodes.push(crate::Node::FnExpr);
144                self.on_enter_with_span(Some(&n.function.span))
145            }
146         }
147
148         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<MethodProp> for $name<C, S> {
149            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::MethodProp) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
150                self.nodes.push(crate::Node::MethodProp);
151                self.on_enter_with_span(Some(&n.function.span))
152            }
153         }
154
155         impl<C: Clone + swc_core::common::comments::Comments, S: swc_core::common::SourceMapper> CoverageInstrumentationMutVisitEnter<FnDecl> for $name<C, S> {
156            fn on_enter(&mut self, n: &mut swc_core::ecma::ast::FnDecl) -> (Option<crate::hint_comments::IgnoreScope>, Option<crate::hint_comments::IgnoreScope>) {
157                self.nodes.push(crate::Node::FnDecl);
158                self.on_enter_with_span(Some(&n.function.span))
159            }
160         }
161
162         on_enter!(BinExpr);
163         on_enter!(VarDeclarator);
164         on_enter!(VarDecl);
165         on_enter!(CondExpr);
166         on_enter!(ExprStmt);
167         on_enter!(IfStmt);
168         on_enter!(LabeledStmt);
169         on_enter!(ContinueStmt);
170         on_enter!(ClassProp);
171         on_enter!(PrivateProp);
172         on_enter!(ClassMethod);
173         on_enter!(ArrowExpr);
174         on_enter!(ForStmt);
175         on_enter!(ForOfStmt);
176         on_enter!(ForInStmt);
177         on_enter!(WhileStmt);
178         on_enter!(DoWhileStmt);
179         on_enter!(SwitchStmt);
180         on_enter!(SwitchCase);
181         on_enter!(BreakStmt);
182         on_enter!(ReturnStmt);
183         on_enter!(BlockStmt);
184         on_enter!(WithStmt);
185         on_enter!(TryStmt);
186         on_enter!(ThrowStmt);
187         on_enter!(ExportDecl);
188         on_enter!(ExportDefaultDecl);
189         on_enter!(DebuggerStmt);
190         on_enter!(AssignPat);
191         on_enter!(GetterProp);
192         on_enter!(SetterProp);
193         on_enter!(TaggedTpl);
194    }
195}