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