This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 659
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(rome_analyze): refactor how Visitors, Queryables and QueryMa…
…tches are related (#4063) * refactor(rome_analyze): allow queryables to register their own visitor * replace the `QueryMatch` enum with `Box<dyn Any>` * store visitors in a BTreeMap to register them in a stable order * fix CI checks * move calculatiung the text range of query matches to lazy evaluation * introduce a `Query` type to enforce the correct downcasting of query matches * add documentation
- Loading branch information
Showing
16 changed files
with
496 additions
and
266 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,58 @@ | ||
use rome_control_flow::ControlFlowGraph; | ||
use rome_rowan::{AstNode, Language, SyntaxKindSet, SyntaxNode, TextRange}; | ||
use std::any::TypeId; | ||
|
||
use crate::{ | ||
registry::{NodeLanguage, Phase}, | ||
services::FromServices, | ||
ServiceBag, | ||
}; | ||
use rome_rowan::{Language, SyntaxKindSet, TextRange}; | ||
|
||
/// Trait implemented for all types, for example lint rules can query them to emit diagnostics or code actions. | ||
use crate::{registry::Phase, services::FromServices, Phases, ServiceBag, Visitor}; | ||
|
||
/// Trait implemented for types that lint rules can query in order to emit diagnostics or code actions. | ||
pub trait Queryable: Sized { | ||
type Input: QueryMatch; | ||
type Output; | ||
|
||
type Language: Language; | ||
type Services: FromServices + Phase; | ||
|
||
/// Statically declares which [QueryMatch] variant is matched by this | ||
/// [Queryable] type. For instance the [Ast] queryable matches on | ||
/// [QueryMatch::Syntax], so its key is defined as [QueryKey::Syntax] | ||
const KEY: QueryKey<Self::Language>; | ||
/// Registers one or more [Visitor] that will emit `Self::Input` query | ||
/// matches during the analyzer run | ||
fn build_visitor( | ||
analyzer: &mut impl AddVisitor<Self::Language>, | ||
root: &<Self::Language as Language>::Root, | ||
); | ||
|
||
/// Returns the type of query matches this [Queryable] expects as inputs | ||
/// | ||
/// Unless your custom queryable needs to match on a specific | ||
/// [SyntaxKindSet], you should not override the default implementation of | ||
/// this method | ||
fn key() -> QueryKey<Self::Language> { | ||
QueryKey::TypeId(TypeId::of::<Self::Input>()) | ||
} | ||
|
||
/// Unwrap an instance of `Self` from a [QueryMatch]. | ||
/// | ||
/// ## Panics | ||
/// | ||
/// If the [QueryMatch] variant of `query` doesn't match `Self::KEY` | ||
fn unwrap_match(services: &ServiceBag, query: &QueryMatch<Self::Language>) -> Self::Output; | ||
fn unwrap_match(services: &ServiceBag, query: &Self::Input) -> Self::Output; | ||
} | ||
|
||
/// Enumerate all the types of [Queryable] analyzer visitors may emit | ||
#[derive(Clone, Debug)] | ||
pub enum QueryMatch<L: Language> { | ||
Syntax(SyntaxNode<L>), | ||
SemanticModel(TextRange), | ||
ControlFlowGraph(ControlFlowGraph<L>, TextRange), | ||
/// This trait is implemented on all types that supports the registration of [Visitor] | ||
pub trait AddVisitor<L: Language> { | ||
/// Registers a [Visitor] for a given `phase` | ||
fn add_visitor<F, V>(&mut self, phase: Phases, visitor: F) | ||
where | ||
F: FnOnce() -> V, | ||
V: Visitor<Language = L> + 'static; | ||
} | ||
|
||
impl<L: Language> QueryMatch<L> { | ||
pub fn text_range(&self) -> TextRange { | ||
match self { | ||
QueryMatch::Syntax(node) => node.text_trimmed_range(), | ||
QueryMatch::SemanticModel(range) | QueryMatch::ControlFlowGraph(_, range) => *range, | ||
} | ||
} | ||
/// Marker trait implemented for all the types analyzer visitors may emit | ||
pub trait QueryMatch: 'static { | ||
fn text_range(&self) -> TextRange; | ||
} | ||
|
||
/// Mirrors the variants of [QueryMatch] to statically compute which queries a | ||
/// given [Queryable] type can match | ||
/// Represents which type a given [Queryable] type can match, either a specific | ||
/// subset of syntax node kinds or any generic type | ||
pub enum QueryKey<L: Language> { | ||
Syntax(SyntaxKindSet<L>), | ||
ControlFlowGraph, | ||
SemanticModel, | ||
} | ||
|
||
/// Query type usable by lint rules to match on specific [AstNode] types | ||
#[derive(Clone)] | ||
pub struct Ast<N>(pub N); | ||
|
||
impl<N> Queryable for Ast<N> | ||
where | ||
N: AstNode + 'static, | ||
{ | ||
type Output = N; | ||
type Language = NodeLanguage<N>; | ||
type Services = (); | ||
|
||
/// Match on [QueryMatch::Syntax] if the kind of the syntax node matches | ||
/// the kind set of `N` | ||
const KEY: QueryKey<Self::Language> = QueryKey::Syntax(N::KIND_SET); | ||
|
||
fn unwrap_match(_: &ServiceBag, query: &QueryMatch<Self::Language>) -> Self::Output { | ||
match query { | ||
QueryMatch::Syntax(node) => N::unwrap_cast(node.clone()), | ||
_ => panic!("tried to unwrap unsupported QueryMatch kind, expected Syntax"), | ||
} | ||
} | ||
} | ||
|
||
impl<L: Language> Queryable for ControlFlowGraph<L> { | ||
type Output = Self; | ||
type Language = L; | ||
type Services = (); | ||
|
||
const KEY: QueryKey<Self::Language> = QueryKey::ControlFlowGraph; | ||
|
||
fn unwrap_match(_: &ServiceBag, query: &QueryMatch<Self::Language>) -> Self::Output { | ||
match query { | ||
QueryMatch::ControlFlowGraph(cfg, _) => cfg.clone(), | ||
_ => panic!("tried to unwrap unsupported QueryMatch kind, expected Syntax"), | ||
} | ||
} | ||
TypeId(TypeId), | ||
} |
Oops, something went wrong.