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