blob: 6d47d4706a67a251a01a0ae2fbe35de678368fc1 [file] [log] [blame]
David Tolnay12597912019-05-08 17:02:44 -07001use crate::operand::{Borrowed, Operand, Owned};
David Tolnay1e99fa32019-05-08 16:18:36 -07002use crate::{file, full, gen};
David Tolnay12597912019-05-08 17:02:44 -07003use proc_macro2::{Ident, Span, TokenStream};
David Tolnay4d918822019-05-08 17:05:52 -07004use quote::quote;
David Tolnay12597912019-05-08 17:02:44 -07005use syn::Index;
6use syn_codegen::{Data, Definitions, Features, Node, Type};
David Tolnay490b91b2019-05-08 15:46:09 -07007
8const VISIT_SRC: &str = "../src/gen/visit.rs";
9
David Tolnay2fb3d482019-05-08 16:22:10 -070010fn simple_visit(item: &str, name: &Operand) -> TokenStream {
David Tolnay9ee641a2019-05-08 17:00:16 -070011 let ident = gen::under_name(item);
David Tolnay2fb3d482019-05-08 16:22:10 -070012 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
13 let name = name.ref_tokens();
14 quote! {
15 _visitor.#method(#name)
16 }
17}
18
David Tolnay2fb3d482019-05-08 16:22:10 -070019fn noop_visit(name: &Operand) -> TokenStream {
20 let name = name.tokens();
21 quote! {
22 skip!(#name)
23 }
24}
25
26fn visit(
David Tolnay12597912019-05-08 17:02:44 -070027 ty: &Type,
28 features: &Features,
29 defs: &Definitions,
David Tolnay2fb3d482019-05-08 16:22:10 -070030 name: &Operand,
31) -> Option<TokenStream> {
32 match ty {
David Tolnay12597912019-05-08 17:02:44 -070033 Type::Box(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070034 let name = name.owned_tokens();
35 visit(t, features, defs, &Owned(quote!(*#name)))
36 }
David Tolnay12597912019-05-08 17:02:44 -070037 Type::Vec(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070038 let operand = Borrowed(quote!(it));
39 let val = visit(t, features, defs, &operand)?;
40 let name = name.ref_tokens();
41 Some(quote! {
42 for it in #name {
43 #val
44 }
45 })
46 }
David Tolnay12597912019-05-08 17:02:44 -070047 Type::Punctuated(p) => {
David Tolnaycd680542019-05-08 16:29:47 -070048 let operand = Borrowed(quote!(it));
49 let val = visit(&p.element, features, defs, &operand)?;
50 let name = name.ref_tokens();
51 Some(quote! {
52 for el in Punctuated::pairs(#name) {
53 let it = el.value();
54 #val
55 }
56 })
57 }
David Tolnay12597912019-05-08 17:02:44 -070058 Type::Option(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070059 let it = Borrowed(quote!(it));
60 let val = visit(t, features, defs, &it)?;
61 let name = name.owned_tokens();
62 Some(quote! {
63 if let Some(ref it) = #name {
64 #val
65 }
66 })
67 }
David Tolnay12597912019-05-08 17:02:44 -070068 Type::Tuple(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070069 let mut code = TokenStream::new();
70 for (i, elem) in t.iter().enumerate() {
71 let name = name.tokens();
72 let i = Index::from(i);
73 let it = Owned(quote!((#name).#i));
74 let val = visit(elem, features, defs, &it).unwrap_or_else(|| noop_visit(&it));
David Tolnay4d918822019-05-08 17:05:52 -070075 code.extend(val);
76 code.extend(quote!(;));
David Tolnaycd680542019-05-08 16:29:47 -070077 }
78 Some(code)
79 }
David Tolnay12597912019-05-08 17:02:44 -070080 Type::Token(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070081 let name = name.tokens();
David Tolnay2fb3d482019-05-08 16:22:10 -070082 let repr = &defs.tokens[t];
83 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
David Tolnaycd680542019-05-08 16:29:47 -070084 let spans = if is_keyword {
85 quote!(span)
David Tolnay2fb3d482019-05-08 16:22:10 -070086 } else {
David Tolnaycd680542019-05-08 16:29:47 -070087 quote!(spans)
88 };
89 Some(quote! {
90 tokens_helper(_visitor, &#name.#spans)
91 })
David Tolnay2fb3d482019-05-08 16:22:10 -070092 }
David Tolnay12597912019-05-08 17:02:44 -070093 Type::Group(_) => {
David Tolnaycd680542019-05-08 16:29:47 -070094 let name = name.tokens();
95 Some(quote! {
96 tokens_helper(_visitor, &#name.span)
97 })
98 }
David Tolnay12597912019-05-08 17:02:44 -070099 Type::Syn(t) => {
100 fn requires_full(features: &Features) -> bool {
David Tolnay2fb3d482019-05-08 16:22:10 -0700101 features.any.contains("full") && features.any.len() == 1
102 }
David Tolnaycd680542019-05-08 16:29:47 -0700103 let mut res = simple_visit(t, name);
David Tolnay2fb3d482019-05-08 16:22:10 -0700104 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
David Tolnaycd680542019-05-08 16:29:47 -0700105 if requires_full(&target.features) && !requires_full(features) {
106 res = quote!(full!(#res));
107 }
108 Some(res)
David Tolnay2fb3d482019-05-08 16:22:10 -0700109 }
David Tolnay12597912019-05-08 17:02:44 -0700110 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
111 Type::Ext(_) | Type::Std(_) => None,
David Tolnay2fb3d482019-05-08 16:22:10 -0700112 }
113}
114
David Tolnay12597912019-05-08 17:02:44 -0700115fn visit_features(features: &Features) -> TokenStream {
David Tolnay2fb3d482019-05-08 16:22:10 -0700116 let features = &features.any;
117 match features.len() {
118 0 => quote!(),
119 1 => quote!(#[cfg(feature = #(#features)*)]),
120 _ => quote!(#[cfg(any(#(feature = #features),*))]),
121 }
122}
123
David Tolnaycc8d14e2019-05-08 17:23:09 -0700124fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay2fb3d482019-05-08 16:22:10 -0700125 let features = visit_features(&s.features);
David Tolnay9ee641a2019-05-08 17:00:16 -0700126 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700127 let ty = Ident::new(&s.ident, Span::call_site());
128 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
129
130 let mut visit_impl = TokenStream::new();
131
132 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700133 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700134 let mut visit_variants = TokenStream::new();
135
136 for (variant, fields) in variants {
137 let variant_ident = Ident::new(variant, Span::call_site());
138
139 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700140 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700141 #ty::#variant_ident => {}
142 });
143 } else {
144 let mut bind_visit_fields = TokenStream::new();
145 let mut visit_fields = TokenStream::new();
146
147 for (idx, ty) in fields.iter().enumerate() {
148 let name = format!("_binding_{}", idx);
149 let binding = Ident::new(&name, Span::call_site());
150
David Tolnay4d918822019-05-08 17:05:52 -0700151 bind_visit_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700152 ref #binding,
153 });
154
155 let borrowed_binding = Borrowed(quote!(#binding));
156
David Tolnay4d918822019-05-08 17:05:52 -0700157 visit_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700158 visit(ty, &s.features, defs, &borrowed_binding)
159 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
160 );
161
David Tolnay4d918822019-05-08 17:05:52 -0700162 visit_fields.extend(quote!(;));
David Tolnay2fb3d482019-05-08 16:22:10 -0700163 }
164
David Tolnay4d918822019-05-08 17:05:52 -0700165 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700166 #ty::#variant_ident(#bind_visit_fields) => {
167 #visit_fields
168 }
169 });
170 }
171 }
172
David Tolnay4d918822019-05-08 17:05:52 -0700173 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700174 match *_i {
175 #visit_variants
176 }
177 });
178 }
David Tolnay12597912019-05-08 17:02:44 -0700179 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700180 for (field, ty) in fields {
181 let id = Ident::new(&field, Span::call_site());
182 let ref_toks = Owned(quote!(_i.#id));
183 let visit_field = visit(&ty, &s.features, defs, &ref_toks)
184 .unwrap_or_else(|| noop_visit(&ref_toks));
David Tolnay4d918822019-05-08 17:05:52 -0700185 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700186 #visit_field;
187 });
188 }
189 }
David Tolnay12597912019-05-08 17:02:44 -0700190 Data::Private => {}
David Tolnay2fb3d482019-05-08 16:22:10 -0700191 }
192
David Tolnaycc8d14e2019-05-08 17:23:09 -0700193 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700194 #features
195 fn #visit_fn(&mut self, i: &'ast #ty) {
196 #visit_fn(self, i)
197 }
198 });
199
David Tolnaycc8d14e2019-05-08 17:23:09 -0700200 impls.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700201 #features
202 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
203 _visitor: &mut V, _i: &'ast #ty
204 ) {
205 #visit_impl
206 }
207 });
208}
209
David Tolnay12597912019-05-08 17:02:44 -0700210pub fn generate(defs: &Definitions) {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700211 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700212 let full_macro = full::get_macro();
David Tolnay490b91b2019-05-08 15:46:09 -0700213 file::write(
214 VISIT_SRC,
215 quote! {
216 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
217
218 use *;
219 #[cfg(any(feature = "full", feature = "derive"))]
220 use punctuated::Punctuated;
221 use proc_macro2::Span;
222 #[cfg(any(feature = "full", feature = "derive"))]
223 use gen::helper::visit::*;
224
225 #full_macro
David Tolnay1db3d8e2019-05-08 16:10:25 -0700226
227 #[cfg(any(feature = "full", feature = "derive"))]
228 macro_rules! skip {
229 ($($tt:tt)*) => {};
230 }
David Tolnay490b91b2019-05-08 15:46:09 -0700231
232 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
233 ///
234 /// See the [module documentation] for details.
235 ///
236 /// [module documentation]: index.html
237 ///
238 /// *This trait is available if Syn is built with the `"visit"` feature.*
239 pub trait Visit<'ast> {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700240 #traits
David Tolnay490b91b2019-05-08 15:46:09 -0700241 }
242
David Tolnaycc8d14e2019-05-08 17:23:09 -0700243 #impls
David Tolnay490b91b2019-05-08 15:46:09 -0700244 },
245 );
246}