You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by lu...@apache.org on 2026/10/08 23:29:28 UTC

(datafusion-sqlparser-rs) 03/14: Fire node hooks for Ident and document their order around typed hooks

This is an automated email from the ASF dual-hosted git repository.

LucaCappelletti94 pushed a commit to branch span-alter-statements
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit dcf773627e6790c8a791ee87e48ceaf6e6838665
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Thu Sep 24 11:35:31 2026 +0200

    Fire node hooks for Ident and document their order around typed hooks
---
 src/ast/mod.rs     |  5 +++-
 src/ast/visitor.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index de925be2..a58c9d65 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -402,8 +402,11 @@ impl fmt::Display for Ident {
 #[cfg(feature = "visitor")]
 impl Visit for Ident {
     fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+        let node = NodeRef::__new(self, core::any::type_name::<Self>(), None, None, Some(self));
+        visitor.pre_visit_node(node)?;
         visitor.pre_visit_ident(self)?;
-        visitor.post_visit_ident(self)
+        visitor.post_visit_ident(self)?;
+        visitor.post_visit_node(node)
     }
 }
 
diff --git a/src/ast/visitor.rs b/src/ast/visitor.rs
index 55bf5ba0..e47d880f 100644
--- a/src/ast/visitor.rs
+++ b/src/ast/visitor.rs
@@ -426,8 +426,10 @@ pub trait Visitor {
         ControlFlow::Continue(())
     }
 
-    /// Invoked for every AST node before visiting its children, and before
-    /// the node's typed hook such as [`Visitor::pre_visit_expr`].
+    /// Invoked for every AST node before visiting its children. It runs before
+    /// hooks declared on the node's type, such as [`Visitor::pre_visit_expr`],
+    /// and after hooks declared on the field holding it, such as
+    /// [`Visitor::pre_visit_relation`].
     ///
     /// # Example
     /// ```
@@ -458,8 +460,10 @@ pub trait Visitor {
         ControlFlow::Continue(())
     }
 
-    /// Invoked for every AST node after visiting its children, and after the
-    /// node's typed hook such as [`Visitor::post_visit_expr`].
+    /// Invoked for every AST node after visiting its children. It runs after
+    /// hooks declared on the node's type, such as [`Visitor::post_visit_expr`],
+    /// and before hooks declared on the field holding it, such as
+    /// [`Visitor::post_visit_relation`].
     fn post_visit_node(&mut self, _node: NodeRef<'_>) -> ControlFlow<Self::Break> {
         ControlFlow::Continue(())
     }
@@ -1435,6 +1439,66 @@ mod tests {
         assert_eq!(visitor.relations, vec!["db1.v", "t"]);
     }
 
+    #[derive(Default)]
+    struct HookOrderVisitor(Vec<String>);
+
+    impl HookOrderVisitor {
+        fn node(&mut self, event: &str, node: NodeRef<'_>) -> ControlFlow<()> {
+            let name = node.type_name().rsplit("::").next().unwrap();
+            if matches!(name, "Expr" | "ObjectName") {
+                self.0.push(format!("{event} node {name}"));
+            }
+            ControlFlow::Continue(())
+        }
+
+        fn typed(&mut self, event: &str) -> ControlFlow<()> {
+            self.0.push(event.to_string());
+            ControlFlow::Continue(())
+        }
+    }
+
+    impl Visitor for HookOrderVisitor {
+        type Break = ();
+
+        fn pre_visit_node(&mut self, node: NodeRef<'_>) -> ControlFlow<()> {
+            self.node("pre", node)
+        }
+        fn post_visit_node(&mut self, node: NodeRef<'_>) -> ControlFlow<()> {
+            self.node("post", node)
+        }
+        fn pre_visit_expr(&mut self, _: &Expr) -> ControlFlow<()> {
+            self.typed("pre expr")
+        }
+        fn post_visit_expr(&mut self, _: &Expr) -> ControlFlow<()> {
+            self.typed("post expr")
+        }
+        fn pre_visit_relation(&mut self, _: &ObjectName) -> ControlFlow<()> {
+            self.typed("pre relation")
+        }
+        fn post_visit_relation(&mut self, _: &ObjectName) -> ControlFlow<()> {
+            self.typed("post relation")
+        }
+    }
+
+    #[test]
+    fn test_visit_node_order_around_typed_hooks() {
+        let mut visitor = HookOrderVisitor::default();
+        do_visit("SELECT a FROM t", &mut visitor);
+        assert_eq!(
+            visitor.0,
+            [
+                "pre node Expr",
+                "pre expr",
+                "post expr",
+                "post node Expr",
+                "pre relation",
+                "pre node ObjectName",
+                "post node ObjectName",
+                "post relation",
+            ]
+        );
+    }
+
     #[derive(Debug, PartialEq)]
     struct Node {
         depth: usize,
@@ -1472,7 +1536,7 @@ mod tests {
     }
 
     #[test]
-    fn test_visit_node_reaches_every_derived_node() {
+    fn test_visit_node_reaches_every_node() {
         let mut visitor = NodeVisitor::default();
         do_visit(
             "SELECT CAST(a AS INT) AS x FROM t JOIN u ON t.id = u.id",
@@ -1492,6 +1556,9 @@ mod tests {
         assert!(item.spanned);
         assert_eq!(item.variant, Some("ExprWithAlias"));
 
+        let alias = &visitor.nodes[find("Ident", "x")];
+        assert_eq!(alias.variant, None);
+
         let data_type = &visitor.nodes[find("DataType", "INT")];
         assert!(!data_type.spanned);
         assert_eq!(data_type.variant, Some("Int"));


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org