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