blob: 7cadf20af3bb292cb972b2db6f8d71879a68dbb9 [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 Tolnaycc8d14e2019-05-08 17:23:09 -070099fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay9ee641a2019-05-08 17:00:16 -0700100 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700101 let ty = Ident::new(&s.ident, Span::call_site());
102 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700103
David Tolnay2fb3d482019-05-08 16:22:10 -0700104 let mut fold_impl = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700105
David Tolnay2fb3d482019-05-08 16:22:10 -0700106 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700107 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700108 let mut fold_variants = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700109
David Tolnay2fb3d482019-05-08 16:22:10 -0700110 for (variant, fields) in variants {
111 let variant_ident = Ident::new(variant, Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700112
David Tolnay2fb3d482019-05-08 16:22:10 -0700113 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700114 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700115 #ty::#variant_ident => {
116 #ty::#variant_ident
David Tolnayf1e57f02019-05-08 15:30:39 -0700117 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700118 });
119 } else {
120 let mut bind_fold_fields = TokenStream::new();
121 let mut fold_fields = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700122
David Tolnay2fb3d482019-05-08 16:22:10 -0700123 for (idx, ty) in fields.iter().enumerate() {
124 let name = format!("_binding_{}", idx);
125 let binding = Ident::new(&name, Span::call_site());
126
David Tolnay4d918822019-05-08 17:05:52 -0700127 bind_fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700128 #binding,
David Tolnayf1e57f02019-05-08 15:30:39 -0700129 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700130
David Tolnay2fb3d482019-05-08 16:22:10 -0700131 let owned_binding = quote!(#binding);
132
David Tolnay4d918822019-05-08 17:05:52 -0700133 fold_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700134 visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
135 );
136
David Tolnay4d918822019-05-08 17:05:52 -0700137 fold_fields.extend(quote!(,));
David Tolnayf1e57f02019-05-08 15:30:39 -0700138 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700139
David Tolnay4d918822019-05-08 17:05:52 -0700140 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700141 #ty::#variant_ident(#bind_fold_fields) => {
142 #ty::#variant_ident(
143 #fold_fields
144 )
145 }
146 });
147 }
148 }
149
David Tolnay4d918822019-05-08 17:05:52 -0700150 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700151 match _i {
152 #fold_variants
153 }
154 });
155 }
David Tolnay12597912019-05-08 17:02:44 -0700156 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700157 let mut fold_fields = TokenStream::new();
158
159 for (field, ty) in fields {
160 let id = Ident::new(&field, Span::call_site());
161 let ref_toks = quote!(_i.#id);
162 let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
163
David Tolnay4d918822019-05-08 17:05:52 -0700164 fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700165 #id: #fold,
David Tolnayf1e57f02019-05-08 15:30:39 -0700166 });
167 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700168
David Tolnay2fb3d482019-05-08 16:22:10 -0700169 if !fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700170 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700171 #ty {
172 #fold_fields
David Tolnayf1e57f02019-05-08 15:30:39 -0700173 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700174 })
175 } else {
David Tolnayf1e57f02019-05-08 15:30:39 -0700176 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700177 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700178 let mut _i = _i;
179 let span = _visitor.fold_span(_i.span());
180 _i.set_span(span);
181 });
182 }
David Tolnay4d918822019-05-08 17:05:52 -0700183 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700184 _i
185 });
186 }
187 }
David Tolnay12597912019-05-08 17:02:44 -0700188 Data::Private => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700189 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700190 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700191 let mut _i = _i;
192 let span = _visitor.fold_span(_i.span());
193 _i.set_span(span);
194 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700195 }
David Tolnay4d918822019-05-08 17:05:52 -0700196 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700197 _i
David Tolnayf1e57f02019-05-08 15:30:39 -0700198 });
199 }
200 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700201
David Tolnay587abd02019-05-08 17:17:31 -0700202 let fold_span_only =
203 s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
204 if fold_span_only {
205 fold_impl = quote! {
206 let span = _visitor.fold_span(_i.span());
207 let mut _i = _i;
208 _i.set_span(span);
209 _i
210 };
211 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700212
David Tolnaycc8d14e2019-05-08 17:23:09 -0700213 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700214 fn #fold_fn(&mut self, i: #ty) -> #ty {
215 #fold_fn(self, i)
216 }
217 });
218
David Tolnaycc8d14e2019-05-08 17:23:09 -0700219 impls.extend(quote! {
David Tolnay587abd02019-05-08 17:17:31 -0700220 pub fn #fold_fn<V: Fold + ?Sized>(
221 _visitor: &mut V, _i: #ty
222 ) -> #ty {
223 #fold_impl
224 }
225 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700226}
227
David Tolnay12597912019-05-08 17:02:44 -0700228pub fn generate(defs: &Definitions) {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700229 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700230 let full_macro = full::get_macro();
David Tolnayf1e57f02019-05-08 15:30:39 -0700231 file::write(
232 FOLD_SRC,
233 quote! {
234 // Unreachable code is generated sometimes without the full feature.
235 #![allow(unreachable_code)]
236
237 use *;
238 #[cfg(any(feature = "full", feature = "derive"))]
239 use token::{Brace, Bracket, Paren, Group};
240 use proc_macro2::Span;
241 #[cfg(any(feature = "full", feature = "derive"))]
242 use gen::helper::fold::*;
243
244 #full_macro
245
246 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
247 ///
248 /// See the [module documentation] for details.
249 ///
250 /// [module documentation]: index.html
251 ///
252 /// *This trait is available if Syn is built with the `"fold"` feature.*
253 pub trait Fold {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700254 #traits
David Tolnayf1e57f02019-05-08 15:30:39 -0700255 }
256
David Tolnaycc8d14e2019-05-08 17:23:09 -0700257 #impls
David Tolnayf1e57f02019-05-08 15:30:39 -0700258 },
259 );
260}