blob: b9f31bb1a32d0b8234a28e1ff995a841c519f4e7 [file] [log] [blame]
David Tolnayb2188a62019-05-09 11:42:44 -07001use crate::error::Result;
David Tolnay1e99fa32019-05-08 16:18:36 -07002use crate::{file, full, gen};
David Tolnay12597912019-05-08 17:02:44 -07003use proc_macro2::{Ident, Span, TokenStream};
David Tolnay4d918822019-05-08 17:05:52 -07004use quote::quote;
David Tolnay12597912019-05-08 17:02:44 -07005use syn::Index;
6use syn_codegen::{Data, Definitions, Features, Node, Type};
David Tolnayf1e57f02019-05-08 15:30:39 -07007
8const FOLD_SRC: &str = "../src/gen/fold.rs";
9
David Tolnay2fb3d482019-05-08 16:22:10 -070010fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
David Tolnay9ee641a2019-05-08 17:00:16 -070011 let ident = gen::under_name(item);
David Tolnay2fb3d482019-05-08 16:22:10 -070012 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
13 quote! {
14 _visitor.#method(#name)
15 }
16}
17
David Tolnay2fb3d482019-05-08 16:22:10 -070018fn visit(
David Tolnay12597912019-05-08 17:02:44 -070019 ty: &Type,
20 features: &Features,
21 defs: &Definitions,
David Tolnay2fb3d482019-05-08 16:22:10 -070022 name: &TokenStream,
23) -> Option<TokenStream> {
24 match ty {
David Tolnay12597912019-05-08 17:02:44 -070025 Type::Box(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070026 let res = visit(t, features, defs, &quote!(*#name))?;
27 Some(quote! {
28 Box::new(#res)
29 })
30 }
David Tolnay12597912019-05-08 17:02:44 -070031 Type::Vec(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070032 let operand = quote!(it);
33 let val = visit(t, features, defs, &operand)?;
34 Some(quote! {
35 FoldHelper::lift(#name, |it| { #val })
36 })
37 }
David Tolnay12597912019-05-08 17:02:44 -070038 Type::Punctuated(p) => {
David Tolnaycd680542019-05-08 16:29:47 -070039 let operand = quote!(it);
40 let val = visit(&p.element, features, defs, &operand)?;
41 Some(quote! {
42 FoldHelper::lift(#name, |it| { #val })
43 })
44 }
David Tolnay12597912019-05-08 17:02:44 -070045 Type::Option(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070046 let it = quote!(it);
47 let val = visit(t, features, defs, &it)?;
48 Some(quote! {
49 (#name).map(|it| { #val })
50 })
51 }
David Tolnay12597912019-05-08 17:02:44 -070052 Type::Tuple(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070053 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);
David Tolnay4d918822019-05-08 17:05:52 -070058 code.extend(val);
59 code.extend(quote!(,));
David Tolnaycd680542019-05-08 16:29:47 -070060 }
61 Some(quote! {
62 (#code)
63 })
64 }
David Tolnay12597912019-05-08 17:02:44 -070065 Type::Token(t) => {
David Tolnay2fb3d482019-05-08 16:22:10 -070066 let repr = &defs.tokens[t];
67 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
David Tolnaycd680542019-05-08 16:29:47 -070068 let spans = if is_keyword {
69 quote!(span)
David Tolnay2fb3d482019-05-08 16:22:10 -070070 } else {
David Tolnaycd680542019-05-08 16:29:47 -070071 quote!(spans)
72 };
73 let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
74 Some(quote! {
75 #ty(tokens_helper(_visitor, &#name.#spans))
76 })
David Tolnayf1e57f02019-05-08 15:30:39 -070077 }
David Tolnay12597912019-05-08 17:02:44 -070078 Type::Group(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070079 let ty = Ident::new(t, Span::call_site());
80 Some(quote! {
81 #ty(tokens_helper(_visitor, &#name.span))
82 })
83 }
David Tolnay12597912019-05-08 17:02:44 -070084 Type::Syn(t) => {
85 fn requires_full(features: &Features) -> bool {
David Tolnay2fb3d482019-05-08 16:22:10 -070086 features.any.contains("full") && features.any.len() == 1
87 }
David Tolnaycd680542019-05-08 16:29:47 -070088 let mut res = simple_visit(t, name);
David Tolnay2fb3d482019-05-08 16:22:10 -070089 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
David Tolnaycd680542019-05-08 16:29:47 -070090 if requires_full(&target.features) && !requires_full(features) {
91 res = quote!(full!(#res));
92 }
93 Some(res)
David Tolnayf1e57f02019-05-08 15:30:39 -070094 }
David Tolnay12597912019-05-08 17:02:44 -070095 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
96 Type::Ext(_) | Type::Std(_) => None,
David Tolnayf1e57f02019-05-08 15:30:39 -070097 }
David Tolnay2fb3d482019-05-08 16:22:10 -070098}
David Tolnayf1e57f02019-05-08 15:30:39 -070099
David Tolnaycc8d14e2019-05-08 17:23:09 -0700100fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
David Tolnay9ee641a2019-05-08 17:00:16 -0700101 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700102 let ty = Ident::new(&s.ident, Span::call_site());
103 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700104
David Tolnay2fb3d482019-05-08 16:22:10 -0700105 let mut fold_impl = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700106
David Tolnay2fb3d482019-05-08 16:22:10 -0700107 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700108 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700109 let mut fold_variants = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700110
David Tolnay2fb3d482019-05-08 16:22:10 -0700111 for (variant, fields) in variants {
112 let variant_ident = Ident::new(variant, Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700113
David Tolnay2fb3d482019-05-08 16:22:10 -0700114 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700115 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700116 #ty::#variant_ident => {
117 #ty::#variant_ident
David Tolnayf1e57f02019-05-08 15:30:39 -0700118 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700119 });
120 } else {
121 let mut bind_fold_fields = TokenStream::new();
122 let mut fold_fields = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700123
David Tolnay2fb3d482019-05-08 16:22:10 -0700124 for (idx, ty) in fields.iter().enumerate() {
125 let name = format!("_binding_{}", idx);
126 let binding = Ident::new(&name, Span::call_site());
127
David Tolnay4d918822019-05-08 17:05:52 -0700128 bind_fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700129 #binding,
David Tolnayf1e57f02019-05-08 15:30:39 -0700130 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700131
David Tolnay2fb3d482019-05-08 16:22:10 -0700132 let owned_binding = quote!(#binding);
133
David Tolnay4d918822019-05-08 17:05:52 -0700134 fold_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700135 visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
136 );
137
David Tolnay4d918822019-05-08 17:05:52 -0700138 fold_fields.extend(quote!(,));
David Tolnayf1e57f02019-05-08 15:30:39 -0700139 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700140
David Tolnay4d918822019-05-08 17:05:52 -0700141 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700142 #ty::#variant_ident(#bind_fold_fields) => {
143 #ty::#variant_ident(
144 #fold_fields
145 )
146 }
147 });
148 }
149 }
150
David Tolnay4d918822019-05-08 17:05:52 -0700151 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700152 match _i {
153 #fold_variants
154 }
155 });
156 }
David Tolnay12597912019-05-08 17:02:44 -0700157 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700158 let mut fold_fields = TokenStream::new();
159
160 for (field, ty) in fields {
161 let id = Ident::new(&field, Span::call_site());
162 let ref_toks = quote!(_i.#id);
163 let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
164
David Tolnay4d918822019-05-08 17:05:52 -0700165 fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700166 #id: #fold,
David Tolnayf1e57f02019-05-08 15:30:39 -0700167 });
168 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700169
David Tolnay2fb3d482019-05-08 16:22:10 -0700170 if !fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700171 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700172 #ty {
173 #fold_fields
David Tolnayf1e57f02019-05-08 15:30:39 -0700174 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700175 })
176 } else {
David Tolnayf1e57f02019-05-08 15:30:39 -0700177 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700178 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700179 let mut _i = _i;
180 let span = _visitor.fold_span(_i.span());
181 _i.set_span(span);
182 });
183 }
David Tolnay4d918822019-05-08 17:05:52 -0700184 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700185 _i
186 });
187 }
188 }
David Tolnay12597912019-05-08 17:02:44 -0700189 Data::Private => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700190 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700191 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700192 let mut _i = _i;
193 let span = _visitor.fold_span(_i.span());
194 _i.set_span(span);
195 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700196 }
David Tolnay4d918822019-05-08 17:05:52 -0700197 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700198 _i
David Tolnayf1e57f02019-05-08 15:30:39 -0700199 });
200 }
201 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700202
David Tolnay587abd02019-05-08 17:17:31 -0700203 let fold_span_only =
204 s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
205 if fold_span_only {
206 fold_impl = quote! {
207 let span = _visitor.fold_span(_i.span());
208 let mut _i = _i;
209 _i.set_span(span);
210 _i
211 };
212 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700213
David Tolnaycc8d14e2019-05-08 17:23:09 -0700214 traits.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700215 fn #fold_fn(&mut self, i: #ty) -> #ty {
216 #fold_fn(self, i)
217 }
218 });
219
David Tolnaycc8d14e2019-05-08 17:23:09 -0700220 impls.extend(quote! {
David Tolnay587abd02019-05-08 17:17:31 -0700221 pub fn #fold_fn<V: Fold + ?Sized>(
222 _visitor: &mut V, _i: #ty
223 ) -> #ty {
224 #fold_impl
225 }
226 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700227}
228
David Tolnayb2188a62019-05-09 11:42:44 -0700229pub fn generate(defs: &Definitions) -> Result<()> {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700230 let (traits, impls) = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700231 let full_macro = full::get_macro();
David Tolnayf1e57f02019-05-08 15:30:39 -0700232 file::write(
233 FOLD_SRC,
234 quote! {
235 // Unreachable code is generated sometimes without the full feature.
236 #![allow(unreachable_code)]
237
238 use *;
239 #[cfg(any(feature = "full", feature = "derive"))]
240 use token::{Brace, Bracket, Paren, Group};
241 use proc_macro2::Span;
242 #[cfg(any(feature = "full", feature = "derive"))]
243 use gen::helper::fold::*;
244
245 #full_macro
246
247 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
248 ///
249 /// See the [module documentation] for details.
250 ///
251 /// [module documentation]: index.html
252 ///
253 /// *This trait is available if Syn is built with the `"fold"` feature.*
254 pub trait Fold {
David Tolnaycc8d14e2019-05-08 17:23:09 -0700255 #traits
David Tolnayf1e57f02019-05-08 15:30:39 -0700256 }
257
David Tolnaycc8d14e2019-05-08 17:23:09 -0700258 #impls
David Tolnayf1e57f02019-05-08 15:30:39 -0700259 },
David Tolnayb2188a62019-05-09 11:42:44 -0700260 )?;
261 Ok(())
David Tolnayf1e57f02019-05-08 15:30:39 -0700262}