// There are some cases where no heuristic would be sufficient to infer the
// right trait bounds based only on the information available during macro
// expansion.
//
// When this happens, we'll turn to attributes as a way for the caller to
// handwrite the correct trait bounds themselves.
//
// The impl for Wrapper<T> in the code below will need to include the bounds
// provided in the `debug(bound = "...")` attribute. When such an attribute is
// present, also disable all inference of bounds so that the macro does not
// attach its own `T: Debug` inferred bound.
//
// impl<T: Trait> Debug for Wrapper<T>
// where
// T::Value: Debug,
// {...}
//
// Optionally, though this is not covered by the test suite, also accept
// `debug(bound = "...")` attributes on individual fields. This should
// substitute only whatever bounds are inferred based on that field's type,
// without removing bounds inferred based on the other fields:
//
// #[derive(CustomDebug)]
// pub struct Wrapper<T: Trait, U> {
// #[debug(bound = "T::Value: Debug")]
// field: Field<T>,
// normal: U,
// }
use derive_debug::CustomDebug;
use std::fmt::Debug;
pub trait Trait {
type Value;
}
#[derive(CustomDebug)]
#[debug(bound = "T::Value: Debug")]
pub struct Wrapper<T: Trait> {
field: Field<T>,
}
#[derive(CustomDebug)]
struct Field<T: Trait> {
values: Vec<T::Value>,
}
fn assert_debug<F: Debug>() {}
fn main() {
struct Id;
impl Trait for Id {
type Value = u8;
}
assert_debug::<Wrapper<Id>>();
}
智者千慮,必有一失。
因此總要有一個兜底,一個後門。
面對過於複雜的情況,邏輯可能存在漏洞,就乾脆給使用者一個後門,自己傳入自定的where_clause
。
額外工作就是繫結並解析一個#[debug(bound = "T:: Value: Debug")]
,這個在builder-7中已經做過了,這裡貼一下即可。
pub(crate) fn parse_customer_debug(
ast: &syn::DeriveInput,
) -> syn::Result<std::option::Option<std::string::String>> {
for attr in ast.attrs.iter() {
if let syn::Result::Ok(syn::Meta::List(syn::MetaList {
ref path,
ref nested,
..
})) = attr.parse_meta()
{ // 名稱校驗
if path.is_ident("debug") {
if let std::option::Option::Some(syn::NestedMeta::Meta(syn::Meta::NameValue(
syn::MetaNameValue {
ref path, ref lit, ..
},
))) = nested.first()
{ // 字首校驗
if path.is_ident("bound") {
if let syn::Lit::Str(ref customer_where_clause) = lit {
return syn::Result::Ok(std::option::Option::Some(
customer_where_clause.value().to_string(),
));
}
}
}
}
}
}
syn::Result::Ok(std::option::Option::None)
}
pub(super) fn solution(
fields: &crate::common::FieldsType,
origin_ident: &syn::Ident,
ast: &syn::DeriveInput,
) -> syn::Result<proc_macro2::TokenStream> {
let mut generics = crate::common::parse_generic_type(ast);
let customer_debug = crate::common::parse_customer_debug(ast)?;
// 如果沒有自定義的where_clause,走老邏輯
if customer_debug.is_none() {
let associated_type_name_map = crate::common::parse_generic_associated_types(ast);
let (field_generic_type_names, phantom_generic_type_names) = parse_type_names(fields)?;
for generic in generics.params.iter_mut() {
if let syn::GenericParam::Type(t) = generic {
let generic_name = t.ident.to_string();
if phantom_generic_type_names.contains(&generic_name)
&& !field_generic_type_names.contains(&generic_name)
{
continue;
}
if associated_type_name_map.contains_key(&generic_name)
&& !field_generic_type_names.contains(&generic_name)
{
continue;
}
t.bounds.push(syn::parse_quote!(std::fmt::Debug));
}
}
generics.make_where_clause();
for (_, associated_types) in associated_type_name_map {
for associated_type in associated_types {
generics
.where_clause
.as_mut()
.unwrap()
.predicates
.push(syn::parse_quote!(#associated_type: std::fmt::Debug));
}
}
} else { // 有自定義的where_clause,直接設定
generics.make_where_clause();
generics
.where_clause
.as_mut()
.unwrap()
.predicates
.push(syn::parse_str(customer_debug.unwrap().as_str()).unwrap());
}
// 老邏輯
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let origin_ident_string = origin_ident.to_string();
let fields_stream_vec = generate_field_stream_vec(fields)?;
syn::Result::Ok(quote::quote! {
impl #impl_generics std::fmt::Debug for #origin_ident #type_generics #where_clause {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(#origin_ident_string)
#(
#fields_stream_vec
)*
.finish()
}
}
})
}
// 枯燥的debug欄位設定,抽取
fn generate_field_stream_vec(
fields: &crate::common::FieldsType,
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
fields
.iter()
.map(|f| {
let ident = &f.ident;
let ident_string = ident.as_ref().unwrap().to_string();
let mut format = "{:?}".to_string();
if let std::option::Option::Some(customer_format) = crate::common::parse_format(f)? {
format = customer_format;
}
syn::Result::Ok(quote::quote! {
.field(#ident_string, &format_args!(#format, &self.#ident))
})
})
.collect()
}
// 兩個簡單的list,直接抽取出來,避免邏輯干擾
fn parse_type_names(fields: &crate::common::FieldsType) -> syn::Result<(Vec<String>, Vec<String>)> {
let mut field_generic_type_names = vec![];
let mut phantom_generic_type_names = vec![];
for field in fields.iter() {
if let std::option::Option::Some(field_generic_type_name) =
crate::common::parse_field_type_name(field)?
{
field_generic_type_names.push(field_generic_type_name);
}
if let std::option::Option::Some(phantom_generic_type_name) =
crate::common::parse_phantom_generic_type_name(field)?
{
phantom_generic_type_names.push(phantom_generic_type_name);
}
}
syn::Result::Ok((field_generic_type_names, phantom_generic_type_names))
}
mod common;
mod solution2;
mod solution3;
mod solution4;
mod solution56;
mod solution7;
mod solution8;
#[proc_macro_derive(CustomDebug, attributes(debug))]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as syn::DeriveInput);
match solution1(&ast) {
syn::Result::Ok(token_stream) => {
return proc_macro::TokenStream::from(token_stream);
}
syn::Result::Err(e) => {
return e.into_compile_error().into();
}
}
}
fn solution1(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let origin_ident = &ast.ident;
let fields = crate::common::parse_fields(&ast)?;
// soluton2
let _ = solution2::solution(fields, origin_ident)?;
let _ = solution3::solution(fields, origin_ident)?;
let _ = solution4::solution(fields, origin_ident, ast)?;
let _ = solution56::solution(fields, origin_ident, ast)?;
let token_stream = solution7::soution(fields, origin_ident, ast)?;
let _ = solution8::solution(fields, origin_ident, ast)?;
syn::Result::Ok(token_stream)
}
至此,debug
結束。
這裡的結束不僅僅是一道題的結束。
回顧一下宏的分類,刨除掉宣告宏macro_rules!
,過程宏包括
- 派生宏
- 函式式宏
- 屬性宏
在proc-macro-workshop中,涉及派生宏的就是兩道題:builder
和debug
。
換句話說,完成這兩道題,我們應該對於派生宏的工作原理應該有所瞭解。
對於陌生的派生宏,我們應該大致知道它的工作方式和實現邏輯了。
這也算部分的達成了最初的目的,對於#[derive(xxx)]
不再恐懼。但是對於#[xxx]
還有一定的路程,接下來就是seq
,也就是#[xxx]
的自定義語法了。
再接再厲!
本作品採用《CC 協議》,轉載必須註明作者和本文連結