blob: ed667c1d7a8fbceb0d75af1efc419489b8f6bec2 [file] [log] [blame]
use proc_macro2;
use ast;
use attr;
use matcher;
use syn;
use syn::spanned::Spanned;
use utils;
pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream {
let debug_trait_path = debug_trait_path();
let fmt_path = fmt_path();
let formatter = quote_spanned! {input.span=> __f};
let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed)
.with_field_filter(|f: &ast::Field| !f.attrs.ignore_debug())
.build_arms(input, "__arg", |_, _, arm_name, style, attrs, bis| {
let field_prints = bis.iter().filter_map(|bi| {
if bi.field.attrs.ignore_debug() {
return None;
}
if attrs.debug_transparent() {
return Some(quote_spanned! {arm_name.span()=>
#debug_trait_path::fmt(__arg_0, #formatter)
});
}
let arg_expr = &bi.expr;
let arg_ident = &bi.ident;
let dummy_debug = bi.field.attrs.debug_format_with().map(|format_fn| {
format_with(
bi.field,
&input.attrs.debug_bound(),
&arg_expr,
&arg_ident,
format_fn,
input.generics.clone(),
)
});
let expr = if bi.field.attrs.debug_format_with().is_some() {
quote_spanned! {arm_name.span()=>
&#arg_ident
}
} else {
quote_spanned! {arm_name.span()=>
&&#arg_expr
}
};
let builder = if let Some(ref name) = bi.field.ident {
let name = name.to_string();
quote_spanned! {arm_name.span()=>
#dummy_debug
let _ = __debug_trait_builder.field(#name, #expr);
}
} else {
quote_spanned! {arm_name.span()=>
#dummy_debug
let _ = __debug_trait_builder.field(#expr);
}
};
Some(builder)
});
let method = match style {
ast::Style::Struct => "debug_struct",
ast::Style::Tuple | ast::Style::Unit => "debug_tuple",
};
let method = syn::Ident::new(method, proc_macro2::Span::call_site());
if attrs.debug_transparent() {
quote_spanned! {arm_name.span()=>
#(#field_prints)*
}
} else {
let name = arm_name.to_string();
quote_spanned! {arm_name.span()=>
let mut __debug_trait_builder = #formatter.#method(#name);
#(#field_prints)*
__debug_trait_builder.finish()
}
}
});
let name = &input.ident;
let generics = utils::build_impl_generics(
input,
&debug_trait_path,
needs_debug_bound,
|field| field.debug_bound(),
|input| input.debug_bound(),
);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// don't attach a span to prevent issue #58
let match_self = quote!(match *self);
quote_spanned! {input.span=>
#[allow(unused_qualifications)]
#[allow(clippy::unneeded_field_pattern)]
impl #impl_generics #debug_trait_path for #name #ty_generics #where_clause {
fn fmt(&self, #formatter: &mut #fmt_path::Formatter) -> #fmt_path::Result {
#match_self {
#body
}
}
}
}
}
fn needs_debug_bound(attrs: &attr::Field) -> bool {
!attrs.ignore_debug() && attrs.debug_bound().is_none()
}
/// Return the path of the `Debug` trait, that is `::std::fmt::Debug`.
fn debug_trait_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::fmt::Debug)
} else {
parse_quote!(::std::fmt::Debug)
}
}
/// Return the path of the `fmt` module, that is `::std::fmt`.
fn fmt_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::fmt)
} else {
parse_quote!(::std::fmt)
}
}
/// Return the path of the `PhantomData` type, that is `::std::marker::PhantomData`.
fn phantom_path() -> syn::Path {
if cfg!(feature = "use_core") {
parse_quote!(::core::marker::PhantomData)
} else {
parse_quote!(::std::marker::PhantomData)
}
}
fn format_with(
f: &ast::Field,
bounds: &Option<&[syn::WherePredicate]>,
arg_expr: &proc_macro2::TokenStream,
arg_ident: &syn::Ident,
format_fn: &syn::Path,
mut generics: syn::Generics,
) -> proc_macro2::TokenStream {
let debug_trait_path = debug_trait_path();
let fmt_path = fmt_path();
let phantom_path = phantom_path();
generics
.make_where_clause()
.predicates
.extend(f.attrs.debug_bound().unwrap_or(&[]).iter().cloned());
generics
.params
.push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
parse_quote!('_derivative),
)));
let where_predicates = generics
.type_params()
.map(|ty| {
let mut bounds = syn::punctuated::Punctuated::new();
bounds.push(syn::TypeParamBound::Lifetime(syn::Lifetime::new(
"'_derivative",
proc_macro2::Span::call_site(),
)));
let path = syn::Path::from(syn::PathSegment::from(ty.ident.clone()));
syn::WherePredicate::Type(syn::PredicateType {
lifetimes: None,
bounded_ty: syn::Type::Path(syn::TypePath { qself: None, path }),
colon_token: Default::default(),
bounds,
})
})
.chain(bounds.iter().flat_map(|b| b.iter().cloned()))
.collect::<Vec<_>>();
generics
.make_where_clause()
.predicates
.extend(where_predicates);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let ty = f.ty;
// Leave off the type parameter bounds, defaults, and attributes
let phantom = generics.type_params().map(|tp| &tp.ident);
let mut ctor_generics = generics.clone();
*ctor_generics
.lifetimes_mut()
.last()
.expect("There must be a '_derivative lifetime") = syn::LifetimeDef::new(parse_quote!('_));
let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl();
let ctor_ty_generics = ctor_ty_generics.as_turbofish();
// don't attach a span to prevent issue #58
let match_self = quote!(match self.0);
quote_spanned!(format_fn.span()=>
let #arg_ident = {
struct Dummy #impl_generics (&'_derivative #ty, #phantom_path <(#(#phantom,)*)>) #where_clause;
impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause {
fn fmt(&self, __f: &mut #fmt_path::Formatter) -> #fmt_path::Result {
#match_self {
this => #format_fn(this, __f)
}
}
}
Dummy #ctor_ty_generics (&&#arg_expr, #phantom_path)
};
)
}