mz_sql_parser/ast/
visit.rs

1// Copyright Syn Developers.
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// This file is derived from the syn project, available at
5// https://github.com/dtolnay/syn. It was incorporated
6// directly into Materialize on June 8, 2020.
7//
8// Licensed under the Apache License, Version 2.0 (the "License");
9// you may not use this file except in compliance with the License.
10// You may obtain a copy of the License in the LICENSE file at the
11// root of this repository, or online at
12//
13//     http://www.apache.org/licenses/LICENSE-2.0
14//
15// Unless required by applicable law or agreed to in writing, software
16// distributed under the License is distributed on an "AS IS" BASIS,
17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18// See the License for the specific language governing permissions and
19// limitations under the License.
20
21//! Traversal of an immutable AST.
22//!
23//! Each method of the [`Visit`] trait is a hook that can be overridden to
24//! customize the behavior when visiting the corresponding type of node. By
25//! default, every method recursively visits the substructure of the input
26//! by invoking the right visitor method of each of its fields.
27//!
28//! ```
29//! # use mz_sql_parser::ast::{Expr, Function, FunctionArgs, WindowSpec, Raw, AstInfo};
30//! #
31//! pub trait Visit<'ast, T: AstInfo> {
32//!     /* ... */
33//!
34//!     fn visit_function(&mut self, node: &'ast Function<T>) {
35//!         visit_function(self, node);
36//!     }
37//!
38//!     /* ... */
39//!     # fn visit_item_name(&mut self, node: &'ast <T as AstInfo>::ItemName);
40//!     # fn visit_function_args(&mut self, node: &'ast FunctionArgs<T>);
41//!     # fn visit_expr(&mut self, node: &'ast Expr<T>);
42//!     # fn visit_window_spec(&mut self, node: &'ast WindowSpec<T>);
43//! }
44//!
45//! pub fn visit_function<'ast, V, T: AstInfo>(visitor: &mut V, node: &'ast Function<T>)
46//! where
47//!     V: Visit<'ast, T> + ?Sized,
48//! {
49//!     visitor.visit_item_name(&node.name);
50//!     visitor.visit_function_args(&node.args);
51//!     if let Some(filter) = &node.filter {
52//!         visitor.visit_expr(&*filter);
53//!     }
54//!     if let Some(over) = &node.over {
55//!         visitor.visit_window_spec(over);
56//!     }
57//! }
58//! ```
59//!
60//! See also the [`visit_mut`] module for traversing mutable ASTs.
61//!
62//! # Examples
63//!
64//! This visitor will count the number of subqueries in a SQL statement.
65//!
66//! ```
67//! use std::error::Error;
68//!
69//! use mz_sql_parser::ast::{AstInfo, Query, Raw};
70//! use mz_sql_parser::ast::visit::{self, Visit};
71//!
72//! struct SubqueryCounter {
73//!     count: usize,
74//! }
75//!
76//! impl<'ast> Visit<'ast, Raw> for SubqueryCounter {
77//!     fn visit_query(&mut self, query: &'ast Query<Raw>) {
78//!         self.count += 1;
79//!
80//!         // Delegate to the default implementation to visit any nested
81//!         // subqueries. Placing this call at the end of the method results
82//!         // in a pre-order traversal. Place it at the beginning for a
83//!         // post-order traversal instead.
84//!         visit::visit_query(self, query);
85//!     }
86//! }
87//!
88//! fn main() -> Result<(), Box<dyn Error>> {
89//!     let sql = "SELECT (SELECT 1) FROM (SELECT 1) WHERE EXISTS (SELECT (SELECT 1))";
90//!     let stmts = mz_sql_parser::parser::parse_statements(sql.into())?;
91//!
92//!     let mut counter = SubqueryCounter { count: 0 };
93//!     for stmt in &stmts {
94//!         counter.visit_statement(&stmt.ast);
95//!     }
96//!     assert_eq!(counter.count, 5);
97//!     Ok(())
98//! }
99//! ```
100//!
101//! The `'ast` lifetime on the input references means that the syntax tree
102//! outlives the complete recursive visit call, so the visitor is allowed to
103//! hold on to references into the syntax tree.
104//!
105//! ```
106//! use std::error::Error;
107//!
108//! use mz_sql_parser::ast::{Ident, Raw, AstInfo, RawItemName};
109//! use mz_sql_parser::ast::visit::{self, Visit};
110//!
111//! struct IdentCollector<'ast> {
112//!     idents: Vec<&'ast Ident>,
113//! }
114//!
115//! impl<'ast> Visit<'ast, Raw> for IdentCollector<'ast> {
116//!     fn visit_ident(&mut self, node: &'ast Ident) {
117//!         self.idents.push(node);
118//!         visit::visit_ident(self, node);
119//!     }
120//!     fn visit_item_name(&mut self, name: &'ast <Raw as AstInfo>::ItemName) {
121//!         match name {
122//!             RawItemName::Name(n) | RawItemName::Id(_, n, _) => {
123//!                 for node in &n.0 {
124//!                     self.idents.push(node);
125//!                     visit::visit_ident(self, node);
126//!                 }
127//!             }
128//!         }
129//!     }
130//! }
131//!
132//! fn main() -> Result<(), Box<dyn Error>> {
133//!     let sql = "SELECT a FROM b.c WHERE 1 + d(e)";
134//!     let stmts = mz_sql_parser::parser::parse_statements(sql.into())?;
135//!
136//!     let mut collector = IdentCollector { idents: vec![] };
137//!     for stmt in &stmts {
138//!         collector.visit_statement(&stmt.ast);
139//!     }
140//!     assert_eq!(collector.idents, &[
141//!         &Ident::new_unchecked("a"), &Ident::new_unchecked("b"),
142//!         &Ident::new_unchecked("c"), &Ident::new_unchecked("d"),
143//!         &Ident::new_unchecked("e"),
144//!     ]);
145//!     Ok(())
146//! }
147//! ```
148//!
149//! The [`VisitNode`] trait is implemented for every node in the AST and can be
150//! used to write generic functions that apply a `Visit` implementation to any
151//! node in the AST.
152//!
153//! # Implementation notes
154//!
155//! This module is automatically generated by the crate's build script. Changes
156//! to the AST will be automatically propagated to the visitor.
157//!
158//! This approach to AST visitors is inspired by the [`syn`] crate. These
159//! module docs are directly derived from the [`syn::visit`] module docs.
160//!
161//! [`syn`]: https://docs.rs/syn/1.*/syn/index.html
162//! [`syn::visit`]: https://docs.rs/syn/1.*/syn/visit/index.html
163
164#![allow(clippy::all)]
165#![allow(unused_variables)]
166
167use crate::ast::*;
168
169include!(concat!(env!("OUT_DIR"), "/visit.rs"));