1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
use crate::{
matcher::{MatchQueryParams, Query},
registry::{NodeLanguage, Phases},
AnalyzerOptions, LanguageRoot, QueryMatch, QueryMatcher, ServiceBag, SignalEntry,
SuppressionCommentEmitter,
};
use biome_rowan::{AstNode, Language, SyntaxNode, TextRange, WalkEvent};
use std::collections::BinaryHeap;
/// Mutable context objects shared by all visitors
pub struct VisitorContext<'phase, 'query, L: Language> {
pub phase: Phases,
pub root: &'phase LanguageRoot<L>,
pub services: &'phase ServiceBag,
pub range: Option<TextRange>,
pub(crate) query_matcher: &'query mut dyn QueryMatcher<L>,
pub(crate) signal_queue: &'query mut BinaryHeap<SignalEntry<'phase, L>>,
pub apply_suppression_comment: SuppressionCommentEmitter<L>,
pub options: &'phase AnalyzerOptions,
}
impl<'phase, 'query, L: Language> VisitorContext<'phase, 'query, L> {
pub fn match_query<T: QueryMatch>(&mut self, query: T) {
self.query_matcher.match_query(MatchQueryParams {
phase: self.phase,
root: self.root,
query: Query::new(query),
services: self.services,
signal_queue: self.signal_queue,
apply_suppression_comment: self.apply_suppression_comment,
options: self.options,
})
}
}
/// Mutable context objects provided to the finish hook of visitors
pub struct VisitorFinishContext<'a, L: Language> {
pub root: &'a LanguageRoot<L>,
pub services: &'a mut ServiceBag,
}
/// Visitors are the main building blocks of the analyzer: they receive syntax
/// [WalkEvent]s, process these events to build secondary data structures from
/// the syntax tree, and emit rule query matches through the [crate::RuleRegistry]
pub trait Visitor {
type Language: Language;
fn visit(
&mut self,
event: &WalkEvent<SyntaxNode<Self::Language>>,
ctx: VisitorContext<Self::Language>,
);
fn finish(self: Box<Self>, ctx: VisitorFinishContext<Self::Language>) {
let _ = ctx;
}
}
/// A node visitor is a special kind of visitor that does not have a persistent
/// state for the entire run of the analyzer. Instead these visitors are
/// transient, they get instantiated when the traversal enters the
/// corresponding node type and destroyed when the corresponding node exits
///
/// Due to these specificities node visitors do not implement [Visitor]
/// directly, instead one or more of these must the merged into a single
/// visitor type using the [crate::merge_node_visitors] macro
pub trait NodeVisitor<V>: Sized {
type Node: AstNode;
fn enter(
node: Self::Node,
ctx: &mut VisitorContext<NodeLanguage<Self::Node>>,
stack: &mut V,
) -> Self;
fn exit(
self,
node: Self::Node,
ctx: &mut VisitorContext<NodeLanguage<Self::Node>>,
stack: &mut V,
);
}
/// Creates a single struct implementing [Visitor] over a collection of type
/// implementing the [NodeVisitor] helper trait. Unlike the global [Visitor],
/// node visitors are transient: they get instantiated when the traversal
/// enters the corresponding node and destroyed when the node is exited. They
/// are intended as a building blocks for creating and managing the state of
/// complex visitors by allowing the implementation to be split over multiple
/// smaller components.
///
/// # Example
///
/// ```ignore
/// struct BinaryVisitor;
///
/// impl NodeVisitor for BinaryVisitor {
/// type Node = BinaryExpression;
/// }
///
/// struct UnaryVisitor;
///
/// impl NodeVisitor for UnaryVisitor {
/// type Node = UnaryExpression;
/// }
///
/// merge_node_visitors! {
/// // This declares a new `ExpressionVisitor` struct that implements
/// // `Visitor` and manages instances of `BinaryVisitor` and
/// // `UnaryVisitor`
/// pub(crate) ExpressionVisitor {
/// binary: BinaryVisitor,
/// unary: UnaryVisitor,
/// }
/// }
/// ```
#[macro_export]
macro_rules! merge_node_visitors {
( $vis:vis $name:ident { $( $id:ident: $visitor:ty, )+ } ) => {
$vis struct $name {
stack: Vec<(::std::any::TypeId, usize)>,
$( $vis $id: Vec<(usize, $visitor)>, )*
}
impl $name {
$vis fn new() -> Self {
Self {
stack: Vec::new(),
$( $id: Vec::new(), )*
}
}
}
impl $crate::Visitor for $name {
type Language = <( $( <$visitor as $crate::NodeVisitor<$name>>::Node, )* ) as ::biome_rowan::macros::UnionLanguage>::Language;
fn visit(
&mut self,
event: &::biome_rowan::WalkEvent<::biome_rowan::SyntaxNode<Self::Language>>,
mut ctx: $crate::VisitorContext<Self::Language>,
) {
match event {
::biome_rowan::WalkEvent::Enter(node) => {
let kind = node.kind();
$(
if <<$visitor as $crate::NodeVisitor<$name>>::Node as ::biome_rowan::AstNode>::can_cast(kind) {
let node = <<$visitor as $crate::NodeVisitor<$name>>::Node as ::biome_rowan::AstNode>::unwrap_cast(node.clone());
let state = <$visitor as $crate::NodeVisitor<$name>>::enter(node, &mut ctx, self);
let stack_index = self.stack.len();
let ty_index = self.$id.len();
self.$id.push((stack_index, state));
self.stack.push((::std::any::TypeId::of::<$visitor>(), ty_index));
return;
}
)*
}
::biome_rowan::WalkEvent::Leave(node) => {
let kind = node.kind();
$(
if <<$visitor as $crate::NodeVisitor<$name>>::Node as ::biome_rowan::AstNode>::can_cast(kind) {
self.stack.pop().unwrap();
let (_, state) = self.$id.pop().unwrap();
let node = <<$visitor as $crate::NodeVisitor<$name>>::Node as ::biome_rowan::AstNode>::unwrap_cast(node.clone());
<$visitor as $crate::NodeVisitor<$name>>::exit(state, node, &mut ctx, self);
return;
}
)*
}
}
}
}
};
}