1fad3a1d3Sopenharmony_ciuse crate::{cfg, file, lookup}; 2fad3a1d3Sopenharmony_ciuse anyhow::Result; 3fad3a1d3Sopenharmony_ciuse proc_macro2::{Ident, Span, TokenStream}; 4fad3a1d3Sopenharmony_ciuse quote::{format_ident, quote}; 5fad3a1d3Sopenharmony_ciuse syn_codegen::{Data, Definitions, Node, Type}; 6fad3a1d3Sopenharmony_ci 7fad3a1d3Sopenharmony_ciconst CLONE_SRC: &str = "src/gen/clone.rs"; 8fad3a1d3Sopenharmony_ci 9fad3a1d3Sopenharmony_cifn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream { 10fad3a1d3Sopenharmony_ci let type_name = &node.ident; 11fad3a1d3Sopenharmony_ci let ident = Ident::new(type_name, Span::call_site()); 12fad3a1d3Sopenharmony_ci 13fad3a1d3Sopenharmony_ci match &node.data { 14fad3a1d3Sopenharmony_ci Data::Enum(variants) if variants.is_empty() => quote!(match *self {}), 15fad3a1d3Sopenharmony_ci Data::Enum(variants) => { 16fad3a1d3Sopenharmony_ci let arms = variants.iter().map(|(variant_name, fields)| { 17fad3a1d3Sopenharmony_ci let variant = Ident::new(variant_name, Span::call_site()); 18fad3a1d3Sopenharmony_ci if fields.is_empty() { 19fad3a1d3Sopenharmony_ci quote! { 20fad3a1d3Sopenharmony_ci #ident::#variant => #ident::#variant, 21fad3a1d3Sopenharmony_ci } 22fad3a1d3Sopenharmony_ci } else { 23fad3a1d3Sopenharmony_ci let mut pats = Vec::new(); 24fad3a1d3Sopenharmony_ci let mut clones = Vec::new(); 25fad3a1d3Sopenharmony_ci for i in 0..fields.len() { 26fad3a1d3Sopenharmony_ci let pat = format_ident!("v{}", i); 27fad3a1d3Sopenharmony_ci clones.push(quote!(#pat.clone())); 28fad3a1d3Sopenharmony_ci pats.push(pat); 29fad3a1d3Sopenharmony_ci } 30fad3a1d3Sopenharmony_ci let mut cfg = None; 31fad3a1d3Sopenharmony_ci if node.ident == "Expr" { 32fad3a1d3Sopenharmony_ci if let Type::Syn(ty) = &fields[0] { 33fad3a1d3Sopenharmony_ci if !lookup::node(defs, ty).features.any.contains("derive") { 34fad3a1d3Sopenharmony_ci cfg = Some(quote!(#[cfg(feature = "full")])); 35fad3a1d3Sopenharmony_ci } 36fad3a1d3Sopenharmony_ci } 37fad3a1d3Sopenharmony_ci } 38fad3a1d3Sopenharmony_ci quote! { 39fad3a1d3Sopenharmony_ci #cfg 40fad3a1d3Sopenharmony_ci #ident::#variant(#(#pats),*) => #ident::#variant(#(#clones),*), 41fad3a1d3Sopenharmony_ci } 42fad3a1d3Sopenharmony_ci } 43fad3a1d3Sopenharmony_ci }); 44fad3a1d3Sopenharmony_ci let nonexhaustive = if node.ident == "Expr" { 45fad3a1d3Sopenharmony_ci Some(quote! { 46fad3a1d3Sopenharmony_ci #[cfg(not(feature = "full"))] 47fad3a1d3Sopenharmony_ci _ => unreachable!(), 48fad3a1d3Sopenharmony_ci }) 49fad3a1d3Sopenharmony_ci } else { 50fad3a1d3Sopenharmony_ci None 51fad3a1d3Sopenharmony_ci }; 52fad3a1d3Sopenharmony_ci quote! { 53fad3a1d3Sopenharmony_ci match self { 54fad3a1d3Sopenharmony_ci #(#arms)* 55fad3a1d3Sopenharmony_ci #nonexhaustive 56fad3a1d3Sopenharmony_ci } 57fad3a1d3Sopenharmony_ci } 58fad3a1d3Sopenharmony_ci } 59fad3a1d3Sopenharmony_ci Data::Struct(fields) => { 60fad3a1d3Sopenharmony_ci let fields = fields.keys().map(|f| { 61fad3a1d3Sopenharmony_ci let ident = Ident::new(f, Span::call_site()); 62fad3a1d3Sopenharmony_ci quote! { 63fad3a1d3Sopenharmony_ci #ident: self.#ident.clone(), 64fad3a1d3Sopenharmony_ci } 65fad3a1d3Sopenharmony_ci }); 66fad3a1d3Sopenharmony_ci quote!(#ident { #(#fields)* }) 67fad3a1d3Sopenharmony_ci } 68fad3a1d3Sopenharmony_ci Data::Private => unreachable!(), 69fad3a1d3Sopenharmony_ci } 70fad3a1d3Sopenharmony_ci} 71fad3a1d3Sopenharmony_ci 72fad3a1d3Sopenharmony_cifn expand_impl(defs: &Definitions, node: &Node) -> TokenStream { 73fad3a1d3Sopenharmony_ci let manual_clone = node.data == Data::Private || node.ident == "Lifetime"; 74fad3a1d3Sopenharmony_ci if manual_clone { 75fad3a1d3Sopenharmony_ci return TokenStream::new(); 76fad3a1d3Sopenharmony_ci } 77fad3a1d3Sopenharmony_ci 78fad3a1d3Sopenharmony_ci let ident = Ident::new(&node.ident, Span::call_site()); 79fad3a1d3Sopenharmony_ci let cfg_features = cfg::features(&node.features, "clone-impls"); 80fad3a1d3Sopenharmony_ci 81fad3a1d3Sopenharmony_ci let copy = node.ident == "AttrStyle" 82fad3a1d3Sopenharmony_ci || node.ident == "BinOp" 83fad3a1d3Sopenharmony_ci || node.ident == "RangeLimits" 84fad3a1d3Sopenharmony_ci || node.ident == "TraitBoundModifier" 85fad3a1d3Sopenharmony_ci || node.ident == "UnOp"; 86fad3a1d3Sopenharmony_ci if copy { 87fad3a1d3Sopenharmony_ci return quote! { 88fad3a1d3Sopenharmony_ci #cfg_features 89fad3a1d3Sopenharmony_ci impl Copy for #ident {} 90fad3a1d3Sopenharmony_ci #cfg_features 91fad3a1d3Sopenharmony_ci impl Clone for #ident { 92fad3a1d3Sopenharmony_ci fn clone(&self) -> Self { 93fad3a1d3Sopenharmony_ci *self 94fad3a1d3Sopenharmony_ci } 95fad3a1d3Sopenharmony_ci } 96fad3a1d3Sopenharmony_ci }; 97fad3a1d3Sopenharmony_ci } 98fad3a1d3Sopenharmony_ci 99fad3a1d3Sopenharmony_ci let body = expand_impl_body(defs, node); 100fad3a1d3Sopenharmony_ci 101fad3a1d3Sopenharmony_ci quote! { 102fad3a1d3Sopenharmony_ci #cfg_features 103fad3a1d3Sopenharmony_ci impl Clone for #ident { 104fad3a1d3Sopenharmony_ci fn clone(&self) -> Self { 105fad3a1d3Sopenharmony_ci #body 106fad3a1d3Sopenharmony_ci } 107fad3a1d3Sopenharmony_ci } 108fad3a1d3Sopenharmony_ci } 109fad3a1d3Sopenharmony_ci} 110fad3a1d3Sopenharmony_ci 111fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> { 112fad3a1d3Sopenharmony_ci let mut impls = TokenStream::new(); 113fad3a1d3Sopenharmony_ci for node in &defs.types { 114fad3a1d3Sopenharmony_ci impls.extend(expand_impl(defs, node)); 115fad3a1d3Sopenharmony_ci } 116fad3a1d3Sopenharmony_ci 117fad3a1d3Sopenharmony_ci file::write( 118fad3a1d3Sopenharmony_ci CLONE_SRC, 119fad3a1d3Sopenharmony_ci quote! { 120fad3a1d3Sopenharmony_ci #![allow(clippy::clone_on_copy, clippy::expl_impl_clone_on_copy)] 121fad3a1d3Sopenharmony_ci 122fad3a1d3Sopenharmony_ci use crate::*; 123fad3a1d3Sopenharmony_ci 124fad3a1d3Sopenharmony_ci #impls 125fad3a1d3Sopenharmony_ci }, 126fad3a1d3Sopenharmony_ci )?; 127fad3a1d3Sopenharmony_ci 128fad3a1d3Sopenharmony_ci Ok(()) 129fad3a1d3Sopenharmony_ci} 130