| use proc_macro2::Ident; | |
| use quote::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_hash") { | |
| 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_hash"); | |
| } | |
| } | |
| attrs | |
| } | |
| pub(crate) fn stable_hash_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { | |
| stable_hash_derive_with_mode(s, StableHashMode::Normal) | |
| } | |
| pub(crate) fn stable_hash_no_context_derive( | |
| s: synstructure::Structure<'_>, | |
| ) -> proc_macro2::TokenStream { | |
| stable_hash_derive_with_mode(s, StableHashMode::NoContext) | |
| } | |
| enum StableHashMode { | |
| // Do a normal derive, where any generic type parameter gets a `StableHash` bound. | |
| // For example, in `struct Abc<T, U>(T, U)` the added bounds are `T: StableHash` and | |
| // `U: StableHash`. | |
| Normal, | |
| // Do an (almost-)perfect derive, where any field with a generic type parameter gets a | |
| // `StableHash` bound. For example, in `struct Def<T, U>(T, U::Assoc)` the added bounds are | |
| // `T::StableHash` and `U::Assoc: StableHash` (not `U: StableHash`). | |
| // | |
| // This is used most commonly in `rustc_type_ir` for types like `TyKind<I: Interner>`. | |
| // `Interner` does not impl `StableHash`, but the fields of `TyKind` do not use `I` itself, | |
| // instead only using associated types from `I` such as `I::Region`. On types like `TyKind` we | |
| // typically also see the use of `derive_where` for built-in traits such as `Debug`. | |
| NoContext, | |
| } | |
| fn stable_hash_derive_with_mode( | |
| mut s: synstructure::Structure<'_>, | |
| mode: StableHashMode, | |
| ) -> proc_macro2::TokenStream { | |
| let add_bounds = match mode { | |
| StableHashMode::Normal => synstructure::AddBounds::Generics, | |
| StableHashMode::NoContext => synstructure::AddBounds::Fields, | |
| }; | |
| s.add_bounds(add_bounds); | |
| let discriminant = stable_hash_discriminant(&mut s); | |
| let body = stable_hash_body(&mut s); | |
| s.bound_impl( | |
| quote!(::rustc_data_structures::stable_hash::StableHash), | |
| quote! { | |
| #[inline] | |
| fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>( | |
| &self, | |
| __hcx: &mut __Hcx, | |
| __hasher: &mut ::rustc_data_structures::stable_hash::StableHasher | |
| ) { | |
| #discriminant | |
| match *self { #body } | |
| } | |
| }, | |
| ) | |
| } | |
| fn stable_hash_discriminant(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream { | |
| match s.ast().data { | |
| syn::Data::Enum(_) => quote! { | |
| ::std::mem::discriminant(self).stable_hash(__hcx, __hasher); | |
| }, | |
| syn::Data::Struct(_) => quote! {}, | |
| syn::Data::Union(_) => panic!("cannot derive on union"), | |
| } | |
| } | |
| fn stable_hash_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).stable_hash(__hcx, __hasher); | |
| } | |
| } else { | |
| quote! { | |
| #bi.stable_hash(__hcx, __hasher); | |
| } | |
| } | |
| }) | |
| } |