-
Notifications
You must be signed in to change notification settings - Fork 659
refactor(rome_analyze): refactor how Visitors, Queryables and QueryMatches are related #4063
Conversation
✅ Deploy Preview for docs-rometools canceled.
|
In the yield example, the visitor will be called for all nodes? struct MissingYields(Vec<JsFunctionDeclaration>);
impl RuleService for MissingYields {
type Query = Ast<JsFunctionDeclaration | JsYieldExpression>; // just an example
fn visit(...) {
match event {
WalkEvent::Enter(node) => {
...
}
WalkEvent::Leave(node) => {
...
}
}
}
fn finish(bag: &mut ServiceBag) {
bag.insert(MissingYields(...));
.}
} The main difference is that we introduce a hook to services. We can have later: FindReactCallsService, FunctionsWithJSX etc... type Query = SemanticServices To actually support requesting any services: type Query = Services<SemanticModel, FindReactCallsService> What do you think? |
For now this is mostly implemented as a simple extension to the existing Visitor API that allows individual rules to run custom specialized visitors on the syntax tree in parallel to existing visitors like the semantic model builder. Being able to specify query / filters on visitors seems interesting but would probably require adding another layer of indirection between the syntax tree preorder iterator and the visitors. |
!bench_analyzer |
Analyzer Benchmark Results
|
!bench_analyzer |
@leops it seems that the action didn't finish |
!bench_analyzer |
Analyzer Benchmark Results
|
let query_type = params.query.type_id(); | ||
let Some(rules) = phase.type_rules.get(&query_type) else { return }; | ||
|
||
let rules = match rules { | ||
TypeRules::SyntaxRules { rules } => { | ||
let node = params.query.downcast_ref::<SyntaxNode<L>>().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you see a way that would avoid the need for the runtime assertion that rules
and the query
type (downcast) match?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this seems to be an infrastructure change only, this change unlocks new features that are not documented in this PR.
Other than showing an example in this PR, I think we should update the contribution guidelines that show how to create a rule with its own visitor system.
.entry(phase) | ||
.or_default() | ||
.push(Box::new(visitor)); | ||
pub fn add_visitor( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's time to document this public API. We started using it more and more... I suppose some description is due.
crates/rome_analyze/src/query.rs
Outdated
fn build_visitor( | ||
analyzer: &mut impl AddVisitor<Self::Language>, | ||
root: &<Self::Language as Language>::Root, | ||
); | ||
|
||
fn key() -> QueryKey<Self::Language> { | ||
QueryKey::TypeId(TypeId::of::<Self::Input>()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation?
Syntax(SyntaxNode<L>), | ||
SemanticModel(TextRange), | ||
ControlFlowGraph(ControlFlowGraph<L>, TextRange), | ||
pub trait AddVisitor<L: Language> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation?
ast_rules: Vec<SyntaxKindRules<L>>, | ||
control_flow: Vec<RegistryRule<L>>, | ||
semantic_model: Vec<RegistryRule<L>>, | ||
type_rules: FxHashMap<TypeId, TypeRules<L>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation?
crates/rome_analyze/src/registry.rs
Outdated
.type_rules | ||
.entry(key) | ||
.or_insert_with(|| TypeRules::TypeRules { rules: Vec::new() }) | ||
else { unreachable!() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we insert a small description inside unreachable!()
in case it is actually reached? The message should hint where to fix the issue.
}; | ||
use rome_rowan::{Language, SyntaxKindSet, TextRange}; | ||
|
||
use crate::{registry::Phase, services::FromServices, Phases, ServiceBag, Visitor}; | ||
|
||
/// Trait implemented for all types, for example lint rules can query them to emit diagnostics or code actions. | ||
pub trait Queryable: Sized { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this trait should have more documentation now
Summary
This PR modifies how various types related to the
Queryable
trait are injected into the analyzer infrastructure. This includes:Queryable
types to dynamically register theVisitor
instances that emits them to the analyzerQueryMatch
enum withTypeId
andBox<dyn Any>
to allow any arbitrary type to be emitted as a visitor eventThe main goal of this refactor is to allow individual rules to declare custom syntax tree visitors to emit the event they rely upon as an optimization opportunity instead of having to traverse the syntax tree in their
run
method. For instance theuseYield
rule could be implemented like this:Code
Test Plan
This is an infrastructure-only change, the code should continue to build and existing tests should continue to pass correctly.
Documentation