mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			151 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use proc_macro2::Ident;
 | |
| use quote::quote;
 | |
| use syn::parse_quote;
 | |
| 
 | |
| struct Attributes {
 | |
|     ignore: bool,
 | |
|     project: Option<Ident>,
 | |
| }
 | |
| 
 | |
| fn parse_attributes(field: &syn::Field) -> Attributes {
 | |
|     let mut attrs = Attributes { ignore: false, project: None };
 | |
|     for attr in &field.attrs {
 | |
|         let meta = &attr.meta;
 | |
|         if !meta.path().is_ident("stable_hasher") {
 | |
|             continue;
 | |
|         }
 | |
|         let mut any_attr = false;
 | |
|         let _ = attr.parse_nested_meta(|nested| {
 | |
|             if nested.path.is_ident("ignore") {
 | |
|                 attrs.ignore = true;
 | |
|                 any_attr = true;
 | |
|             }
 | |
|             if nested.path.is_ident("project") {
 | |
|                 let _ = nested.parse_nested_meta(|meta| {
 | |
|                     if attrs.project.is_none() {
 | |
|                         attrs.project = meta.path.get_ident().cloned();
 | |
|                     }
 | |
|                     any_attr = true;
 | |
|                     Ok(())
 | |
|                 });
 | |
|             }
 | |
|             Ok(())
 | |
|         });
 | |
|         if !any_attr {
 | |
|             panic!("error parsing stable_hasher");
 | |
|         }
 | |
|     }
 | |
|     attrs
 | |
| }
 | |
| 
 | |
| pub(crate) fn hash_stable_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
 | |
|     hash_stable_derive_with_mode(s, HashStableMode::Normal)
 | |
| }
 | |
| 
 | |
| pub(crate) fn hash_stable_generic_derive(
 | |
|     s: synstructure::Structure<'_>,
 | |
| ) -> proc_macro2::TokenStream {
 | |
|     hash_stable_derive_with_mode(s, HashStableMode::Generic)
 | |
| }
 | |
| 
 | |
| pub(crate) fn hash_stable_no_context_derive(
 | |
|     s: synstructure::Structure<'_>,
 | |
| ) -> proc_macro2::TokenStream {
 | |
|     hash_stable_derive_with_mode(s, HashStableMode::NoContext)
 | |
| }
 | |
| 
 | |
| enum HashStableMode {
 | |
|     // Use the query-system aware stable hashing context.
 | |
|     Normal,
 | |
|     // Emit a generic implementation that uses a crate-local `StableHashingContext`
 | |
|     // trait, when the crate is upstream of `rustc_middle`.
 | |
|     Generic,
 | |
|     // Emit a hash-stable implementation that takes no context,
 | |
|     // and emits per-field where clauses for (almost-)perfect derives.
 | |
|     NoContext,
 | |
| }
 | |
| 
 | |
| fn hash_stable_derive_with_mode(
 | |
|     mut s: synstructure::Structure<'_>,
 | |
|     mode: HashStableMode,
 | |
| ) -> proc_macro2::TokenStream {
 | |
|     let generic: syn::GenericParam = match mode {
 | |
|         HashStableMode::Normal => parse_quote!('__ctx),
 | |
|         HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX),
 | |
|     };
 | |
| 
 | |
|     s.underscore_const(true);
 | |
| 
 | |
|     // no_context impl is able to derive by-field, which is closer to a perfect derive.
 | |
|     s.add_bounds(match mode {
 | |
|         HashStableMode::Normal | HashStableMode::Generic => synstructure::AddBounds::Generics,
 | |
|         HashStableMode::NoContext => synstructure::AddBounds::Fields,
 | |
|     });
 | |
| 
 | |
|     // For generic impl, add `where __CTX: HashStableContext`.
 | |
|     match mode {
 | |
|         HashStableMode::Normal => {}
 | |
|         HashStableMode::Generic => {
 | |
|             s.add_where_predicate(parse_quote! { __CTX: crate::HashStableContext });
 | |
|         }
 | |
|         HashStableMode::NoContext => {}
 | |
|     }
 | |
| 
 | |
|     s.add_impl_generic(generic);
 | |
| 
 | |
|     let discriminant = hash_stable_discriminant(&mut s);
 | |
|     let body = hash_stable_body(&mut s);
 | |
| 
 | |
|     let context: syn::Type = match mode {
 | |
|         HashStableMode::Normal => {
 | |
|             parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>)
 | |
|         }
 | |
|         HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX),
 | |
|     };
 | |
| 
 | |
|     s.bound_impl(
 | |
|         quote!(
 | |
|             ::rustc_data_structures::stable_hasher::HashStable<
 | |
|                 #context
 | |
|             >
 | |
|         ),
 | |
|         quote! {
 | |
|             #[inline]
 | |
|             fn hash_stable(
 | |
|                 &self,
 | |
|                 __hcx: &mut #context,
 | |
|                 __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) {
 | |
|                 #discriminant
 | |
|                 match *self { #body }
 | |
|             }
 | |
|         },
 | |
|     )
 | |
| }
 | |
| 
 | |
| fn hash_stable_discriminant(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream {
 | |
|     match s.ast().data {
 | |
|         syn::Data::Enum(_) => quote! {
 | |
|             ::std::mem::discriminant(self).hash_stable(__hcx, __hasher);
 | |
|         },
 | |
|         syn::Data::Struct(_) => quote! {},
 | |
|         syn::Data::Union(_) => panic!("cannot derive on union"),
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn hash_stable_body(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream {
 | |
|     s.each(|bi| {
 | |
|         let attrs = parse_attributes(bi.ast());
 | |
|         if attrs.ignore {
 | |
|             quote! {}
 | |
|         } else if let Some(project) = attrs.project {
 | |
|             quote! {
 | |
|                 (&#bi.#project).hash_stable(__hcx, __hasher);
 | |
|             }
 | |
|         } else {
 | |
|             quote! {
 | |
|                 #bi.hash_stable(__hcx, __hasher);
 | |
|             }
 | |
|         }
 | |
|     })
 | |
| }
 | 
