1 use proc_macro2;
2 
3 use ast;
4 use attr;
5 use matcher;
6 use syn;
7 use syn::spanned::Spanned;
8 use utils;
9 
derive(input: &ast::Input) -> proc_macro2::TokenStream10 pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream {
11     let debug_trait_path = debug_trait_path();
12     let fmt_path = fmt_path();
13 
14     let formatter = quote_spanned! {input.span=> __f};
15 
16     let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed)
17         .with_field_filter(|f: &ast::Field| !f.attrs.ignore_debug())
18         .build_arms(input, "__arg", |_, _, arm_name, style, attrs, bis| {
19             let field_prints = bis.iter().filter_map(|bi| {
20                 if bi.field.attrs.ignore_debug() {
21                     return None;
22                 }
23 
24                 if attrs.debug_transparent() {
25                     return Some(quote_spanned! {arm_name.span()=>
26                         #debug_trait_path::fmt(__arg_0, #formatter)
27                     });
28                 }
29 
30                 let arg_expr = &bi.expr;
31                 let arg_ident = &bi.ident;
32 
33                 let dummy_debug = bi.field.attrs.debug_format_with().map(|format_fn| {
34                     format_with(
35                         bi.field,
36                         &input.attrs.debug_bound(),
37                         &arg_expr,
38                         &arg_ident,
39                         format_fn,
40                         input.generics.clone(),
41                     )
42                 });
43                 let expr = if bi.field.attrs.debug_format_with().is_some() {
44                     quote_spanned! {arm_name.span()=>
45                         &#arg_ident
46                     }
47                 } else {
48                     quote_spanned! {arm_name.span()=>
49                         &&#arg_expr
50                     }
51                 };
52 
53                 let builder = if let Some(ref name) = bi.field.ident {
54                     let name = name.to_string();
55                     quote_spanned! {arm_name.span()=>
56                         #dummy_debug
57                         let _ = __debug_trait_builder.field(#name, #expr);
58                     }
59                 } else {
60                     quote_spanned! {arm_name.span()=>
61                         #dummy_debug
62                         let _ = __debug_trait_builder.field(#expr);
63                     }
64                 };
65 
66                 Some(builder)
67             });
68 
69             let method = match style {
70                 ast::Style::Struct => "debug_struct",
71                 ast::Style::Tuple | ast::Style::Unit => "debug_tuple",
72             };
73             let method = syn::Ident::new(method, proc_macro2::Span::call_site());
74 
75             if attrs.debug_transparent() {
76                 quote_spanned! {arm_name.span()=>
77                     #(#field_prints)*
78                 }
79             } else {
80                 let name = arm_name.to_string();
81                 quote_spanned! {arm_name.span()=>
82                     let mut __debug_trait_builder = #formatter.#method(#name);
83                     #(#field_prints)*
84                     __debug_trait_builder.finish()
85                 }
86             }
87         });
88 
89     let name = &input.ident;
90 
91     let generics = utils::build_impl_generics(
92         input,
93         &debug_trait_path,
94         needs_debug_bound,
95         |field| field.debug_bound(),
96         |input| input.debug_bound(),
97     );
98     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
99 
100     // don't attach a span to prevent issue #58
101     let match_self = quote!(match *self);
102     quote_spanned! {input.span=>
103         #[allow(unused_qualifications)]
104         #[allow(clippy::unneeded_field_pattern)]
105         impl #impl_generics #debug_trait_path for #name #ty_generics #where_clause {
106             fn fmt(&self, #formatter: &mut #fmt_path::Formatter) -> #fmt_path::Result {
107                 #match_self {
108                     #body
109                 }
110             }
111         }
112     }
113 }
114 
needs_debug_bound(attrs: &attr::Field) -> bool115 fn needs_debug_bound(attrs: &attr::Field) -> bool {
116     !attrs.ignore_debug() && attrs.debug_bound().is_none()
117 }
118 
119 /// Return the path of the `Debug` trait, that is `::std::fmt::Debug`.
debug_trait_path() -> syn::Path120 fn debug_trait_path() -> syn::Path {
121     if cfg!(feature = "use_core") {
122         parse_quote!(::core::fmt::Debug)
123     } else {
124         parse_quote!(::std::fmt::Debug)
125     }
126 }
127 
128 /// Return the path of the `fmt` module, that is `::std::fmt`.
fmt_path() -> syn::Path129 fn fmt_path() -> syn::Path {
130     if cfg!(feature = "use_core") {
131         parse_quote!(::core::fmt)
132     } else {
133         parse_quote!(::std::fmt)
134     }
135 }
136 
137 /// Return the path of the `PhantomData` type, that is `::std::marker::PhantomData`.
phantom_path() -> syn::Path138 fn phantom_path() -> syn::Path {
139     if cfg!(feature = "use_core") {
140         parse_quote!(::core::marker::PhantomData)
141     } else {
142         parse_quote!(::std::marker::PhantomData)
143     }
144 }
145 
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::TokenStream146 fn format_with(
147     f: &ast::Field,
148     bounds: &Option<&[syn::WherePredicate]>,
149     arg_expr: &proc_macro2::TokenStream,
150     arg_ident: &syn::Ident,
151     format_fn: &syn::Path,
152     mut generics: syn::Generics,
153 ) -> proc_macro2::TokenStream {
154     let debug_trait_path = debug_trait_path();
155     let fmt_path = fmt_path();
156     let phantom_path = phantom_path();
157 
158     generics
159         .make_where_clause()
160         .predicates
161         .extend(f.attrs.debug_bound().unwrap_or(&[]).iter().cloned());
162 
163     generics
164         .params
165         .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
166             parse_quote!('_derivative),
167         )));
168     let where_predicates = generics
169         .type_params()
170         .map(|ty| {
171             let mut bounds = syn::punctuated::Punctuated::new();
172             bounds.push(syn::TypeParamBound::Lifetime(syn::Lifetime::new(
173                 "'_derivative",
174                 proc_macro2::Span::call_site(),
175             )));
176 
177             let path = syn::Path::from(syn::PathSegment::from(ty.ident.clone()));
178 
179             syn::WherePredicate::Type(syn::PredicateType {
180                 lifetimes: None,
181                 bounded_ty: syn::Type::Path(syn::TypePath { qself: None, path }),
182                 colon_token: Default::default(),
183                 bounds,
184             })
185         })
186         .chain(bounds.iter().flat_map(|b| b.iter().cloned()))
187         .collect::<Vec<_>>();
188     generics
189         .make_where_clause()
190         .predicates
191         .extend(where_predicates);
192 
193     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
194 
195     let ty = f.ty;
196 
197     // Leave off the type parameter bounds, defaults, and attributes
198     let phantom = generics.type_params().map(|tp| &tp.ident);
199 
200     let mut ctor_generics = generics.clone();
201     *ctor_generics
202         .lifetimes_mut()
203         .last()
204         .expect("There must be a '_derivative lifetime") = syn::LifetimeDef::new(parse_quote!('_));
205     let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl();
206     let ctor_ty_generics = ctor_ty_generics.as_turbofish();
207 
208     // don't attach a span to prevent issue #58
209     let match_self = quote!(match self.0);
210     quote_spanned!(format_fn.span()=>
211         let #arg_ident = {
212             struct Dummy #impl_generics (&'_derivative #ty, #phantom_path <(#(#phantom,)*)>) #where_clause;
213 
214             impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause {
215                 fn fmt(&self, __f: &mut #fmt_path::Formatter) -> #fmt_path::Result {
216                     #match_self {
217                         this => #format_fn(this, __f)
218                     }
219                 }
220             }
221 
222             Dummy #ctor_ty_generics (&&#arg_expr, #phantom_path)
223         };
224     )
225 }
226