1 use crate::operand::{Borrowed, Operand, Owned};
2 use crate::{file, full, gen};
3 use anyhow::Result;
4 use proc_macro2::{Ident, Span, TokenStream};
5 use quote::{format_ident, quote};
6 use syn::Index;
7 use syn_codegen::{Data, Definitions, Features, Node, Type};
8
9 const VISIT_SRC: &str = "../src/gen/visit.rs";
10
simple_visit(item: &str, name: &Operand) -> TokenStream11 fn simple_visit(item: &str, name: &Operand) -> TokenStream {
12 let ident = gen::under_name(item);
13 let method = format_ident!("visit_{}", ident);
14 let name = name.ref_tokens();
15 quote! {
16 v.#method(#name)
17 }
18 }
19
noop_visit(name: &Operand) -> TokenStream20 fn noop_visit(name: &Operand) -> TokenStream {
21 let name = name.tokens();
22 quote! {
23 skip!(#name)
24 }
25 }
26
visit( ty: &Type, features: &Features, defs: &Definitions, name: &Operand, ) -> Option<TokenStream>27 fn visit(
28 ty: &Type,
29 features: &Features,
30 defs: &Definitions,
31 name: &Operand,
32 ) -> Option<TokenStream> {
33 match ty {
34 Type::Box(t) => {
35 let name = name.owned_tokens();
36 visit(t, features, defs, &Owned(quote!(*#name)))
37 }
38 Type::Vec(t) => {
39 let operand = Borrowed(quote!(it));
40 let val = visit(t, features, defs, &operand)?;
41 let name = name.ref_tokens();
42 Some(quote! {
43 for it in #name {
44 #val;
45 }
46 })
47 }
48 Type::Punctuated(p) => {
49 let operand = Borrowed(quote!(it));
50 let val = visit(&p.element, features, defs, &operand)?;
51 let name = name.ref_tokens();
52 Some(quote! {
53 for el in Punctuated::pairs(#name) {
54 let (it, p) = el.into_tuple();
55 #val;
56 if let Some(p) = p {
57 tokens_helper(v, &p.spans);
58 }
59 }
60 })
61 }
62 Type::Option(t) => {
63 let it = Borrowed(quote!(it));
64 let val = visit(t, features, defs, &it)?;
65 let name = name.owned_tokens();
66 Some(quote! {
67 if let Some(it) = &#name {
68 #val;
69 }
70 })
71 }
72 Type::Tuple(t) => {
73 let mut code = TokenStream::new();
74 for (i, elem) in t.iter().enumerate() {
75 let name = name.tokens();
76 let i = Index::from(i);
77 let it = Owned(quote!((#name).#i));
78 let val = visit(elem, features, defs, &it).unwrap_or_else(|| noop_visit(&it));
79 code.extend(val);
80 code.extend(quote!(;));
81 }
82 Some(code)
83 }
84 Type::Token(t) => {
85 let name = name.tokens();
86 let repr = &defs.tokens[t];
87 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
88 let spans = if is_keyword {
89 quote!(span)
90 } else {
91 quote!(spans)
92 };
93 Some(quote! {
94 tokens_helper(v, &#name.#spans);
95 })
96 }
97 Type::Group(_) => {
98 let name = name.tokens();
99 Some(quote! {
100 tokens_helper(v, &#name.span);
101 })
102 }
103 Type::Syn(t) => {
104 fn requires_full(features: &Features) -> bool {
105 features.any.contains("full") && features.any.len() == 1
106 }
107 let mut res = simple_visit(t, name);
108 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
109 if requires_full(&target.features) && !requires_full(features) {
110 res = quote!(full!(#res));
111 }
112 Some(res)
113 }
114 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
115 Type::Ext(_) | Type::Std(_) => None,
116 }
117 }
118
node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions)119 fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
120 let under_name = gen::under_name(&s.ident);
121 let ty = Ident::new(&s.ident, Span::call_site());
122 let visit_fn = format_ident!("visit_{}", under_name);
123
124 let mut visit_impl = TokenStream::new();
125
126 match &s.data {
127 Data::Enum(variants) => {
128 let mut visit_variants = TokenStream::new();
129
130 for (variant, fields) in variants {
131 let variant_ident = Ident::new(variant, Span::call_site());
132
133 if fields.is_empty() {
134 visit_variants.extend(quote! {
135 #ty::#variant_ident => {}
136 });
137 } else {
138 let mut bind_visit_fields = TokenStream::new();
139 let mut visit_fields = TokenStream::new();
140
141 for (idx, ty) in fields.iter().enumerate() {
142 let binding = format_ident!("_binding_{}", idx);
143
144 bind_visit_fields.extend(quote! {
145 #binding,
146 });
147
148 let borrowed_binding = Borrowed(quote!(#binding));
149
150 visit_fields.extend(
151 visit(ty, &s.features, defs, &borrowed_binding)
152 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
153 );
154
155 visit_fields.extend(quote!(;));
156 }
157
158 visit_variants.extend(quote! {
159 #ty::#variant_ident(#bind_visit_fields) => {
160 #visit_fields
161 }
162 });
163 }
164 }
165
166 let nonexhaustive = if s.exhaustive {
167 None
168 } else {
169 Some(quote! {
170 #[cfg(syn_no_non_exhaustive)]
171 _ => unreachable!(),
172 })
173 };
174
175 visit_impl.extend(quote! {
176 match node {
177 #visit_variants
178 #nonexhaustive
179 }
180 });
181 }
182 Data::Struct(fields) => {
183 for (field, ty) in fields {
184 if let Type::Syn(ty) = ty {
185 if ty == "Reserved" {
186 continue;
187 }
188 }
189
190 let id = Ident::new(&field, Span::call_site());
191 let ref_toks = Owned(quote!(node.#id));
192 let visit_field = visit(&ty, &s.features, defs, &ref_toks)
193 .unwrap_or_else(|| noop_visit(&ref_toks));
194 visit_impl.extend(quote! {
195 #visit_field;
196 });
197 }
198 }
199 Data::Private => {
200 if ty == "Ident" {
201 visit_impl.extend(quote! {
202 v.visit_span(&node.span());
203 });
204 }
205 }
206 }
207
208 let ast_lifetime = if s.ident == "Span" {
209 None
210 } else {
211 Some(quote!('ast))
212 };
213
214 traits.extend(quote! {
215 fn #visit_fn(&mut self, i: &#ast_lifetime #ty) {
216 #visit_fn(self, i);
217 }
218 });
219
220 impls.extend(quote! {
221 pub fn #visit_fn<'ast, V>(v: &mut V, node: &#ast_lifetime #ty)
222 where
223 V: Visit<'ast> + ?Sized,
224 {
225 #visit_impl
226 }
227 });
228 }
229
generate(defs: &Definitions) -> Result<()>230 pub fn generate(defs: &Definitions) -> Result<()> {
231 let (traits, impls) = gen::traverse(defs, node);
232 let full_macro = full::get_macro();
233 file::write(
234 VISIT_SRC,
235 quote! {
236 #![allow(unused_variables)]
237
238 #[cfg(any(feature = "full", feature = "derive"))]
239 use crate::gen::helper::visit::*;
240 #[cfg(any(feature = "full", feature = "derive"))]
241 use crate::punctuated::Punctuated;
242 use crate::*;
243 use proc_macro2::Span;
244
245 #full_macro
246
247 macro_rules! skip {
248 ($($tt:tt)*) => {};
249 }
250
251 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
252 ///
253 /// See the [module documentation] for details.
254 ///
255 /// [module documentation]: self
256 ///
257 /// *This trait is available only if Syn is built with the `"visit"` feature.*
258 pub trait Visit<'ast> {
259 #traits
260 }
261
262 #impls
263 },
264 )?;
265 Ok(())
266 }
267