blob: 87d4bebf97e90978c2389529181bfbd395827fad [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 -07009#[derive(Default)]
10struct State {
11 fold_trait: TokenStream,
12 fold_impl: TokenStream,
13}
David Tolnayf1e57f02019-05-08 15:30:39 -070014
David Tolnay2fb3d482019-05-08 16:22:10 -070015fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
David Tolnay9ee641a2019-05-08 17:00:16 -070016 let ident = gen::under_name(item);
David Tolnay2fb3d482019-05-08 16:22:10 -070017 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
18 quote! {
19 _visitor.#method(#name)
20 }
21}
22
David Tolnay2fb3d482019-05-08 16:22:10 -070023fn visit(
David Tolnay12597912019-05-08 17:02:44 -070024 ty: &Type,
25 features: &Features,
26 defs: &Definitions,
David Tolnay2fb3d482019-05-08 16:22:10 -070027 name: &TokenStream,
28) -> Option<TokenStream> {
29 match ty {
David Tolnay12597912019-05-08 17:02:44 -070030 Type::Box(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070031 let res = visit(t, features, defs, &quote!(*#name))?;
32 Some(quote! {
33 Box::new(#res)
34 })
35 }
David Tolnay12597912019-05-08 17:02:44 -070036 Type::Vec(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070037 let operand = quote!(it);
38 let val = visit(t, features, defs, &operand)?;
39 Some(quote! {
40 FoldHelper::lift(#name, |it| { #val })
41 })
42 }
David Tolnay12597912019-05-08 17:02:44 -070043 Type::Punctuated(p) => {
David Tolnaycd680542019-05-08 16:29:47 -070044 let operand = quote!(it);
45 let val = visit(&p.element, features, defs, &operand)?;
46 Some(quote! {
47 FoldHelper::lift(#name, |it| { #val })
48 })
49 }
David Tolnay12597912019-05-08 17:02:44 -070050 Type::Option(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070051 let it = quote!(it);
52 let val = visit(t, features, defs, &it)?;
53 Some(quote! {
54 (#name).map(|it| { #val })
55 })
56 }
David Tolnay12597912019-05-08 17:02:44 -070057 Type::Tuple(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070058 let mut code = TokenStream::new();
59 for (i, elem) in t.iter().enumerate() {
60 let i = Index::from(i);
61 let it = quote!((#name).#i);
62 let val = visit(elem, features, defs, &it).unwrap_or(it);
David Tolnay4d918822019-05-08 17:05:52 -070063 code.extend(val);
64 code.extend(quote!(,));
David Tolnaycd680542019-05-08 16:29:47 -070065 }
66 Some(quote! {
67 (#code)
68 })
69 }
David Tolnay12597912019-05-08 17:02:44 -070070 Type::Token(t) => {
David Tolnay2fb3d482019-05-08 16:22:10 -070071 let repr = &defs.tokens[t];
72 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
David Tolnaycd680542019-05-08 16:29:47 -070073 let spans = if is_keyword {
74 quote!(span)
David Tolnay2fb3d482019-05-08 16:22:10 -070075 } else {
David Tolnaycd680542019-05-08 16:29:47 -070076 quote!(spans)
77 };
78 let ty: TokenStream = syn::parse_str(&format!("Token![{}]", repr)).unwrap();
79 Some(quote! {
80 #ty(tokens_helper(_visitor, &#name.#spans))
81 })
David Tolnayf1e57f02019-05-08 15:30:39 -070082 }
David Tolnay12597912019-05-08 17:02:44 -070083 Type::Group(t) => {
David Tolnaycd680542019-05-08 16:29:47 -070084 let ty = Ident::new(t, Span::call_site());
85 Some(quote! {
86 #ty(tokens_helper(_visitor, &#name.span))
87 })
88 }
David Tolnay12597912019-05-08 17:02:44 -070089 Type::Syn(t) => {
90 fn requires_full(features: &Features) -> bool {
David Tolnay2fb3d482019-05-08 16:22:10 -070091 features.any.contains("full") && features.any.len() == 1
92 }
David Tolnaycd680542019-05-08 16:29:47 -070093 let mut res = simple_visit(t, name);
David Tolnay2fb3d482019-05-08 16:22:10 -070094 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
David Tolnaycd680542019-05-08 16:29:47 -070095 if requires_full(&target.features) && !requires_full(features) {
96 res = quote!(full!(#res));
97 }
98 Some(res)
David Tolnayf1e57f02019-05-08 15:30:39 -070099 }
David Tolnay12597912019-05-08 17:02:44 -0700100 Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
101 Type::Ext(_) | Type::Std(_) => None,
David Tolnayf1e57f02019-05-08 15:30:39 -0700102 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700103}
David Tolnayf1e57f02019-05-08 15:30:39 -0700104
David Tolnay12597912019-05-08 17:02:44 -0700105fn visit_features(features: &Features) -> TokenStream {
David Tolnay2fb3d482019-05-08 16:22:10 -0700106 let features = &features.any;
107 match features.len() {
108 0 => quote!(),
109 1 => quote!(#[cfg(feature = #(#features)*)]),
110 _ => quote!(#[cfg(any(#(feature = #features),*))]),
David Tolnayf1e57f02019-05-08 15:30:39 -0700111 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700112}
David Tolnayf1e57f02019-05-08 15:30:39 -0700113
David Tolnay12597912019-05-08 17:02:44 -0700114fn node(state: &mut State, s: &Node, defs: &Definitions) {
David Tolnay2fb3d482019-05-08 16:22:10 -0700115 let features = visit_features(&s.features);
David Tolnay9ee641a2019-05-08 17:00:16 -0700116 let under_name = gen::under_name(&s.ident);
David Tolnay2fb3d482019-05-08 16:22:10 -0700117 let ty = Ident::new(&s.ident, Span::call_site());
118 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700119
David Tolnay2fb3d482019-05-08 16:22:10 -0700120 let mut fold_impl = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700121
David Tolnay2fb3d482019-05-08 16:22:10 -0700122 match &s.data {
David Tolnay12597912019-05-08 17:02:44 -0700123 Data::Enum(variants) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700124 let mut fold_variants = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700125
David Tolnay2fb3d482019-05-08 16:22:10 -0700126 for (variant, fields) in variants {
127 let variant_ident = Ident::new(variant, Span::call_site());
David Tolnayf1e57f02019-05-08 15:30:39 -0700128
David Tolnay2fb3d482019-05-08 16:22:10 -0700129 if fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700130 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700131 #ty::#variant_ident => {
132 #ty::#variant_ident
David Tolnayf1e57f02019-05-08 15:30:39 -0700133 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700134 });
135 } else {
136 let mut bind_fold_fields = TokenStream::new();
137 let mut fold_fields = TokenStream::new();
David Tolnayf1e57f02019-05-08 15:30:39 -0700138
David Tolnay2fb3d482019-05-08 16:22:10 -0700139 for (idx, ty) in fields.iter().enumerate() {
140 let name = format!("_binding_{}", idx);
141 let binding = Ident::new(&name, Span::call_site());
142
David Tolnay4d918822019-05-08 17:05:52 -0700143 bind_fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700144 #binding,
David Tolnayf1e57f02019-05-08 15:30:39 -0700145 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700146
David Tolnay2fb3d482019-05-08 16:22:10 -0700147 let owned_binding = quote!(#binding);
148
David Tolnay4d918822019-05-08 17:05:52 -0700149 fold_fields.extend(
David Tolnay2fb3d482019-05-08 16:22:10 -0700150 visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
151 );
152
David Tolnay4d918822019-05-08 17:05:52 -0700153 fold_fields.extend(quote!(,));
David Tolnayf1e57f02019-05-08 15:30:39 -0700154 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700155
David Tolnay4d918822019-05-08 17:05:52 -0700156 fold_variants.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700157 #ty::#variant_ident(#bind_fold_fields) => {
158 #ty::#variant_ident(
159 #fold_fields
160 )
161 }
162 });
163 }
164 }
165
David Tolnay4d918822019-05-08 17:05:52 -0700166 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700167 match _i {
168 #fold_variants
169 }
170 });
171 }
David Tolnay12597912019-05-08 17:02:44 -0700172 Data::Struct(fields) => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700173 let mut fold_fields = TokenStream::new();
174
175 for (field, ty) in fields {
176 let id = Ident::new(&field, Span::call_site());
177 let ref_toks = quote!(_i.#id);
178 let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
179
David Tolnay4d918822019-05-08 17:05:52 -0700180 fold_fields.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700181 #id: #fold,
David Tolnayf1e57f02019-05-08 15:30:39 -0700182 });
183 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700184
David Tolnay2fb3d482019-05-08 16:22:10 -0700185 if !fields.is_empty() {
David Tolnay4d918822019-05-08 17:05:52 -0700186 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700187 #ty {
188 #fold_fields
David Tolnayf1e57f02019-05-08 15:30:39 -0700189 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700190 })
191 } else {
David Tolnayf1e57f02019-05-08 15:30:39 -0700192 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700193 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700194 let mut _i = _i;
195 let span = _visitor.fold_span(_i.span());
196 _i.set_span(span);
197 });
198 }
David Tolnay4d918822019-05-08 17:05:52 -0700199 fold_impl.extend(quote! {
David Tolnayf1e57f02019-05-08 15:30:39 -0700200 _i
201 });
202 }
203 }
David Tolnay12597912019-05-08 17:02:44 -0700204 Data::Private => {
David Tolnay2fb3d482019-05-08 16:22:10 -0700205 if ty == "Ident" {
David Tolnay4d918822019-05-08 17:05:52 -0700206 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700207 let mut _i = _i;
208 let span = _visitor.fold_span(_i.span());
209 _i.set_span(span);
210 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700211 }
David Tolnay4d918822019-05-08 17:05:52 -0700212 fold_impl.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700213 _i
David Tolnayf1e57f02019-05-08 15:30:39 -0700214 });
215 }
216 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700217
David Tolnay587abd02019-05-08 17:17:31 -0700218 let fold_span_only =
219 s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
220 if fold_span_only {
221 fold_impl = quote! {
222 let span = _visitor.fold_span(_i.span());
223 let mut _i = _i;
224 _i.set_span(span);
225 _i
226 };
227 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700228
David Tolnay4d918822019-05-08 17:05:52 -0700229 state.fold_trait.extend(quote! {
David Tolnay2fb3d482019-05-08 16:22:10 -0700230 #features
231 fn #fold_fn(&mut self, i: #ty) -> #ty {
232 #fold_fn(self, i)
233 }
234 });
235
David Tolnay587abd02019-05-08 17:17:31 -0700236 state.fold_impl.extend(quote! {
237 #features
238 pub fn #fold_fn<V: Fold + ?Sized>(
239 _visitor: &mut V, _i: #ty
240 ) -> #ty {
241 #fold_impl
242 }
243 });
David Tolnayf1e57f02019-05-08 15:30:39 -0700244}
245
David Tolnay12597912019-05-08 17:02:44 -0700246pub fn generate(defs: &Definitions) {
David Tolnay2fb3d482019-05-08 16:22:10 -0700247 let state = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700248 let full_macro = full::get_macro();
David Tolnayf1e57f02019-05-08 15:30:39 -0700249 let fold_trait = state.fold_trait;
250 let fold_impl = state.fold_impl;
251 file::write(
252 FOLD_SRC,
253 quote! {
254 // Unreachable code is generated sometimes without the full feature.
255 #![allow(unreachable_code)]
256
257 use *;
258 #[cfg(any(feature = "full", feature = "derive"))]
259 use token::{Brace, Bracket, Paren, Group};
260 use proc_macro2::Span;
261 #[cfg(any(feature = "full", feature = "derive"))]
262 use gen::helper::fold::*;
263
264 #full_macro
265
266 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
267 ///
268 /// See the [module documentation] for details.
269 ///
270 /// [module documentation]: index.html
271 ///
272 /// *This trait is available if Syn is built with the `"fold"` feature.*
273 pub trait Fold {
274 #fold_trait
275 }
276
David Tolnayf1e57f02019-05-08 15:30:39 -0700277 #fold_impl
278 },
279 );
280}