Add support for generics to #[derive(ContentHash)]

#3054
This commit is contained in:
Evan Mesterhazy 2024-02-14 13:28:11 -05:00 committed by Evan Mesterhazy
parent a9f489ccdf
commit 8e1a6c708f
3 changed files with 38 additions and 7 deletions

View file

@ -1,7 +1,18 @@
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{Data, Fields, Index};
use syn::{parse_quote, Data, Fields, GenericParam, Generics, Index};
pub fn add_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param
.bounds
.push(parse_quote!(::jj_lib::content_hash::ContentHash));
}
}
generics
}
pub fn generate_hash_impl(data: &Data) -> TokenStream {
match *data {

View file

@ -18,10 +18,15 @@ pub fn derive_content_hash(input: proc_macro::TokenStream) -> proc_macro::TokenS
// Generate an expression to hash each of the fields in the struct.
let hash_impl = content_hash::generate_hash_impl(&input.data);
// Handle structs and enums with generics.
let generics = content_hash::add_trait_bounds(input.generics);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let expanded = quote! {
#[automatically_derived]
impl ::jj_lib::content_hash::ContentHash for #name {
fn hash(&self, state: &mut impl ::jj_lib::content_hash::DigestUpdate) {
impl #impl_generics ::jj_lib::content_hash::ContentHash for #name #ty_generics
#where_clause {
fn hash(&self, state: &mut impl digest::Update) {
#hash_impl
}
}

View file

@ -240,13 +240,28 @@ mod tests {
content_hash! {
struct Foo { x: Vec<Option<i32>>, y: i64 }
}
let foo_hash = hex::encode(hash(&Foo {
x: vec![None, Some(42)],
y: 17,
}));
insta::assert_snapshot!(
hex::encode(hash(&Foo {
x: vec![None, Some(42)],
y: 17
})),
foo_hash,
@"e33c423b4b774b1353c414e0f9ef108822fde2fd5113fcd53bf7bd9e74e3206690b96af96373f268ed95dd020c7cbe171c7b7a6947fcaf5703ff6c8e208cefd4"
);
// Try again with an equivalent generic struct deriving ContentHash.
#[derive(ContentHash)]
struct GenericFoo<X, Y> {
x: X,
y: Y,
}
assert_eq!(
hex::encode(hash(&GenericFoo {
x: vec![None, Some(42)],
y: 17i64
})),
foo_hash
);
}
// This will be removed once all uses of content_hash! are replaced by the