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}
226