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