blob: 988045dd5de74da98a56a52221d2640d3d439152 [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 Tolnaycc8d14e2019-05-08 17:23:09 -0700115fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay9ee641a2019-05-08 17:00:16 -0700116 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700117 let ty = Ident::new(&s.ident, Span::call_site());
118 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
119
120 let mut visit_impl = TokenStream::new();
121
122 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700123 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700124 let mut visit_variants = TokenStream::new();
125
126 for (variant, fields) in variants {
127 let variant_ident = Ident::new(variant, Span::call_site());
128
129 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700130 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700131 #ty::#variant_ident => {}
132 });
133 } else {
134 let mut bind_visit_fields = TokenStream::new();
135 let mut visit_fields = TokenStream::new();
136
137 for (idx, ty) in fields.iter().enumerate() {
138 let name = format!("_binding_{}", idx);
139 let binding = Ident::new(&name, Span::call_site());
140
David Tolnay4d918822019-05-08 17:05:52 -0700141 bind_visit_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700142 ref #binding,
143 });
144
145 let borrowed_binding = Borrowed(quote!(#binding));
146
David Tolnay4d918822019-05-08 17:05:52 -0700147 visit_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700148 visit(ty, &s.features, defs, &borrowed_binding)
149 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
150 );
151
David Tolnay4d918822019-05-08 17:05:52 -0700152 visit_fields.extend(quote!(;));
David Tolnay2fb3d482019-05-08 16:22:10 -0700153 }
154
David Tolnay4d918822019-05-08 17:05:52 -0700155 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700156 #ty::#variant_ident(#bind_visit_fields) => {
157 #visit_fields
158 }
159 });
160 }
161 }
162
David Tolnay4d918822019-05-08 17:05:52 -0700163 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700164 match *_i {
165 #visit_variants
166 }
167 });
168 }
David Tolnay12597912019-05-08 17:02:44 -0700169 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700170 for (field, ty) in fields {
171 let id = Ident::new(&field, Span::call_site());
172 let ref_toks = Owned(quote!(_i.#id));
173 let visit_field = visit(&ty, &s.features, defs, &ref_toks)
174 .unwrap_or_else(|| noop_visit(&ref_toks));
David Tolnay4d918822019-05-08 17:05:52 -0700175 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700176 #visit_field;
177 });
178 }
179 }
David Tolnay12597912019-05-08 17:02:44 -0700180 Data::Private => {}
David Tolnay2fb3d482019-05-08 16:22:10 -0700181 }
182
David Tolnaycc8d14e2019-05-08 17:23:09 -0700183 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700184 fn #visit_fn(&mut self, i: &'ast #ty) {
185 #visit_fn(self, i)
186 }
187 });
188
David Tolnaycc8d14e2019-05-08 17:23:09 -0700189 impls.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700190 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
191 _visitor: &mut V, _i: &'ast #ty
192 ) {
193 #visit_impl
194 }
195 });
196}
197
David Tolnay12597912019-05-08 17:02:44 -0700198pub fn generate(defs: &Definitions) {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700199 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700200 let full_macro = full::get_macro();
David Tolnay490b91b2019-05-08 15:46:09 -0700201 file::write(
202 VISIT_SRC,
203 quote! {
204 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
205
206 use *;
207 #[cfg(any(feature = "full", feature = "derive"))]
208 use punctuated::Punctuated;
209 use proc_macro2::Span;
210 #[cfg(any(feature = "full", feature = "derive"))]
211 use gen::helper::visit::*;
212
213 #full_macro
David Tolnay1db3d8e2019-05-08 16:10:25 -0700214
215 #[cfg(any(feature = "full", feature = "derive"))]
216 macro_rules! skip {
217 ($($tt:tt)*) => {};
218 }
David Tolnay490b91b2019-05-08 15:46:09 -0700219
220 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
221 ///
222 /// See the [module documentation] for details.
223 ///
224 /// [module documentation]: index.html
225 ///
226 /// *This trait is available if Syn is built with the `"visit"` feature.*
227 pub trait Visit<'ast> {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700228 #traits
David Tolnay490b91b2019-05-08 15:46:09 -0700229 }
230
David Tolnaycc8d14e2019-05-08 17:23:09 -0700231 #impls
David Tolnay490b91b2019-05-08 15:46:09 -0700232 },
233 );
234}