derivative/
hash.rs

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}