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::>(); 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) }; ) }