1use proc_macro2;
2
3use ast;
4use attr;
5use matcher;
6use paths;
7use syn;
8use utils;
9
10pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream {
11 let hasher_trait_path = hasher_trait_path();
12 let hash_trait_path = hash_trait_path();
13
14 let discriminant = if let ast::Body::Enum(_) = input.body {
15 let discriminant = paths::discriminant_path();
16 Some(quote!(
17 #hash_trait_path::hash(&#discriminant(self), __state);
18 ))
19 } else {
20 None
21 };
22
23 let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms(
24 input,
25 "__arg",
26 |_, _, _, _, _, bis| {
27 let field_prints = bis.iter().filter_map(|bi| {
28 if bi.field.attrs.ignore_hash() {
29 return None;
30 }
31
32 let arg = &bi.expr;
33
34 if let Some(hash_with) = bi.field.attrs.hash_with() {
35 Some(quote! {
36 #hash_with(&#arg, __state);
37 })
38 } else {
39 Some(quote! {
40 #hash_trait_path::hash(&#arg, __state);
41 })
42 }
43 });
44
45 quote! {
46 #(#field_prints)*
47 }
48 },
49 );
50
51 let name = &input.ident;
52 let generics = utils::build_impl_generics(
53 input,
54 &hash_trait_path,
55 needs_hash_bound,
56 |field| field.hash_bound(),
57 |input| input.hash_bound(),
58 );
59 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
60
61 let hasher_ty_parameter = utils::hygienic_type_parameter(input, "__H");
62 quote! {
63 #[allow(unused_qualifications)]
64 impl #impl_generics #hash_trait_path for #name #ty_generics #where_clause {
65 fn hash<#hasher_ty_parameter>(&self, __state: &mut #hasher_ty_parameter)
66 where #hasher_ty_parameter: #hasher_trait_path
67 {
68 #discriminant
69 match *self {
70 #body
71 }
72 }
73 }
74 }
75}
76
77fn needs_hash_bound(attrs: &attr::Field) -> bool {
78 !attrs.ignore_hash() && attrs.hash_bound().is_none()
79}
80
81/// Return the path of the `Hash` trait, that is `::std::hash::Hash`.
82fn hash_trait_path() -> syn::Path {
83 if cfg!(feature = "use_core") {
84 parse_quote!(::core::hash::Hash)
85 } else {
86 parse_quote!(::std::hash::Hash)
87 }
88}
89
90/// Return the path of the `Hasher` trait, that is `::std::hash::Hasher`.
91fn hasher_trait_path() -> syn::Path {
92 if cfg!(feature = "use_core") {
93 parse_quote!(::core::hash::Hasher)
94 } else {
95 parse_quote!(::std::hash::Hasher)
96 }
97}
98