1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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()
}
fn hash_trait_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::hash::Hash)
} else {
parse_quote!(::std::hash::Hash)
}
}
fn hasher_trait_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::hash::Hasher)
} else {
parse_quote!(::std::hash::Hasher)
}
}