proc-macro-workshop:debug-8

审题

// 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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!