1use crate::{
2 context::RuleContext,
3 matcher::{GroupKey, MatchQueryParams},
4 query::{QueryKey, Queryable},
5 signals::RuleSignal,
6 AddVisitor, AnalysisFilter, GroupCategory, QueryMatcher, Rule, RuleGroup, RuleKey,
7 RuleMetadata, ServiceBag, SignalEntry, Visitor,
8};
9use biome_diagnostics::Error;
10use biome_rowan::{AstNode, Language, RawSyntaxKind, SyntaxKind, SyntaxNode};
11use rustc_hash::{FxHashMap, FxHashSet};
12use std::{
13 any::TypeId,
14 borrow,
15 collections::{BTreeMap, BTreeSet},
16};
17
18#[repr(usize)]
20#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub enum Phases {
22 Syntax = 0,
23 Semantic = 1,
24}
25
26pub trait Phase {
29 fn phase() -> Phases;
30}
31
32impl Phase for () {
34 fn phase() -> Phases {
35 Phases::Syntax
36 }
37}
38
39pub trait RegistryVisitor<L: Language> {
40 fn record_category<C: GroupCategory<Language = L>>(&mut self) {
42 C::record_groups(self);
43 }
44
45 fn record_group<G: RuleGroup<Language = L>>(&mut self) {
47 G::record_rules(self);
48 }
49
50 fn record_rule<R>(&mut self)
52 where
53 R: Rule + 'static,
54 R::Query: Queryable<Language = L>,
55 <R::Query as Queryable>::Output: Clone;
56}
57
58#[derive(Debug, Default)]
61pub struct MetadataRegistry {
62 inner: BTreeSet<MetadataKey>,
63}
64
65impl MetadataRegistry {
66 pub fn find_group(&self, group: &str) -> Option<GroupKey> {
68 let key = self.inner.get(group)?;
69 Some(key.into_group_key())
70 }
71
72 pub fn find_rule(&self, group: &str, rule: &str) -> Option<RuleKey> {
74 let key = self.inner.get(&(group, rule))?;
75 Some(key.into_rule_key())
76 }
77
78 pub(crate) fn insert_rule(&mut self, group: &'static str, rule: &'static str) {
79 self.inner.insert(MetadataKey {
80 inner: (group, rule),
81 });
82 }
83}
84
85impl<L: Language> RegistryVisitor<L> for MetadataRegistry {
86 fn record_rule<R>(&mut self)
87 where
88 R: Rule + 'static,
89 R::Query: Queryable<Language = L>,
90 <R::Query as Queryable>::Output: Clone,
91 {
92 self.insert_rule(<R::Group as RuleGroup>::NAME, R::METADATA.name);
93 }
94}
95
96pub struct RuleRegistry<L: Language> {
104 phase_rules: [PhaseRules<L>; 2],
106}
107
108impl<L: Language + Default> RuleRegistry<L> {
109 pub fn builder<'a>(
110 filter: &'a AnalysisFilter<'a>,
111 root: &'a L::Root,
112 ) -> RuleRegistryBuilder<'a, L> {
113 RuleRegistryBuilder {
114 filter,
115 root,
116 registry: RuleRegistry {
117 phase_rules: Default::default(),
118 },
119 visitors: BTreeMap::default(),
120 services: ServiceBag::default(),
121 diagnostics: Vec::new(),
122 }
123 }
124}
125
126#[derive(Default)]
128struct PhaseRules<L: Language> {
129 type_rules: FxHashMap<TypeId, TypeRules<L>>,
131 rule_states: Vec<RuleState<L>>,
133}
134
135enum TypeRules<L: Language> {
136 SyntaxRules { rules: Vec<SyntaxKindRules<L>> },
137 TypeRules { rules: Vec<RegistryRule<L>> },
138}
139
140pub struct RuleRegistryBuilder<'a, L: Language> {
141 filter: &'a AnalysisFilter<'a>,
142 root: &'a L::Root,
143 registry: RuleRegistry<L>,
145 visitors: BTreeMap<(Phases, TypeId), Box<dyn Visitor<Language = L>>>,
147 services: ServiceBag,
149 diagnostics: Vec<Error>,
150}
151
152impl<L: Language + Default + 'static> RegistryVisitor<L> for RuleRegistryBuilder<'_, L> {
153 fn record_category<C: GroupCategory<Language = L>>(&mut self) {
154 if self.filter.match_category::<C>() {
155 C::record_groups(self);
156 }
157 }
158
159 fn record_group<G: RuleGroup<Language = L>>(&mut self) {
160 if self.filter.match_group::<G>() {
161 G::record_rules(self);
162 }
163 }
164
165 fn record_rule<R>(&mut self)
167 where
168 R: Rule + 'static,
169 <R as Rule>::Options: Default,
170 R::Query: Queryable<Language = L>,
171 <R::Query as Queryable>::Output: Clone,
172 {
173 if !self.filter.match_rule::<R>() {
174 return;
175 }
176
177 let phase = R::phase() as usize;
178 let phase = &mut self.registry.phase_rules[phase];
179
180 let rule = RegistryRule::new::<R>(phase.rule_states.len());
181
182 match <R::Query as Queryable>::key() {
183 QueryKey::Syntax(key) => {
184 let TypeRules::SyntaxRules { rules } = phase
185 .type_rules
186 .entry(TypeId::of::<SyntaxNode<L>>())
187 .or_insert_with(|| TypeRules::SyntaxRules { rules: Vec::new() })
188 else {
189 unreachable!("the SyntaxNode type has already been registered as a TypeRules instead of a SyntaxRules, this is generally caused by an implementation of `Queryable::key` returning a `QueryKey::TypeId` with the type ID of `SyntaxNode`")
190 };
191
192 for kind in key.iter() {
194 let RawSyntaxKind(index) = kind.to_raw();
197 let index = usize::from(index);
198
199 if rules.len() <= index {
202 rules.resize_with(index + 1, SyntaxKindRules::new);
203 }
204
205 let node = &mut rules[index];
208 node.rules.push(rule);
209 }
210 }
211 QueryKey::TypeId(key) => {
212 let TypeRules::TypeRules { rules } = phase
213 .type_rules
214 .entry(key)
215 .or_insert_with(|| TypeRules::TypeRules { rules: Vec::new() })
216 else {
217 unreachable!("the query type has already been registered as a SyntaxRules instead of a TypeRules, this is generally ca used by an implementation of `Queryable::key` returning a `QueryKey::TypeId` with the type ID of `SyntaxNode`")
218 };
219
220 rules.push(rule);
221 }
222 }
223
224 phase.rule_states.push(RuleState::default());
225
226 <R::Query as Queryable>::build_visitor(&mut self.visitors, self.root);
227 }
228}
229
230impl<L: Language> AddVisitor<L> for BTreeMap<(Phases, TypeId), Box<dyn Visitor<Language = L>>> {
231 fn add_visitor<F, V>(&mut self, phase: Phases, visitor: F)
232 where
233 F: FnOnce() -> V,
234 V: Visitor<Language = L> + 'static,
235 {
236 self.entry((phase, TypeId::of::<V>()))
237 .or_insert_with(move || Box::new((visitor)()));
238 }
239}
240
241type BuilderResult<L> = (
242 RuleRegistry<L>,
243 ServiceBag,
244 Vec<Error>,
245 BTreeMap<(Phases, TypeId), Box<dyn Visitor<Language = L>>>,
246);
247
248impl<L: Language> RuleRegistryBuilder<'_, L> {
249 pub fn build(self) -> BuilderResult<L> {
250 (
251 self.registry,
252 self.services,
253 self.diagnostics,
254 self.visitors,
255 )
256 }
257}
258
259impl<L: Language + 'static> QueryMatcher<L> for RuleRegistry<L> {
260 fn match_query(&mut self, mut params: MatchQueryParams<L>) {
261 let phase = &mut self.phase_rules[params.phase as usize];
262
263 let query_type = params.query.type_id();
264 let Some(rules) = phase.type_rules.get(&query_type) else {
265 return;
266 };
267
268 let rules = match rules {
269 TypeRules::SyntaxRules { rules } => {
270 let node = params.query.downcast_ref::<SyntaxNode<L>>().unwrap();
271
272 let RawSyntaxKind(kind) = node.kind().to_raw();
275 let kind = usize::from(kind);
276
277 match rules.get(kind) {
279 Some(entry) => &entry.rules,
280 None => return,
281 }
282 }
283 TypeRules::TypeRules { rules } => rules,
284 };
285
286 for rule in rules {
288 let state = &mut phase.rule_states[rule.state_index];
289 let _ = (rule.run)(&mut params, state);
291 }
292 }
293}
294
295struct SyntaxKindRules<L: Language> {
297 rules: Vec<RegistryRule<L>>,
298}
299
300impl<L: Language> SyntaxKindRules<L> {
301 fn new() -> Self {
302 Self { rules: Vec::new() }
303 }
304}
305
306pub(crate) type RuleLanguage<R> = QueryLanguage<<R as Rule>::Query>;
307pub(crate) type QueryLanguage<N> = <N as Queryable>::Language;
308pub(crate) type NodeLanguage<N> = <N as AstNode>::Language;
309
310pub(crate) type RuleRoot<R> = LanguageRoot<RuleLanguage<R>>;
311pub type LanguageRoot<L> = <L as Language>::Root;
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
315pub struct MetadataKey {
316 inner: (&'static str, &'static str),
317}
318
319impl MetadataKey {
320 fn into_group_key(self) -> GroupKey {
321 let (group, _) = self.inner;
322 GroupKey::new(group)
323 }
324
325 fn into_rule_key(self) -> RuleKey {
326 let (group, rule) = self.inner;
327 RuleKey::new(group, rule)
328 }
329}
330
331impl<'a> borrow::Borrow<(&'a str, &'a str)> for MetadataKey {
332 fn borrow(&self) -> &(&'a str, &'a str) {
333 &self.inner
334 }
335}
336
337impl borrow::Borrow<str> for MetadataKey {
338 fn borrow(&self) -> &str {
339 self.inner.0
340 }
341}
342
343pub struct RegistryRuleMetadata {
345 pub group: &'static str,
346 pub rule: RuleMetadata,
347}
348
349impl RegistryRuleMetadata {
350 pub fn to_rule_key(&self) -> RuleKey {
351 RuleKey::new(self.group, self.rule.name)
352 }
353}
354
355#[derive(Copy, Clone)]
357pub struct RegistryRule<L: Language> {
358 run: RuleExecutor<L>,
359 state_index: usize,
360}
361
362#[derive(Default)]
364struct RuleState<L: Language> {
365 suppressions: RuleSuppressions<L>,
366}
367
368#[derive(Default)]
370pub struct RuleSuppressions<L: Language> {
371 inner: FxHashSet<SyntaxNode<L>>,
372}
373
374impl<L: Language> RuleSuppressions<L> {
375 pub fn suppress_node(&mut self, node: SyntaxNode<L>) {
377 self.inner.insert(node);
378 }
379}
380
381type RuleExecutor<L> = fn(&mut MatchQueryParams<L>, &mut RuleState<L>) -> Result<(), Error>;
383
384impl<L: Language + Default> RegistryRule<L> {
385 fn new<R>(state_index: usize) -> Self
386 where
387 R: Rule + 'static,
388 <R as Rule>::Options: Default,
389 R::Query: Queryable<Language = L> + 'static,
390 <R::Query as Queryable>::Output: Clone,
391 {
392 fn run<R>(
394 params: &mut MatchQueryParams<RuleLanguage<R>>,
395 state: &mut RuleState<RuleLanguage<R>>,
396 ) -> Result<(), Error>
397 where
398 R: Rule + 'static,
399 R::Query: 'static,
400 <R::Query as Queryable>::Output: Clone,
401 <R as Rule>::Options: Default,
402 {
403 if let Some(node) = params.query.downcast_ref::<SyntaxNode<RuleLanguage<R>>>() {
404 if state.suppressions.inner.contains(node) {
405 return Ok(());
406 }
407 }
408
409 let query_result = params.query.downcast_ref().unwrap();
412 let query_result = <R::Query as Queryable>::unwrap_match(params.services, query_result);
413 let globals = params.options.globals();
414 let preferred_quote = params.options.preferred_quote();
415 let options = params.options.rule_options::<R>().unwrap_or_default();
416 let ctx = match RuleContext::new(
417 &query_result,
418 params.root,
419 params.services,
420 &globals,
421 ¶ms.options.file_path,
422 &options,
423 preferred_quote,
424 ) {
425 Ok(ctx) => ctx,
426 Err(error) => return Err(error),
427 };
428
429 for result in R::run(&ctx) {
430 let text_range =
431 R::text_range(&ctx, &result).unwrap_or_else(|| params.query.text_range());
432
433 R::suppressed_nodes(&ctx, &result, &mut state.suppressions);
434
435 let signal = Box::new(RuleSignal::<R>::new(
436 params.root,
437 query_result.clone(),
438 result,
439 params.services,
440 params.apply_suppression_comment,
441 params.options,
442 ));
443
444 params.signal_queue.push(SignalEntry {
445 signal,
446 rule: RuleKey::rule::<R>(),
447 text_range,
448 });
449 }
450
451 Ok(())
452 }
453
454 Self {
455 run: run::<R>,
456 state_index,
457 }
458 }
459}