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 | |
10 | 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 | |
115 | 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`. |
120 | 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`. |
129 | 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`. |
138 | 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 | |
146 | 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 | |