1 | use proc_macro2; |
2 | |
3 | use ast; |
4 | use attr; |
5 | use matcher; |
6 | use paths; |
7 | use syn; |
8 | use utils; |
9 | |
10 | pub 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 | |
77 | fn 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`. |
82 | fn 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`. |
91 | fn 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 | |