blob: e0f49d1eeae2fae81dfe4f4fa26e98e21c4dd836 [file] [log] [blame]
David Tolnay1e99fa32019-05-08 16:18:36 -07001use crate::{file, full, gen};
David Tolnay2fb3d482019-05-08 16:22:10 -07002use proc_macro2::{Span, TokenStream};
3use quote::{quote, TokenStreamExt};
4use syn::*;
David Tolnayf1e57f02019-05-08 15:30:39 -07005use syn_codegen as types;
6
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(
24 ty: &types::Type,
25 features: &types::Features,
26 defs: &types::Definitions,
27 name: &TokenStream,
28) -> Option<TokenStream> {
29 match ty {
David Tolnaycd680542019-05-08 16:29:47 -070030 types::Type::Box(t) => {
31 let res = visit(t, features, defs, &quote!(*#name))?;
32 Some(quote! {
33 Box::new(#res)
34 })
35 }
36 types::Type::Vec(t) => {
37 let operand = quote!(it);
38 let val = visit(t, features, defs, &operand)?;
39 Some(quote! {
40 FoldHelper::lift(#name, |it| { #val })
41 })
42 }
43 types::Type::Punctuated(p) => {
44 let operand = quote!(it);
45 let val = visit(&p.element, features, defs, &operand)?;
46 Some(quote! {
47 FoldHelper::lift(#name, |it| { #val })
48 })
49 }
50 types::Type::Option(t) => {
51 let it = quote!(it);
52 let val = visit(t, features, defs, &it)?;
53 Some(quote! {
54 (#name).map(|it| { #val })
55 })
56 }
57 types::Type::Tuple(t) => {
58 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);
63 code.append_all(val);
64 code.append_all(quote!(,));
65 }
66 Some(quote! {
67 (#code)
68 })
69 }
David Tolnay2fb3d482019-05-08 16:22:10 -070070 types::Type::Token(t) => {
71 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 Tolnaycd680542019-05-08 16:29:47 -070083 types::Type::Group(t) => {
84 let ty = Ident::new(t, Span::call_site());
85 Some(quote! {
86 #ty(tokens_helper(_visitor, &#name.span))
87 })
88 }
David Tolnay2fb3d482019-05-08 16:22:10 -070089 types::Type::Syn(t) => {
90 fn requires_full(features: &types::Features) -> bool {
91 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 Tolnay2fb3d482019-05-08 16:22:10 -0700100 types::Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
101 types::Type::Ext(_) | types::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 Tolnay2fb3d482019-05-08 16:22:10 -0700105fn visit_features(features: &types::Features) -> TokenStream {
106 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 Tolnay2fb3d482019-05-08 16:22:10 -0700114fn node(state: &mut State, s: &types::Node, defs: &types::Definitions) {
115 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 {
123 types::Data::Enum(variants) => {
124 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() {
130 fold_variants.append_all(quote! {
131 #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
143 bind_fold_fields.append_all(quote! {
144 #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
149 fold_fields.append_all(
150 visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
151 );
152
153 fold_fields.append_all(quote!(,));
David Tolnayf1e57f02019-05-08 15:30:39 -0700154 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700155
156 fold_variants.append_all(quote! {
157 #ty::#variant_ident(#bind_fold_fields) => {
158 #ty::#variant_ident(
159 #fold_fields
160 )
161 }
162 });
163 }
164 }
165
166 fold_impl.append_all(quote! {
167 match _i {
168 #fold_variants
169 }
170 });
171 }
172 types::Data::Struct(fields) => {
173 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
180 fold_fields.append_all(quote! {
181 #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() {
186 fold_impl.append_all(quote! {
187 #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" {
193 fold_impl.append_all(quote! {
194 let mut _i = _i;
195 let span = _visitor.fold_span(_i.span());
196 _i.set_span(span);
197 });
198 }
199 fold_impl.append_all(quote! {
200 _i
201 });
202 }
203 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700204 types::Data::Private => {
205 if ty == "Ident" {
206 fold_impl.append_all(quote! {
207 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 Tolnay2fb3d482019-05-08 16:22:10 -0700212 fold_impl.append_all(quote! {
213 _i
David Tolnayf1e57f02019-05-08 15:30:39 -0700214 });
215 }
216 }
David Tolnay2fb3d482019-05-08 16:22:10 -0700217
218 let include_fold_impl = match &s.data {
219 types::Data::Private => gen::TERMINAL_TYPES.contains(&s.ident.as_str()),
220 types::Data::Struct(_) | types::Data::Enum(_) => true,
221 };
222
223 state.fold_trait.append_all(quote! {
224 #features
225 fn #fold_fn(&mut self, i: #ty) -> #ty {
226 #fold_fn(self, i)
227 }
228 });
229
230 if include_fold_impl {
231 state.fold_impl.append_all(quote! {
232 #features
233 pub fn #fold_fn<V: Fold + ?Sized>(
234 _visitor: &mut V, _i: #ty
235 ) -> #ty {
236 #fold_impl
237 }
238 });
239 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700240}
241
David Tolnayf1e57f02019-05-08 15:30:39 -0700242pub fn generate(defs: &types::Definitions) {
David Tolnay2fb3d482019-05-08 16:22:10 -0700243 let state = gen::traverse(defs, node);
David Tolnay1db3d8e2019-05-08 16:10:25 -0700244 let full_macro = full::get_macro();
David Tolnayf1e57f02019-05-08 15:30:39 -0700245 let fold_trait = state.fold_trait;
246 let fold_impl = state.fold_impl;
247 file::write(
248 FOLD_SRC,
249 quote! {
250 // Unreachable code is generated sometimes without the full feature.
251 #![allow(unreachable_code)]
252
253 use *;
254 #[cfg(any(feature = "full", feature = "derive"))]
255 use token::{Brace, Bracket, Paren, Group};
256 use proc_macro2::Span;
257 #[cfg(any(feature = "full", feature = "derive"))]
258 use gen::helper::fold::*;
259
260 #full_macro
261
262 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
263 ///
264 /// See the [module documentation] for details.
265 ///
266 /// [module documentation]: index.html
267 ///
268 /// *This trait is available if Syn is built with the `"fold"` feature.*
269 pub trait Fold {
270 #fold_trait
271 }
272
273 #[cfg(any(feature = "full", feature = "derive"))]
274 macro_rules! fold_span_only {
275 ($f:ident : $t:ident) => {
276 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
277 let span = _visitor.fold_span(_i.span());
278 _i.set_span(span);
279 _i
280 }
281 }
282 }
283
284 #[cfg(any(feature = "full", feature = "derive"))]
285 fold_span_only!(fold_lit_byte: LitByte);
286 #[cfg(any(feature = "full", feature = "derive"))]
287 fold_span_only!(fold_lit_byte_str: LitByteStr);
288 #[cfg(any(feature = "full", feature = "derive"))]
289 fold_span_only!(fold_lit_char: LitChar);
290 #[cfg(any(feature = "full", feature = "derive"))]
291 fold_span_only!(fold_lit_float: LitFloat);
292 #[cfg(any(feature = "full", feature = "derive"))]
293 fold_span_only!(fold_lit_int: LitInt);
294 #[cfg(any(feature = "full", feature = "derive"))]
295 fold_span_only!(fold_lit_str: LitStr);
296
297 #fold_impl
298 },
299 );
300}