rustc_macros/
hash_stable.rs
1use proc_macro2::Ident;
2use quote::quote;
3use syn::parse_quote;
4
5struct Attributes {
6 ignore: bool,
7 project: Option<Ident>,
8}
9
10fn parse_attributes(field: &syn::Field) -> Attributes {
11 let mut attrs = Attributes { ignore: false, project: None };
12 for attr in &field.attrs {
13 let meta = &attr.meta;
14 if !meta.path().is_ident("stable_hasher") {
15 continue;
16 }
17 let mut any_attr = false;
18 let _ = attr.parse_nested_meta(|nested| {
19 if nested.path.is_ident("ignore") {
20 attrs.ignore = true;
21 any_attr = true;
22 }
23 if nested.path.is_ident("project") {
24 let _ = nested.parse_nested_meta(|meta| {
25 if attrs.project.is_none() {
26 attrs.project = meta.path.get_ident().cloned();
27 }
28 any_attr = true;
29 Ok(())
30 });
31 }
32 Ok(())
33 });
34 if !any_attr {
35 panic!("error parsing stable_hasher");
36 }
37 }
38 attrs
39}
40
41pub(crate) fn hash_stable_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
42 hash_stable_derive_with_mode(s, HashStableMode::Normal)
43}
44
45pub(crate) fn hash_stable_generic_derive(
46 s: synstructure::Structure<'_>,
47) -> proc_macro2::TokenStream {
48 hash_stable_derive_with_mode(s, HashStableMode::Generic)
49}
50
51pub(crate) fn hash_stable_no_context_derive(
52 s: synstructure::Structure<'_>,
53) -> proc_macro2::TokenStream {
54 hash_stable_derive_with_mode(s, HashStableMode::NoContext)
55}
56
57enum HashStableMode {
58 Normal,
60 Generic,
63 NoContext,
66}
67
68fn hash_stable_derive_with_mode(
69 mut s: synstructure::Structure<'_>,
70 mode: HashStableMode,
71) -> proc_macro2::TokenStream {
72 let generic: syn::GenericParam = match mode {
73 HashStableMode::Normal => parse_quote!('__ctx),
74 HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX),
75 };
76
77 s.underscore_const(true);
78
79 s.add_bounds(match mode {
81 HashStableMode::Normal | HashStableMode::Generic => synstructure::AddBounds::Generics,
82 HashStableMode::NoContext => synstructure::AddBounds::Fields,
83 });
84
85 match mode {
87 HashStableMode::Normal => {}
88 HashStableMode::Generic => {
89 s.add_where_predicate(parse_quote! { __CTX: crate::HashStableContext });
90 }
91 HashStableMode::NoContext => {}
92 }
93
94 s.add_impl_generic(generic);
95
96 let discriminant = hash_stable_discriminant(&mut s);
97 let body = hash_stable_body(&mut s);
98
99 let context: syn::Type = match mode {
100 HashStableMode::Normal => {
101 parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>)
102 }
103 HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX),
104 };
105
106 s.bound_impl(
107 quote!(
108 ::rustc_data_structures::stable_hasher::HashStable<
109 #context
110 >
111 ),
112 quote! {
113 #[inline]
114 fn hash_stable(
115 &self,
116 __hcx: &mut #context,
117 __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) {
118 #discriminant
119 match *self { #body }
120 }
121 },
122 )
123}
124
125fn hash_stable_discriminant(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream {
126 match s.ast().data {
127 syn::Data::Enum(_) => quote! {
128 ::std::mem::discriminant(self).hash_stable(__hcx, __hasher);
129 },
130 syn::Data::Struct(_) => quote! {},
131 syn::Data::Union(_) => panic!("cannot derive on union"),
132 }
133}
134
135fn hash_stable_body(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream {
136 s.each(|bi| {
137 let attrs = parse_attributes(bi.ast());
138 if attrs.ignore {
139 quote! {}
140 } else if let Some(project) = attrs.project {
141 quote! {
142 (&#bi.#project).hash_stable(__hcx, __hasher);
143 }
144 } else {
145 quote! {
146 #bi.hash_stable(__hcx, __hasher);
147 }
148 }
149 })
150}