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, "e!(*#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