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中,涉及派生宏的就是两道题:builder
和debug
。
换句话说,完成这两道题,我们应该对于派生宏的工作原理应该有所了解。
对于陌生的派生宏,我们应该大致知道它的工作方式和实现逻辑了。
这也算部分的达成了最初的目的,对于#[derive(xxx)]
不再恐惧。但是对于#[xxx]
还有一定的路程,接下来就是seq
,也就是#[xxx]
的自定义语法了。
再接再厉!
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: