-
Notifications
You must be signed in to change notification settings - Fork 348
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a procedural macro to derive the ContentHash trait for structs
This is a no-op in terms of function, but provides a nicer way to derive the ContentHash trait for structs using the `#[derive(ContentHash)]` syntax used for other traits such as `Debug`. This commit only adds the macro. A subsequent commit will replace uses of `content_hash!{}` with `#[derive(ContentHash)]`. The new macro generates nice error messages, just like the old macro: ``` error[E0277]: the trait bound `NotImplemented: content_hash::ContentHash` is not satisfied --> lib/src/content_hash.rs:265:16 | 265 | z: NotImplemented, | ^^^^^^^^^^^^^^ the trait `content_hash::ContentHash` is not implemented for `NotImplemented` | = help: the following other types implement trait `content_hash::ContentHash`: bool i32 i64 u8 u32 u64 std::collections::HashMap<K, V> BTreeMap<K, V> and 38 others ``` This commit does two things to make proc macros re-exported by jj_lib useable by deps: 1. jj_lib needs to be able refer to itself as `jj_lib` which it does by adding an `extern crate self as jj_lib` declaration. 2. jj_lib::content_hash needs to re-export the `digest::Update` type so that users of jj_lib can use the `#[derive(ContentHash)]` proc macro without directly depending on the digest crate. This is done by re-exporting it as `DigestUpdate`. #3054
- Loading branch information
1 parent
a80d018
commit c77a476
Showing
11 changed files
with
162 additions
and
25 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "jj-lib-proc-macros" | ||
publish = false | ||
|
||
version = { workspace = true } | ||
edition = { workspace = true } | ||
license = { workspace = true } | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
proc-macro2 = { workspace = true } | ||
quote = { workspace = true } | ||
syn = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{quote, quote_spanned}; | ||
use syn::spanned::Spanned; | ||
use syn::{Data, Fields, Index}; | ||
|
||
pub fn generate_hash_impl(data: &Data) -> TokenStream { | ||
match *data { | ||
Data::Struct(ref data) => match data.fields { | ||
Fields::Named(ref fields) => { | ||
let hash_statements = fields.named.iter().map(|f| { | ||
let field_name = &f.ident; | ||
let ty = &f.ty; | ||
quote_spanned! {ty.span()=> | ||
<#ty as ::jj_lib::content_hash::ContentHash>::hash(&self.#field_name, state); | ||
} | ||
}); | ||
quote! { | ||
#(#hash_statements)* | ||
} | ||
} | ||
Fields::Unnamed(ref fields) => { | ||
let hash_statements = fields.unnamed.iter().enumerate().map(|(i, f)| { | ||
let index = Index::from(i); | ||
let ty = &f.ty; | ||
quote_spanned! {ty.span() => | ||
<#ty as ::jj_lib::content_hash::ContentHash>::hash(&self.#index, state); | ||
} | ||
}); | ||
quote! { | ||
#(#hash_statements)* | ||
} | ||
} | ||
Fields::Unit => { | ||
quote! {} | ||
} | ||
}, | ||
_ => unimplemented!("ContentHash can only be derived for structs."), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
mod content_hash; | ||
|
||
extern crate proc_macro; | ||
|
||
use quote::quote; | ||
use syn::{parse_macro_input, DeriveInput}; | ||
|
||
/// Derives the `ContentHash` trait for a struct by calling `ContentHash::hash` | ||
/// on each of the struct members in the order that they're declared. All | ||
/// members of the struct must implement the `ContentHash` trait. | ||
#[proc_macro_derive(ContentHash)] | ||
pub fn derive_content_hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream { | ||
let input = parse_macro_input!(input as DeriveInput); | ||
|
||
// The name of the struct. | ||
let name = &input.ident; | ||
|
||
// Generate an expression to hash each of the fields in the struct. | ||
let hash_impl = content_hash::generate_hash_impl(&input.data); | ||
|
||
let expanded = quote! { | ||
#[automatically_derived] | ||
impl ::jj_lib::content_hash::ContentHash for #name { | ||
fn hash(&self, state: &mut impl ::jj_lib::content_hash::DigestUpdate) { | ||
#hash_impl | ||
} | ||
} | ||
}; | ||
expanded.into() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.