1use std::{fmt::Debug, sync::Arc};
2
3use auto_impl::auto_impl;
4use par_iter::prelude::*;
5use parking_lot::Mutex;
6use serde::Serialize;
7use swc_common::{
8 errors::{Diagnostic, DiagnosticBuilder, Emitter, Handler, HANDLER},
9 Spanned,
10};
11use swc_css_ast::Stylesheet;
12use swc_css_visit::{Visit, VisitWith};
13
14use super::config::{LintRuleReaction, RuleConfig};
15
16#[auto_impl(Box, &mut)]
22pub trait LintRule: Debug + Send + Sync {
23 fn lint_stylesheet(&mut self, stylesheet: &Stylesheet);
24}
25
26impl<R> LintRule for Vec<R>
28where
29 R: LintRule,
30{
31 fn lint_stylesheet(&mut self, stylesheet: &Stylesheet) {
32 if cfg!(target_arch = "wasm32") {
33 for rule in self {
34 rule.lint_stylesheet(stylesheet);
35 }
36 } else {
37 let errors = self
38 .par_iter_mut()
39 .flat_map(|rule| {
40 let emitter = Capturing::default();
41 {
42 let handler = Handler::with_emitter(true, false, Box::new(emitter.clone()));
43 HANDLER.set(&handler, || {
44 rule.lint_stylesheet(stylesheet);
45 });
46 }
47
48 Arc::try_unwrap(emitter.errors).unwrap().into_inner()
49 })
50 .collect::<Vec<_>>();
51
52 HANDLER.with(|handler| {
53 for error in errors {
54 DiagnosticBuilder::new_diagnostic(handler, error).emit();
55 }
56 });
57 }
58 }
59}
60
61#[derive(Default, Clone)]
62struct Capturing {
63 errors: Arc<Mutex<Vec<Diagnostic>>>,
64}
65
66impl Emitter for Capturing {
67 fn emit(&mut self, db: &mut DiagnosticBuilder<'_>) {
68 self.errors.lock().push(db.take());
69 }
70}
71
72pub(crate) fn visitor_rule<V>(reaction: LintRuleReaction, v: V) -> Box<dyn LintRule>
73where
74 V: 'static + Send + Sync + Visit + Default + Debug,
75{
76 Box::new(VisitorRule(v, reaction))
77}
78
79#[derive(Debug)]
80struct VisitorRule<V>(V, LintRuleReaction)
81where
82 V: Send + Sync + Visit;
83
84impl<V> LintRule for VisitorRule<V>
85where
86 V: Send + Sync + Visit + Debug,
87{
88 fn lint_stylesheet(&mut self, stylesheet: &Stylesheet) {
89 if !matches!(self.1, LintRuleReaction::Off) {
90 stylesheet.visit_with(&mut self.0);
91 }
92 }
93}
94
95#[derive(Debug, Clone, Default)]
96pub struct LintRuleContext<C>
97where
98 C: Debug + Clone + Serialize + Default,
99{
100 reaction: LintRuleReaction,
101 config: C,
102}
103
104impl<C> LintRuleContext<C>
105where
106 C: Debug + Clone + Serialize + Default,
107{
108 pub(crate) fn report<N, S>(&self, ast_node: N, message: S)
109 where
110 N: Spanned,
111 S: AsRef<str>,
112 {
113 HANDLER.with(|handler| match self.reaction {
114 LintRuleReaction::Error => handler
115 .struct_span_err(ast_node.span(), message.as_ref())
116 .emit(),
117 LintRuleReaction::Warning => handler
118 .struct_span_warn(ast_node.span(), message.as_ref())
119 .emit(),
120 _ => {}
121 });
122 }
123
124 #[inline]
125 pub(crate) fn config(&self) -> &C {
126 &self.config
127 }
128
129 #[inline]
130 pub(crate) fn reaction(&self) -> LintRuleReaction {
131 self.reaction
132 }
133}
134
135impl<C> From<&RuleConfig<C>> for LintRuleContext<C>
136where
137 C: Debug + Clone + Serialize + Default,
138{
139 fn from(config: &RuleConfig<C>) -> Self {
140 Self {
141 reaction: config.get_rule_reaction(),
142 config: config.get_rule_config().clone(),
143 }
144 }
145}