proc-macro-workshop:debug-8

godme發表於2022-07-04
// 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中,涉及派生宏的就是兩道題:builderdebug
換句話說,完成這兩道題,我們應該對於派生宏的工作原理應該有所瞭解。
對於陌生的派生宏,我們應該大致知道它的工作方式和實現邏輯了。

這也算部分的達成了最初的目的,對於#[derive(xxx)]不再恐懼。但是對於#[xxx]還有一定的路程,接下來就是seq,也就是#[xxx]的自定義語法了。

再接再厲!

本作品採用《CC 協議》,轉載必須註明作者和本文連結