use proc_macro2; use ast; use attr; use matcher; use syn; use utils; /// Derive `Copy` for `input`. pub fn derive_copy(input: &ast::Input) -> proc_macro2::TokenStream { let name = &input.ident; let copy_trait_path = copy_trait_path(); let generics = utils::build_impl_generics( input, ©_trait_path, |attrs| attrs.copy_bound().is_none(), |field| field.copy_bound(), |input| input.copy_bound(), ); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { #[allow(unused_qualifications)] impl #impl_generics #copy_trait_path for #name #ty_generics #where_clause {} } } /// Derive `Clone` for `input`. pub fn derive_clone(input: &ast::Input) -> proc_macro2::TokenStream { let name = &input.ident; let clone_trait_path = clone_trait_path(); let generics = utils::build_impl_generics( input, &clone_trait_path, needs_clone_bound, |field| field.clone_bound(), |input| input.clone_bound(), ); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let is_copy = input.attrs.copy.is_some(); if is_copy && input.generics.type_params().count() == 0 { quote! { #[allow(unused_qualifications)] impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause { fn clone(&self) -> Self { *self } } } } else { let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms( input, "__arg", |arm_path, _, _, style, _, bis| { let field_clones = bis.iter().map(|bi| { let arg = &bi.expr; let clone = if let Some(clone_with) = bi.field.attrs.clone_with() { quote!(#clone_with(&#arg)) } else { quote!(#arg.clone()) }; if let Some(ref name) = bi.field.ident { quote! { #name: #clone } } else { clone } }); match style { ast::Style::Struct => { quote! { #arm_path { #(#field_clones),* } } } ast::Style::Tuple => { quote! { #arm_path (#(#field_clones),*) } } ast::Style::Unit => { quote! { #arm_path } } } }, ); let clone_from = if input.attrs.clone_from() { Some( matcher::Matcher::new(matcher::BindingStyle::RefMut, input.attrs.is_packed).build_arms( input, "__arg", |outer_arm_path, _, _, _, _, outer_bis| { let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms( input, "__other", |inner_arm_path, _, _, _, _, inner_bis| { if outer_arm_path == inner_arm_path { let field_clones = outer_bis.iter().zip(inner_bis).map( |(outer_bi, inner_bi)| { let outer = &outer_bi.expr; let inner = &inner_bi.expr; quote!(#outer.clone_from(&#inner);) }, ); quote! { #(#field_clones)* return; } } else { quote!() } }, ); quote! { match *other { #body } } }, ), ) } else { None }; let clone_from = clone_from.map(|body| { // Enumerations are only cloned-from if both variants are the same. // If they are different, fallback to normal cloning. let fallback = if let ast::Body::Enum(_) = input.body { Some(quote!(*self = other.clone();)) } else { None }; quote! { #[allow(clippy::needless_return)] fn clone_from(&mut self, other: &Self) { match *self { #body } #fallback } } }); quote! { #[allow(unused_qualifications)] impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause { fn clone(&self) -> Self { match *self { #body } } #clone_from } } } } fn needs_clone_bound(attrs: &attr::Field) -> bool { attrs.clone_bound().is_none() } /// Return the path of the `Clone` trait, that is `::std::clone::Clone`. fn clone_trait_path() -> syn::Path { if cfg!(feature = "use_core") { parse_quote!(::core::clone::Clone) } else { parse_quote!(::std::clone::Clone) } } /// Return the path of the `Copy` trait, that is `::std::marker::Copy`. fn copy_trait_path() -> syn::Path { if cfg!(feature = "use_core") { parse_quote!(::core::marker::Copy) } else { parse_quote!(::std::marker::Copy) } }