blob: 9d5538487eb565bd3bdd0236a17c75b73beeadfd [file] [log] [blame]
David Tolnay1e99fa32019-05-08 16:18:36 -07001use crate::{file, full, gen};
David Tolnay12597912019-05-08 17:02:44 -07002use proc_macro2::{Ident, Span, TokenStream};
David Tolnay4d918822019-05-08 17:05:52 -07003use quote::quote;
David Tolnay12597912019-05-08 17:02:44 -07004use syn::Index;
5use syn_codegen::{Data, Definitions, Features, Node, Type};
David Tolnayf1e57f02019-05-08 15:30:39 -07006
7const FOLD_SRC: &str = "../src/gen/fold.rs";
8
David Tolnay2fb3d482019-05-08 16:22:10 -07009fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
David Tolnay9ee641a2019-05-08 17:00:16 -070010 let ident = gen::under_name(item);
David Tolnay2fb3d482019-05-08 16:22:10 -070011 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
12 quote! {
13 _visitor.#method(#name)
14 }
15}
16
David Tolnay2fb3d482019-05-08 16:22:10 -070017fn visit(
David Tolnay12597912019-05-08 17:02:44 -070018 ty: &Type,
19 features: &Features,
20 defs: &Definitions,
David Tolnay2fb3d482019-05-08 16:22:10 -070021 name: &TokenStream,
22) -> Option<TokenStream> {
23 match ty {
David Tolnay12597912019-05-08 17:02:44 -070024 Type::Box(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070025 let res = visit(t, features, defs, &quote!(*#name))?;
26 Some(quote! {
27 Box::new(#res)
28 })
29 }
David Tolnay12597912019-05-08 17:02:44 -070030 Type::Vec(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070031 let operand = quote!(it);
32 let val = visit(t, features, defs, &operand)?;
33 Some(quote! {
34 FoldHelper::lift(#name, |it| { #val })
35 })
36 }
David Tolnay12597912019-05-08 17:02:44 -070037 Type::Punctuated(p) => {
David Tolnaycd680542019-05-08 16:29:47 -070038 let operand = quote!(it);
39 let val = visit(&p.element, features, defs, &operand)?;
40 Some(quote! {
41 FoldHelper::lift(#name, |it| { #val })
42 })
43 }
David Tolnay12597912019-05-08 17:02:44 -070044 Type::Option(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070045 let it = quote!(it);
46 let val = visit(t, features, defs, &it)?;
47 Some(quote! {
48 (#name).map(|it| { #val })
49 })
50 }
David Tolnay12597912019-05-08 17:02:44 -070051 Type::Tuple(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070052 let mut code = TokenStream::new();
53 for (i, elem) in t.iter().enumerate() {
54 let i = Index::from(i);
55 let it = quote!((#name).#i);
56 let val = visit(elem, features, defs, &it).unwrap_or(it);
David Tolnay4d918822019-05-08 17:05:52 -070057 code.extend(val);
58 code.extend(quote!(,));
David Tolnaycd680542019-05-08 16:29:47 -070059 }
60 Some(quote! {
61 (#code)
62 })
63 }
David Tolnay12597912019-05-08 17:02:44 -070064 Type::Token(t) => {
David Tolnay2fb3d482019-05-08 16:22:10 -070065 let repr = &defs.tokens[t];
66 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
David Tolnaycd680542019-05-08 16:29:47 -070067 let spans = if is_keyword {
68 quote!(span)
David Tolnay2fb3d482019-05-08 16:22:10 -070069 } else {
David Tolnaycd680542019-05-08 16:29:47 -070070 quote!(spans)
71 };
72 let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
73 Some(quote! {
74 #ty(tokens_helper(_visitor, &#name.#spans))
75 })
David Tolnayf1e57f02019-05-08 15:30:39 -070076 }
David Tolnay12597912019-05-08 17:02:44 -070077 Type::Group(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070078 let ty = Ident::new(t, Span::call_site());
79 Some(quote! {
80 #ty(tokens_helper(_visitor, &#name.span))
81 })
82 }
David Tolnay12597912019-05-08 17:02:44 -070083 Type::Syn(t) => {
84 fn requires_full(features: &Features) -> bool {
David Tolnay2fb3d482019-05-08 16:22:10 -070085 features.any.contains("full") && features.any.len() == 1
86 }
David Tolnaycd680542019-05-08 16:29:47 -070087 let mut res = simple_visit(t, name);
David Tolnay2fb3d482019-05-08 16:22:10 -070088 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
David Tolnaycd680542019-05-08 16:29:47 -070089 if requires_full(&target.features) && !requires_full(features) {
90 res = quote!(full!(#res));
91 }
92 Some(res)
David Tolnayf1e57f02019-05-08 15:30:39 -070093 }
David Tolnay12597912019-05-08 17:02:44 -070094 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
95 Type::Ext(_) | Type::Std(_) => None,
David Tolnayf1e57f02019-05-08 15:30:39 -070096 }
David Tolnay2fb3d482019-05-08 16:22:10 -070097}
David Tolnayf1e57f02019-05-08 15:30:39 -070098
David Tolnay12597912019-05-08 17:02:44 -070099fn visit_features(features: &Features) -> TokenStream {
David Tolnay2fb3d482019-05-08 16:22:10 -0700100 let features = &features.any;
101 match features.len() {
102 0 => quote!(),
103 1 => quote!(#[cfg(feature = #(#features)*)]),
104 _ => quote!(#[cfg(any(#(feature = #features),*))]),
David Tolnayf1e57f02019-05-08 15:30:39 -0700105 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700106}
David Tolnayf1e57f02019-05-08 15:30:39 -0700107
David Tolnaycc8d14e2019-05-08 17:23:09 -0700108fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay2fb3d482019-05-08 16:22:10 -0700109 let features = visit_features(&s.features);
David Tolnay9ee641a2019-05-08 17:00:16 -0700110 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700111 let ty = Ident::new(&s.ident, Span::call_site());
112 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700113
David Tolnay2fb3d482019-05-08 16:22:10 -0700114 let mut fold_impl = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700115
David Tolnay2fb3d482019-05-08 16:22:10 -0700116 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700117 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700118 let mut fold_variants = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700119
David Tolnay2fb3d482019-05-08 16:22:10 -0700120 for (variant, fields) in variants {
121 let variant_ident = Ident::new(variant, Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700122
David Tolnay2fb3d482019-05-08 16:22:10 -0700123 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700124 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700125 #ty::#variant_ident => {
126 #ty::#variant_ident
David Tolnayf1e57f02019-05-08 15:30:39 -0700127 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700128 });
129 } else {
130 let mut bind_fold_fields = TokenStream::new();
131 let mut fold_fields = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700132
David Tolnay2fb3d482019-05-08 16:22:10 -0700133 for (idx, ty) in fields.iter().enumerate() {
134 let name = format!("_binding_{}", idx);
135 let binding = Ident::new(&name, Span::call_site());
136
David Tolnay4d918822019-05-08 17:05:52 -0700137 bind_fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700138 #binding,
David Tolnayf1e57f02019-05-08 15:30:39 -0700139 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700140
David Tolnay2fb3d482019-05-08 16:22:10 -0700141 let owned_binding = quote!(#binding);
142
David Tolnay4d918822019-05-08 17:05:52 -0700143 fold_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700144 visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
145 );
146
David Tolnay4d918822019-05-08 17:05:52 -0700147 fold_fields.extend(quote!(,));
David Tolnayf1e57f02019-05-08 15:30:39 -0700148 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700149
David Tolnay4d918822019-05-08 17:05:52 -0700150 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700151 #ty::#variant_ident(#bind_fold_fields) => {
152 #ty::#variant_ident(
153 #fold_fields
154 )
155 }
156 });
157 }
158 }
159
David Tolnay4d918822019-05-08 17:05:52 -0700160 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700161 match _i {
162 #fold_variants
163 }
164 });
165 }
David Tolnay12597912019-05-08 17:02:44 -0700166 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700167 let mut fold_fields = TokenStream::new();
168
169 for (field, ty) in fields {
170 let id = Ident::new(&field, Span::call_site());
171 let ref_toks = quote!(_i.#id);
172 let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
173
David Tolnay4d918822019-05-08 17:05:52 -0700174 fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700175 #id: #fold,
David Tolnayf1e57f02019-05-08 15:30:39 -0700176 });
177 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700178
David Tolnay2fb3d482019-05-08 16:22:10 -0700179 if !fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700180 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700181 #ty {
182 #fold_fields
David Tolnayf1e57f02019-05-08 15:30:39 -0700183 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700184 })
185 } else {
David Tolnayf1e57f02019-05-08 15:30:39 -0700186 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700187 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700188 let mut _i = _i;
189 let span = _visitor.fold_span(_i.span());
190 _i.set_span(span);
191 });
192 }
David Tolnay4d918822019-05-08 17:05:52 -0700193 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700194 _i
195 });
196 }
197 }
David Tolnay12597912019-05-08 17:02:44 -0700198 Data::Private => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700199 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700200 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700201 let mut _i = _i;
202 let span = _visitor.fold_span(_i.span());
203 _i.set_span(span);
204 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700205 }
David Tolnay4d918822019-05-08 17:05:52 -0700206 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700207 _i
David Tolnayf1e57f02019-05-08 15:30:39 -0700208 });
209 }
210 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700211
David Tolnay587abd02019-05-08 17:17:31 -0700212 let fold_span_only =
213 s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
214 if fold_span_only {
215 fold_impl = quote! {
216 let span = _visitor.fold_span(_i.span());
217 let mut _i = _i;
218 _i.set_span(span);
219 _i
220 };
221 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700222
David Tolnaycc8d14e2019-05-08 17:23:09 -0700223 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700224 #features
225 fn #fold_fn(&mut self, i: #ty) -> #ty {
226 #fold_fn(self, i)
227 }
228 });
229
David Tolnaycc8d14e2019-05-08 17:23:09 -0700230 impls.extend(quote! {
David Tolnay587abd02019-05-08 17:17:31 -0700231 #features
232 pub fn #fold_fn<V: Fold + ?Sized>(
233 _visitor: &mut V, _i: #ty
234 ) -> #ty {
235 #fold_impl
236 }
237 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700238}
239
David Tolnay12597912019-05-08 17:02:44 -0700240pub fn generate(defs: &Definitions) {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700241 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700242 let full_macro = full::get_macro();
David Tolnayf1e57f02019-05-08 15:30:39 -0700243 file::write(
244 FOLD_SRC,
245 quote! {
246 // Unreachable code is generated sometimes without the full feature.
247 #![allow(unreachable_code)]
248
249 use *;
250 #[cfg(any(feature = "full", feature = "derive"))]
251 use token::{Brace, Bracket, Paren, Group};
252 use proc_macro2::Span;
253 #[cfg(any(feature = "full", feature = "derive"))]
254 use gen::helper::fold::*;
255
256 #full_macro
257
258 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
259 ///
260 /// See the [module documentation] for details.
261 ///
262 /// [module documentation]: index.html
263 ///
264 /// *This trait is available if Syn is built with the `"fold"` feature.*
265 pub trait Fold {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700266 #traits
David Tolnayf1e57f02019-05-08 15:30:39 -0700267 }
268
David Tolnaycc8d14e2019-05-08 17:23:09 -0700269 #impls
David Tolnayf1e57f02019-05-08 15:30:39 -0700270 },
271 );
272}