blob: 122d57e298e61348a60f275ad037c98c9c7ed1ae [file] [log] [blame]
David Tolnayb2188a62019-05-09 11:42:44 -07001use crate::error::Result;
David Tolnay12597912019-05-08 17:02:44 -07002use crate::operand::{Borrowed, Operand, Owned};
David Tolnay1e99fa32019-05-08 16:18:36 -07003use crate::{file, full, gen};
David Tolnay12597912019-05-08 17:02:44 -07004use proc_macro2::{Ident, Span, TokenStream};
David Tolnay4d918822019-05-08 17:05:52 -07005use quote::quote;
David Tolnay12597912019-05-08 17:02:44 -07006use syn::Index;
7use syn_codegen::{Data, Definitions, Features, Node, Type};
David Tolnay490b91b2019-05-08 15:46:09 -07008
9const VISIT_SRC: &str = "../src/gen/visit.rs";
10
David Tolnay2fb3d482019-05-08 16:22:10 -070011fn simple_visit(item: &str, name: &Operand) -> TokenStream {
David Tolnay9ee641a2019-05-08 17:00:16 -070012 let ident = gen::under_name(item);
David Tolnay2fb3d482019-05-08 16:22:10 -070013 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
14 let name = name.ref_tokens();
15 quote! {
16 _visitor.#method(#name)
17 }
18}
19
David Tolnay2fb3d482019-05-08 16:22:10 -070020fn noop_visit(name: &Operand) -> TokenStream {
21 let name = name.tokens();
22 quote! {
23 skip!(#name)
24 }
25}
26
27fn visit(
David Tolnay12597912019-05-08 17:02:44 -070028 ty: &Type,
29 features: &Features,
30 defs: &Definitions,
David Tolnay2fb3d482019-05-08 16:22:10 -070031 name: &Operand,
32) -> Option<TokenStream> {
33 match ty {
David Tolnay12597912019-05-08 17:02:44 -070034 Type::Box(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070035 let name = name.owned_tokens();
36 visit(t, features, defs, &Owned(quote!(*#name)))
37 }
David Tolnay12597912019-05-08 17:02:44 -070038 Type::Vec(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070039 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 }
David Tolnay12597912019-05-08 17:02:44 -070048 Type::Punctuated(p) => {
David Tolnaycd680542019-05-08 16:29:47 -070049 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 }
David Tolnay12597912019-05-08 17:02:44 -070059 Type::Option(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070060 let it = Borrowed(quote!(it));
61 let val = visit(t, features, defs, &it)?;
62 let name = name.owned_tokens();
63 Some(quote! {
64 if let Some(ref it) = #name {
65 #val
66 }
67 })
68 }
David Tolnay12597912019-05-08 17:02:44 -070069 Type::Tuple(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070070 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));
David Tolnay4d918822019-05-08 17:05:52 -070076 code.extend(val);
77 code.extend(quote!(;));
David Tolnaycd680542019-05-08 16:29:47 -070078 }
79 Some(code)
80 }
David Tolnay12597912019-05-08 17:02:44 -070081 Type::Token(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070082 let name = name.tokens();
David Tolnay2fb3d482019-05-08 16:22:10 -070083 let repr = &defs.tokens[t];
84 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
David Tolnaycd680542019-05-08 16:29:47 -070085 let spans = if is_keyword {
86 quote!(span)
David Tolnay2fb3d482019-05-08 16:22:10 -070087 } else {
David Tolnaycd680542019-05-08 16:29:47 -070088 quote!(spans)
89 };
90 Some(quote! {
91 tokens_helper(_visitor, &#name.#spans)
92 })
David Tolnay2fb3d482019-05-08 16:22:10 -070093 }
David Tolnay12597912019-05-08 17:02:44 -070094 Type::Group(_) => {
David Tolnaycd680542019-05-08 16:29:47 -070095 let name = name.tokens();
96 Some(quote! {
97 tokens_helper(_visitor, &#name.span)
98 })
99 }
David Tolnay12597912019-05-08 17:02:44 -0700100 Type::Syn(t) => {
101 fn requires_full(features: &Features) -> bool {
David Tolnay2fb3d482019-05-08 16:22:10 -0700102 features.any.contains("full") && features.any.len() == 1
103 }
David Tolnaycd680542019-05-08 16:29:47 -0700104 let mut res = simple_visit(t, name);
David Tolnay2fb3d482019-05-08 16:22:10 -0700105 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
David Tolnaycd680542019-05-08 16:29:47 -0700106 if requires_full(&target.features) && !requires_full(features) {
107 res = quote!(full!(#res));
108 }
109 Some(res)
David Tolnay2fb3d482019-05-08 16:22:10 -0700110 }
David Tolnay12597912019-05-08 17:02:44 -0700111 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
112 Type::Ext(_) | Type::Std(_) => None,
David Tolnay2fb3d482019-05-08 16:22:10 -0700113 }
114}
115
David Tolnaycc8d14e2019-05-08 17:23:09 -0700116fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay9ee641a2019-05-08 17:00:16 -0700117 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700118 let ty = Ident::new(&s.ident, Span::call_site());
119 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
120
121 let mut visit_impl = TokenStream::new();
122
123 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700124 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700125 let mut visit_variants = TokenStream::new();
126
127 for (variant, fields) in variants {
128 let variant_ident = Ident::new(variant, Span::call_site());
129
130 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700131 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700132 #ty::#variant_ident => {}
133 });
134 } else {
135 let mut bind_visit_fields = TokenStream::new();
136 let mut visit_fields = TokenStream::new();
137
138 for (idx, ty) in fields.iter().enumerate() {
139 let name = format!("_binding_{}", idx);
140 let binding = Ident::new(&name, Span::call_site());
141
David Tolnay4d918822019-05-08 17:05:52 -0700142 bind_visit_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700143 ref #binding,
144 });
145
146 let borrowed_binding = Borrowed(quote!(#binding));
147
David Tolnay4d918822019-05-08 17:05:52 -0700148 visit_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700149 visit(ty, &s.features, defs, &borrowed_binding)
150 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
151 );
152
David Tolnay4d918822019-05-08 17:05:52 -0700153 visit_fields.extend(quote!(;));
David Tolnay2fb3d482019-05-08 16:22:10 -0700154 }
155
David Tolnay4d918822019-05-08 17:05:52 -0700156 visit_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700157 #ty::#variant_ident(#bind_visit_fields) => {
158 #visit_fields
159 }
160 });
161 }
162 }
163
David Tolnay4d918822019-05-08 17:05:52 -0700164 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700165 match *_i {
166 #visit_variants
167 }
168 });
169 }
David Tolnay12597912019-05-08 17:02:44 -0700170 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700171 for (field, ty) in fields {
172 let id = Ident::new(&field, Span::call_site());
173 let ref_toks = Owned(quote!(_i.#id));
174 let visit_field = visit(&ty, &s.features, defs, &ref_toks)
175 .unwrap_or_else(|| noop_visit(&ref_toks));
David Tolnay4d918822019-05-08 17:05:52 -0700176 visit_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700177 #visit_field;
178 });
179 }
180 }
David Tolnay12597912019-05-08 17:02:44 -0700181 Data::Private => {}
David Tolnay2fb3d482019-05-08 16:22:10 -0700182 }
183
David Tolnaycc8d14e2019-05-08 17:23:09 -0700184 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700185 fn #visit_fn(&mut self, i: &'ast #ty) {
186 #visit_fn(self, i)
187 }
188 });
189
David Tolnaycc8d14e2019-05-08 17:23:09 -0700190 impls.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700191 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
192 _visitor: &mut V, _i: &'ast #ty
193 ) {
194 #visit_impl
195 }
196 });
197}
198
David Tolnayb2188a62019-05-09 11:42:44 -0700199pub fn generate(defs: &Definitions) -> Result<()> {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700200 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700201 let full_macro = full::get_macro();
David Tolnay490b91b2019-05-08 15:46:09 -0700202 file::write(
203 VISIT_SRC,
204 quote! {
205 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
206
207 use *;
208 #[cfg(any(feature = "full", feature = "derive"))]
209 use punctuated::Punctuated;
210 use proc_macro2::Span;
211 #[cfg(any(feature = "full", feature = "derive"))]
212 use gen::helper::visit::*;
213
214 #full_macro
David Tolnay1db3d8e2019-05-08 16:10:25 -0700215
216 #[cfg(any(feature = "full", feature = "derive"))]
217 macro_rules! skip {
218 ($($tt:tt)*) => {};
219 }
David Tolnay490b91b2019-05-08 15:46:09 -0700220
221 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
222 ///
223 /// See the [module documentation] for details.
224 ///
225 /// [module documentation]: index.html
226 ///
227 /// *This trait is available if Syn is built with the `"visit"` feature.*
228 pub trait Visit<'ast> {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700229 #traits
David Tolnay490b91b2019-05-08 15:46:09 -0700230 }
231
David Tolnaycc8d14e2019-05-08 17:23:09 -0700232 #impls
David Tolnay490b91b2019-05-08 15:46:09 -0700233 },
David Tolnayb2188a62019-05-09 11:42:44 -0700234 )?;
235 Ok(())
David Tolnay490b91b2019-05-08 15:46:09 -0700236}