• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{file, full, gen};
2 use anyhow::Result;
3 use proc_macro2::{Ident, Span, TokenStream};
4 use quote::{format_ident, quote};
5 use syn::Index;
6 use syn_codegen::{Data, Definitions, Features, Node, Type};
7 
8 const FOLD_SRC: &str = "../src/gen/fold.rs";
9 
simple_visit(item: &str, name: &TokenStream) -> TokenStream10 fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
11     let ident = gen::under_name(item);
12     let method = format_ident!("fold_{}", ident);
13     quote! {
14         f.#method(#name)
15     }
16 }
17 
visit( ty: &Type, features: &Features, defs: &Definitions, name: &TokenStream, ) -> Option<TokenStream>18 fn visit(
19     ty: &Type,
20     features: &Features,
21     defs: &Definitions,
22     name: &TokenStream,
23 ) -> Option<TokenStream> {
24     match ty {
25         Type::Box(t) => {
26             let res = visit(t, features, defs, &quote!(*#name))?;
27             Some(quote! {
28                 Box::new(#res)
29             })
30         }
31         Type::Vec(t) => {
32             let operand = quote!(it);
33             let val = visit(t, features, defs, &operand)?;
34             Some(quote! {
35                 FoldHelper::lift(#name, |it| #val)
36             })
37         }
38         Type::Punctuated(p) => {
39             let operand = quote!(it);
40             let val = visit(&p.element, features, defs, &operand)?;
41             Some(quote! {
42                 FoldHelper::lift(#name, |it| #val)
43             })
44         }
45         Type::Option(t) => {
46             let it = quote!(it);
47             let val = visit(t, features, defs, &it)?;
48             Some(quote! {
49                 (#name).map(|it| #val)
50             })
51         }
52         Type::Tuple(t) => {
53             let mut code = TokenStream::new();
54             for (i, elem) in t.iter().enumerate() {
55                 let i = Index::from(i);
56                 let it = quote!((#name).#i);
57                 let val = visit(elem, features, defs, &it).unwrap_or(it);
58                 code.extend(val);
59                 code.extend(quote!(,));
60             }
61             Some(quote! {
62                 (#code)
63             })
64         }
65         Type::Token(t) => {
66             let repr = &defs.tokens[t];
67             let is_keyword = repr.chars().next().unwrap().is_alphabetic();
68             let spans = if is_keyword {
69                 quote!(span)
70             } else {
71                 quote!(spans)
72             };
73             let ty = if repr == "await" {
74                 quote!(crate::token::Await)
75             } else {
76                 syn::parse_str(&format!("Token![{}]", repr)).unwrap()
77             };
78             Some(quote! {
79                 #ty(tokens_helper(f, &#name.#spans))
80             })
81         }
82         Type::Group(t) => {
83             let ty = Ident::new(t, Span::call_site());
84             Some(quote! {
85                 #ty(tokens_helper(f, &#name.span))
86             })
87         }
88         Type::Syn(t) => {
89             fn requires_full(features: &Features) -> bool {
90                 features.any.contains("full") && features.any.len() == 1
91             }
92             let mut res = simple_visit(t, name);
93             let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
94             if requires_full(&target.features) && !requires_full(features) {
95                 res = quote!(full!(#res));
96             }
97             Some(res)
98         }
99         Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
100         Type::Ext(_) | Type::Std(_) => None,
101     }
102 }
103 
node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions)104 fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
105     let under_name = gen::under_name(&s.ident);
106     let ty = Ident::new(&s.ident, Span::call_site());
107     let fold_fn = format_ident!("fold_{}", under_name);
108 
109     let mut fold_impl = TokenStream::new();
110 
111     match &s.data {
112         Data::Enum(variants) => {
113             let mut fold_variants = TokenStream::new();
114 
115             for (variant, fields) in variants {
116                 let variant_ident = Ident::new(variant, Span::call_site());
117 
118                 if fields.is_empty() {
119                     fold_variants.extend(quote! {
120                         #ty::#variant_ident => {
121                             #ty::#variant_ident
122                         }
123                     });
124                 } else {
125                     let mut bind_fold_fields = TokenStream::new();
126                     let mut fold_fields = TokenStream::new();
127 
128                     for (idx, ty) in fields.iter().enumerate() {
129                         let binding = format_ident!("_binding_{}", idx);
130 
131                         bind_fold_fields.extend(quote! {
132                             #binding,
133                         });
134 
135                         let owned_binding = quote!(#binding);
136 
137                         fold_fields.extend(
138                             visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
139                         );
140 
141                         fold_fields.extend(quote!(,));
142                     }
143 
144                     fold_variants.extend(quote! {
145                         #ty::#variant_ident(#bind_fold_fields) => {
146                             #ty::#variant_ident(
147                                 #fold_fields
148                             )
149                         }
150                     });
151                 }
152             }
153 
154             let nonexhaustive = if s.exhaustive {
155                 None
156             } else {
157                 Some(quote! {
158                     #[cfg(syn_no_non_exhaustive)]
159                     _ => unreachable!(),
160                 })
161             };
162 
163             fold_impl.extend(quote! {
164                 match node {
165                     #fold_variants
166                     #nonexhaustive
167                 }
168             });
169         }
170         Data::Struct(fields) => {
171             let mut fold_fields = TokenStream::new();
172 
173             for (field, ty) in fields {
174                 let id = Ident::new(&field, Span::call_site());
175                 let ref_toks = quote!(node.#id);
176 
177                 if let Type::Syn(ty) = ty {
178                     if ty == "Reserved" {
179                         fold_fields.extend(quote! {
180                             #id: #ref_toks,
181                         });
182                         continue;
183                     }
184                 }
185 
186                 let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
187 
188                 fold_fields.extend(quote! {
189                     #id: #fold,
190                 });
191             }
192 
193             if !fields.is_empty() {
194                 fold_impl.extend(quote! {
195                     #ty {
196                         #fold_fields
197                     }
198                 })
199             } else {
200                 if ty == "Ident" {
201                     fold_impl.extend(quote! {
202                         let mut node = node;
203                         let span = f.fold_span(node.span());
204                         node.set_span(span);
205                     });
206                 }
207                 fold_impl.extend(quote! {
208                     node
209                 });
210             }
211         }
212         Data::Private => {
213             if ty == "Ident" {
214                 fold_impl.extend(quote! {
215                     let mut node = node;
216                     let span = f.fold_span(node.span());
217                     node.set_span(span);
218                 });
219             }
220             fold_impl.extend(quote! {
221                 node
222             });
223         }
224     }
225 
226     let fold_span_only =
227         s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
228     if fold_span_only {
229         fold_impl = quote! {
230             let span = f.fold_span(node.span());
231             let mut node = node;
232             node.set_span(span);
233             node
234         };
235     }
236 
237     traits.extend(quote! {
238         fn #fold_fn(&mut self, i: #ty) -> #ty {
239             #fold_fn(self, i)
240         }
241     });
242 
243     impls.extend(quote! {
244         pub fn #fold_fn<F>(f: &mut F, node: #ty) -> #ty
245         where
246             F: Fold + ?Sized,
247         {
248             #fold_impl
249         }
250     });
251 }
252 
generate(defs: &Definitions) -> Result<()>253 pub fn generate(defs: &Definitions) -> Result<()> {
254     let (traits, impls) = gen::traverse(defs, node);
255     let full_macro = full::get_macro();
256     file::write(
257         FOLD_SRC,
258         quote! {
259             // Unreachable code is generated sometimes without the full feature.
260             #![allow(unreachable_code, unused_variables)]
261             #![allow(clippy::match_wildcard_for_single_variants)]
262 
263             #[cfg(any(feature = "full", feature = "derive"))]
264             use crate::gen::helper::fold::*;
265             #[cfg(any(feature = "full", feature = "derive"))]
266             use crate::token::{Brace, Bracket, Group, Paren};
267             use crate::*;
268             use proc_macro2::Span;
269 
270             #full_macro
271 
272             /// Syntax tree traversal to transform the nodes of an owned syntax tree.
273             ///
274             /// See the [module documentation] for details.
275             ///
276             /// [module documentation]: self
277             ///
278             /// *This trait is available only if Syn is built with the `"fold"` feature.*
279             pub trait Fold {
280                 #traits
281             }
282 
283             #impls
284         },
285     )?;
286     Ok(())
287 }
288