blob: dd0248918337c5058332c5d1b3fa20f0b671e462 [file] [log] [blame]
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,
&copy_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).build_arms(
input,
|arm_path, _, style, _, bis| {
let field_clones = bis.iter().map(|bi| {
let arg = &bi.ident;
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).build_arms(
input,
|outer_arm_path, _, _, _, outer_bis| {
let body = matcher::Matcher::new(matcher::BindingStyle::Ref)
.with_name("__other".into())
.build_arms(input, |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.ident;
let inner = &inner_bi.ident;
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! {
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)
}
}