Open
Description
What is the reason for that particular design decision versus providing a more general Visitor
implementation?
Two options for a generalised Visitor trait come to mind:
- expose pre + post trait method variants for every AST node type, or
- expose only two trait methods (
pre_visit
+post_visit
) with signatures likefn pre_visit(&mut self, node: &AstNode) -> ControlFlow<Self::Break>
- whereAstNode
is an enum with a wrapper variant for every AST node type found insrc/ast/mod.rs
and can bematch
ed against.
Would the maintainers be interested in a PR that implements one of the above two approaches?
My preference would be for option 2 because it would not break the trait when node types are added/removed.
Suggested approach:
- Define a new
RawVisitor
trait (andRawVisitorMut
trait) like this:
pub trait RawVisitor {
type Break;
fn pre_visit(&mut self, node: &AstNode) -> ControlFlow<Self::Break>;
fn post_visit(&mut self, node: &AstNode) -> ControlFlow<Self::Break>;
}
- Define an adapter type (
RawVisitorAdapter
?) that accepts aV: Visitor
generic argument and implementsRawVisitor
&RawVisitorMut
, which calls the appropriate method onV
(or none at all)
struct RawVisitorAdapter<V: Visitor>(v);
impl<V: Visitor> RawVisitor for RawVisitorAdapter<V> {
type Break = V::Break;
fn pre_visit(&mut self, node: &AstNode) -> ControlFlow<Self::Break> {
match node {
AstNode(Statement) => self.0.pre_visit_statement(...),
// etc
}
}
fn post_visit(&mut self, node: &AstNode) -> ControlFlow<Self::Break>;
}
- Change the
Visit
derivation macros to generate code in terms ofRawVisitor
&RawVisitorMut
instead ofVisitor
, like this:
pub trait Visit {
fn visit_raw<V: RawVisitor>(&self, visitor: &mut V) -> ControlFlow<V::Break>;
// This has an identical signature to the existing trait, but has a default implementation
fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
self.visit_raw(RawVisitorAdapter::new(visitor))
}
}
Metadata
Metadata
Assignees
Labels
No labels