swc_ecma_lints/
rule.rs

1use std::{fmt::Debug, mem::take, sync::Arc};
2
3use auto_impl::auto_impl;
4use parking_lot::Mutex;
5use swc_common::{
6    errors::{Diagnostic, DiagnosticBuilder, Emitter, Handler, HANDLER},
7    GLOBALS,
8};
9use swc_ecma_ast::{Module, Script};
10use swc_ecma_visit::{Visit, VisitWith};
11use swc_parallel::join;
12
13/// A lint rule.
14///
15/// # Implementation notes
16///
17/// Must report error to [swc_common::HANDLER]
18#[auto_impl(Box, &mut)]
19pub trait Rule: Debug + Send + Sync {
20    fn lint_module(&mut self, program: &Module);
21    fn lint_script(&mut self, program: &Script);
22}
23
24trait LintNode<R: Rule>: Send + Sync {
25    fn lint(&self, rule: &mut R);
26}
27
28impl<R: Rule> LintNode<R> for Module {
29    #[inline]
30    fn lint(&self, rule: &mut R) {
31        rule.lint_module(self);
32    }
33}
34
35impl<R: Rule> LintNode<R> for Script {
36    #[inline]
37    fn lint(&self, rule: &mut R) {
38        rule.lint_script(self);
39    }
40}
41
42fn join_lint_rules<N: LintNode<R>, R: Rule>(rules: &mut [R], program: &N) {
43    let len = rules.len();
44    if len == 0 {
45        return;
46    }
47    if len == 1 {
48        program.lint(&mut rules[0]);
49        return;
50    }
51
52    let (ra, rb) = rules.split_at_mut(len / 2);
53
54    GLOBALS.with(|globals| {
55        HANDLER.with(|handler| {
56            join(
57                || {
58                    GLOBALS.set(globals, || {
59                        HANDLER.set(handler, || join_lint_rules(ra, program))
60                    })
61                },
62                || {
63                    GLOBALS.set(globals, || {
64                        HANDLER.set(handler, || join_lint_rules(rb, program))
65                    })
66                },
67            )
68        })
69    });
70}
71
72fn lint_rules<N: LintNode<R>, R: Rule>(rules: &mut Vec<R>, program: &N) {
73    if rules.is_empty() {
74        return;
75    }
76
77    if cfg!(target_arch = "wasm32") {
78        for rule in rules {
79            program.lint(rule);
80        }
81    } else {
82        let capturing = Capturing::default();
83
84        {
85            HANDLER.set(
86                &Handler::with_emitter(true, false, Box::new(capturing.clone())),
87                || {
88                    join_lint_rules(rules, program);
89                },
90            );
91
92            let mut errors = take(&mut *capturing.errors.lock());
93            errors.sort_by_key(|error| error.span.primary_span());
94
95            HANDLER.with(|handler| {
96                for error in errors {
97                    DiagnosticBuilder::new_diagnostic(handler, error).emit();
98                }
99            });
100        }
101    }
102}
103
104#[derive(Default, Clone)]
105struct Capturing {
106    errors: Arc<Mutex<Vec<Diagnostic>>>,
107}
108
109impl Emitter for Capturing {
110    fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
111        self.errors.lock().push((**db).clone());
112    }
113}
114
115/// This preserves the order of errors.
116impl<R> Rule for Vec<R>
117where
118    R: Rule,
119{
120    fn lint_module(&mut self, program: &Module) {
121        lint_rules(self, program)
122    }
123
124    fn lint_script(&mut self, program: &Script) {
125        lint_rules(self, program)
126    }
127}
128
129pub(crate) fn visitor_rule<V>(v: V) -> Box<dyn Rule>
130where
131    V: 'static + Send + Sync + Visit + Default + Debug,
132{
133    Box::new(VisitorRule(v))
134}
135
136#[derive(Debug)]
137struct VisitorRule<V>(V)
138where
139    V: Send + Sync + Visit;
140
141impl<V> Rule for VisitorRule<V>
142where
143    V: Send + Sync + Visit + Debug,
144{
145    fn lint_module(&mut self, program: &Module) {
146        program.visit_with(&mut self.0);
147    }
148
149    fn lint_script(&mut self, program: &Script) {
150        program.visit_with(&mut self.0);
151    }
152}