blob: 8f13f80f225017f5ea292c0f501815331d87ce15 [file] [log] [blame]
use proc_macro2;
use ast;
use attr;
use matcher;
use paths;
use syn;
use utils;
pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream {
let hasher_trait_path = hasher_trait_path();
let hash_trait_path = hash_trait_path();
let discriminant = if let ast::Body::Enum(_) = input.body {
let discriminant = paths::discriminant_path();
Some(quote!(
#hash_trait_path::hash(&#discriminant(self), __state);
))
} else {
None
};
let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms(
input,
"__arg",
|_, _, _, _, _, bis| {
let field_prints = bis.iter().filter_map(|bi| {
if bi.field.attrs.ignore_hash() {
return None;
}
let arg = &bi.expr;
if let Some(hash_with) = bi.field.attrs.hash_with() {
Some(quote! {
#hash_with(&#arg, __state);
})
} else {
Some(quote! {
#hash_trait_path::hash(&#arg, __state);
})
}
});
quote! {
#(#field_prints)*
}
},
);
let name = &input.ident;
let generics = utils::build_impl_generics(
input,
&hash_trait_path,
needs_hash_bound,
|field| field.hash_bound(),
|input| input.hash_bound(),
);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let hasher_ty_parameter = utils::hygienic_type_parameter(input, "__H");
quote! {
#[allow(unused_qualifications)]
impl #impl_generics #hash_trait_path for #name #ty_generics #where_clause {
fn hash<#hasher_ty_parameter>(&self, __state: &mut #hasher_ty_parameter)
where #hasher_ty_parameter: #hasher_trait_path
{
#discriminant
match *self {
#body
}
}
}
}
}
fn needs_hash_bound(attrs: &attr::Field) -> bool {
!attrs.ignore_hash() && attrs.hash_bound().is_none()
}
/// Return the path of the `Hash` trait, that is `::std::hash::Hash`.
fn hash_trait_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::hash::Hash)
} else {
parse_quote!(::std::hash::Hash)
}
}
/// Return the path of the `Hasher` trait, that is `::std::hash::Hasher`.
fn hasher_trait_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::hash::Hasher)
} else {
parse_quote!(::std::hash::Hasher)
}
}