• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = el.value();
55                     #val;
56                 }
57             })
58         }
59         Type::Option(t) => {
60             let it = Borrowed(quote!(it));
61             let val = visit(t, features, defs, &it)?;
62             let name = name.ref_tokens();
63             Some(quote! {
64                 if let Some(it) = #name {
65                     #val;
66                 }
67             })
68         }
69         Type::Tuple(t) => {
70             let mut code = TokenStream::new();
71             for (i, elem) in t.iter().enumerate() {
72                 let name = name.tokens();
73                 let i = Index::from(i);
74                 let it = Owned(quote!((#name).#i));
75                 let val = visit(elem, features, defs, &it).unwrap_or_else(|| noop_visit(&it));
76                 code.extend(val);
77                 code.extend(quote!(;));
78             }
79             Some(code)
80         }
81         Type::Syn(t) => {
82             fn requires_full(features: &Features) -> bool {
83                 features.any.contains("full") && features.any.len() == 1
84             }
85             let mut res = simple_visit(t, name);
86             let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
87             if requires_full(&target.features) && !requires_full(features) {
88                 res = quote!(full!(#res));
89             }
90             Some(res)
91         }
92         Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
93         Type::Ext(_) | Type::Std(_) | Type::Token(_) | Type::Group(_) => None,
94     }
95 }
96 
node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions)97 fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
98     let under_name = gen::under_name(&s.ident);
99     let ty = Ident::new(&s.ident, Span::call_site());
100     let visit_fn = format_ident!("visit_{}", under_name);
101 
102     let mut visit_impl = TokenStream::new();
103 
104     match &s.data {
105         Data::Enum(variants) if variants.is_empty() => {
106             visit_impl.extend(quote! {
107                 match *node {}
108             });
109         }
110         Data::Enum(variants) => {
111             let mut visit_variants = TokenStream::new();
112 
113             for (variant, fields) in variants {
114                 let variant_ident = Ident::new(variant, Span::call_site());
115 
116                 if fields.is_empty() {
117                     visit_variants.extend(quote! {
118                         #ty::#variant_ident => {}
119                     });
120                 } else {
121                     let mut bind_visit_fields = TokenStream::new();
122                     let mut visit_fields = TokenStream::new();
123 
124                     for (idx, ty) in fields.iter().enumerate() {
125                         let binding = format_ident!("_binding_{}", idx);
126 
127                         bind_visit_fields.extend(quote! {
128                             #binding,
129                         });
130 
131                         let borrowed_binding = Borrowed(quote!(#binding));
132 
133                         visit_fields.extend(
134                             visit(ty, &s.features, defs, &borrowed_binding)
135                                 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
136                         );
137 
138                         visit_fields.extend(quote!(;));
139                     }
140 
141                     visit_variants.extend(quote! {
142                         #ty::#variant_ident(#bind_visit_fields) => {
143                             #visit_fields
144                         }
145                     });
146                 }
147             }
148 
149             visit_impl.extend(quote! {
150                 match node {
151                     #visit_variants
152                 }
153             });
154         }
155         Data::Struct(fields) => {
156             for (field, ty) in fields {
157                 let id = Ident::new(field, Span::call_site());
158                 let ref_toks = Owned(quote!(node.#id));
159                 let visit_field = visit(ty, &s.features, defs, &ref_toks)
160                     .unwrap_or_else(|| noop_visit(&ref_toks));
161                 visit_impl.extend(quote! {
162                     #visit_field;
163                 });
164             }
165         }
166         Data::Private => {
167             if ty == "Ident" {
168                 visit_impl.extend(quote! {
169                     v.visit_span(&node.span());
170                 });
171             }
172         }
173     }
174 
175     let ast_lifetime = if s.ident == "Span" {
176         None
177     } else {
178         Some(quote!('ast))
179     };
180 
181     traits.extend(quote! {
182         fn #visit_fn(&mut self, i: &#ast_lifetime #ty) {
183             #visit_fn(self, i);
184         }
185     });
186 
187     impls.extend(quote! {
188         pub fn #visit_fn<'ast, V>(v: &mut V, node: &#ast_lifetime #ty)
189         where
190             V: Visit<'ast> + ?Sized,
191         {
192             #visit_impl
193         }
194     });
195 }
196 
generate(defs: &Definitions) -> Result<()>197 pub fn generate(defs: &Definitions) -> Result<()> {
198     let (traits, impls) = gen::traverse(defs, node);
199     let full_macro = full::get_macro();
200     file::write(
201         VISIT_SRC,
202         quote! {
203             #![allow(unused_variables)]
204             #![allow(clippy::needless_pass_by_ref_mut)]
205 
206             #[cfg(any(feature = "full", feature = "derive"))]
207             use crate::punctuated::Punctuated;
208             use crate::*;
209             use proc_macro2::Span;
210 
211             #full_macro
212 
213             macro_rules! skip {
214                 ($($tt:tt)*) => {};
215             }
216 
217             /// Syntax tree traversal to walk a shared borrow of a syntax tree.
218             ///
219             /// See the [module documentation] for details.
220             ///
221             /// [module documentation]: self
222             pub trait Visit<'ast> {
223                 #traits
224             }
225 
226             #impls
227         },
228     )?;
229     Ok(())
230 }
231