derivative/
debug.rs

1use proc_macro2;
2
3use ast;
4use attr;
5use matcher;
6use syn;
7use syn::spanned::Spanned;
8use utils;
9
10pub 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
115fn 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`.
120fn 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`.
129fn 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`.
138fn 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
146fn 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}