derive_more_impl/fmt/
debug.rs

1//! Implementation of a [`fmt::Debug`] derive macro.
2//!
3//! [`fmt::Debug`]: std::fmt::Debug
4
5use proc_macro2::TokenStream;
6use quote::{format_ident, quote};
7use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned as _};
8
9use crate::utils::{
10    attr::{self, ParseMultiple as _},
11    Either, Spanning,
12};
13
14use super::{
15    trait_name_to_attribute_name, ContainerAttributes, ContainsGenericsExt as _,
16    FmtAttribute,
17};
18
19/// Expands a [`fmt::Debug`] derive macro.
20///
21/// [`fmt::Debug`]: std::fmt::Debug
22pub fn expand(input: &syn::DeriveInput, _: &str) -> syn::Result<TokenStream> {
23    let attr_name = format_ident!("{}", trait_name_to_attribute_name("Debug"));
24
25    let attrs = ContainerAttributes::parse_attrs(&input.attrs, &attr_name)?
26        .map(Spanning::into_inner)
27        .unwrap_or_default();
28    let ident = &input.ident;
29
30    let type_params = input
31        .generics
32        .params
33        .iter()
34        .filter_map(|p| match p {
35            syn::GenericParam::Type(t) => Some(&t.ident),
36            syn::GenericParam::Const(..) | syn::GenericParam::Lifetime(..) => None,
37        })
38        .collect::<Vec<_>>();
39
40    let (bounds, body) = match &input.data {
41        syn::Data::Struct(s) => {
42            expand_struct(attrs, ident, s, &type_params, &attr_name)
43        }
44        syn::Data::Enum(e) => expand_enum(attrs, e, &type_params, &attr_name),
45        syn::Data::Union(_) => {
46            return Err(syn::Error::new(
47                input.span(),
48                "`Debug` cannot be derived for unions",
49            ));
50        }
51    }?;
52
53    let (impl_gens, ty_gens, where_clause) = {
54        let (impl_gens, ty_gens, where_clause) = input.generics.split_for_impl();
55        let mut where_clause = where_clause
56            .cloned()
57            .unwrap_or_else(|| parse_quote! { where });
58        where_clause.predicates.extend(bounds);
59        (impl_gens, ty_gens, where_clause)
60    };
61
62    Ok(quote! {
63        #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types
64        #[automatically_derived]
65        impl #impl_gens derive_more::core::fmt::Debug for #ident #ty_gens #where_clause {
66            #[inline]
67            fn fmt(
68                &self, __derive_more_f: &mut derive_more::core::fmt::Formatter<'_>
69            ) -> derive_more::core::fmt::Result {
70                #body
71            }
72        }
73    })
74}
75
76/// Expands a [`fmt::Debug`] derive macro for the provided struct.
77///
78/// [`fmt::Debug`]: std::fmt::Debug
79fn expand_struct(
80    attrs: ContainerAttributes,
81    ident: &syn::Ident,
82    s: &syn::DataStruct,
83    type_params: &[&syn::Ident],
84    attr_name: &syn::Ident,
85) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> {
86    let s = Expansion {
87        attr: &attrs,
88        fields: &s.fields,
89        type_params,
90        ident,
91        attr_name,
92    };
93    s.validate_attrs()?;
94    let bounds = s.generate_bounds()?;
95    let body = s.generate_body()?;
96
97    let vars = s.fields.iter().enumerate().map(|(i, f)| {
98        let var = f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"));
99        let member = f
100            .ident
101            .clone()
102            .map_or_else(|| syn::Member::Unnamed(i.into()), syn::Member::Named);
103        quote! { let #var = &self.#member; }
104    });
105
106    let body = quote! {
107        #( #vars )*
108        #body
109    };
110
111    Ok((bounds, body))
112}
113
114/// Expands a [`fmt::Debug`] derive macro for the provided enum.
115///
116/// [`fmt::Debug`]: std::fmt::Debug
117fn expand_enum(
118    mut attrs: ContainerAttributes,
119    e: &syn::DataEnum,
120    type_params: &[&syn::Ident],
121    attr_name: &syn::Ident,
122) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> {
123    if let Some(enum_fmt) = attrs.fmt.as_ref() {
124        return Err(syn::Error::new_spanned(
125            enum_fmt,
126            format!(
127                "`#[{attr_name}(\"...\", ...)]` attribute is not allowed on enum, place it on its \
128                 variants instead",
129            ),
130        ));
131    }
132
133    let (bounds, match_arms) = e.variants.iter().try_fold(
134        (Vec::new(), TokenStream::new()),
135        |(mut bounds, mut arms), variant| {
136            let ident = &variant.ident;
137
138            attrs.fmt = variant
139                .attrs
140                .iter()
141                .filter(|attr| attr.path().is_ident("debug"))
142                .try_fold(None, |mut attrs, attr| {
143                    let attr = attr.parse_args::<FmtAttribute>()?;
144                    attrs.replace(attr).map_or(Ok(()), |dup| {
145                        Err(syn::Error::new(
146                            dup.span(),
147                            format!(
148                                "multiple `#[{attr_name}(\"...\", ...)]` attributes aren't allowed",
149                            ),
150                        ))
151                    })?;
152                    Ok::<_, syn::Error>(attrs)
153                })?;
154
155            let v = Expansion {
156                attr: &attrs,
157                fields: &variant.fields,
158                type_params,
159                ident,
160                attr_name,
161            };
162            v.validate_attrs()?;
163            let arm_body = v.generate_body()?;
164            bounds.extend(v.generate_bounds()?);
165
166            let fields_idents =
167                variant.fields.iter().enumerate().map(|(i, f)| {
168                    f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))
169                });
170            let matcher = match variant.fields {
171                syn::Fields::Named(_) => {
172                    quote! { Self::#ident { #( #fields_idents ),* } }
173                }
174                syn::Fields::Unnamed(_) => {
175                    quote! { Self::#ident ( #( #fields_idents ),* ) }
176                }
177                syn::Fields::Unit => quote! { Self::#ident },
178            };
179
180            arms.extend([quote! { #matcher => { #arm_body }, }]);
181
182            Ok::<_, syn::Error>((bounds, arms))
183        },
184    )?;
185
186    let body = match_arms
187        .is_empty()
188        .then(|| quote! { match *self {} })
189        .unwrap_or_else(|| quote! { match self { #match_arms } });
190
191    Ok((bounds, body))
192}
193
194/// Representation of a [`fmt::Debug`] derive macro field attribute.
195///
196/// ```rust,ignore
197/// #[debug(skip)]
198/// #[debug("<fmt-literal>", <fmt-args>)]
199/// ```
200///
201/// [`fmt::Debug`]: std::fmt::Debug
202type FieldAttribute = Either<attr::Skip, FmtAttribute>;
203
204/// Helper struct to generate [`Debug::fmt()`] implementation body and trait
205/// bounds for a struct or an enum variant.
206///
207/// [`Debug::fmt()`]: std::fmt::Debug::fmt()
208#[derive(Debug)]
209struct Expansion<'a> {
210    attr: &'a ContainerAttributes,
211
212    /// Struct or enum [`Ident`](struct@syn::Ident).
213    ident: &'a syn::Ident,
214
215    /// Struct or enum [`syn::Fields`].
216    fields: &'a syn::Fields,
217
218    /// Type parameters in this struct or enum.
219    type_params: &'a [&'a syn::Ident],
220
221    /// Name of the attributes, considered by this macro.
222    attr_name: &'a syn::Ident,
223}
224
225impl Expansion<'_> {
226    /// Validates attributes of this [`Expansion`] to be consistent.
227    fn validate_attrs(&self) -> syn::Result<()> {
228        if self.attr.fmt.is_some() {
229            for field_attr in self
230                .fields
231                .iter()
232                .map(|f| FieldAttribute::parse_attrs(&f.attrs, self.attr_name))
233            {
234                if let Some(FieldAttribute::Right(fmt_attr)) =
235                    field_attr?.map(Spanning::into_inner)
236                {
237                    return Err(syn::Error::new_spanned(
238                        fmt_attr,
239                        "`#[debug(...)]` attributes are not allowed on fields when \
240                         `#[debug(\"...\", ...)]` is specified on struct or variant",
241                    ));
242                }
243            }
244        }
245        Ok(())
246    }
247
248    /// Generates [`Debug::fmt()`] implementation for a struct or an enum variant.
249    ///
250    /// [`Debug::fmt()`]: std::fmt::Debug::fmt()
251    fn generate_body(&self) -> syn::Result<TokenStream> {
252        if let Some(fmt) = &self.attr.fmt {
253            return Ok(
254                if let Some((expr, trait_ident)) =
255                    fmt.transparent_call_on_fields(self.fields)
256                {
257                    quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) }
258                } else {
259                    let deref_args = fmt.additional_deref_args(self.fields);
260
261                    quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) }
262                },
263            );
264        };
265
266        match self.fields {
267            syn::Fields::Unit => {
268                let ident = self.ident.to_string();
269                Ok(quote! {
270                    derive_more::core::fmt::Formatter::write_str(
271                        __derive_more_f,
272                        #ident,
273                    )
274                })
275            }
276            syn::Fields::Unnamed(unnamed) => {
277                let mut exhaustive = true;
278                let ident_str = self.ident.to_string();
279
280                let out = quote! {
281                    &mut derive_more::__private::debug_tuple(
282                        __derive_more_f,
283                        #ident_str,
284                    )
285                };
286                let out = unnamed.unnamed.iter().enumerate().try_fold(
287                    out,
288                    |out, (i, field)| match FieldAttribute::parse_attrs(
289                        &field.attrs,
290                        self.attr_name,
291                    )?
292                    .map(Spanning::into_inner)
293                    {
294                        Some(FieldAttribute::Left(_skip)) => {
295                            exhaustive = false;
296                            Ok::<_, syn::Error>(out)
297                        }
298                        Some(FieldAttribute::Right(fmt_attr)) => {
299                            let deref_args = fmt_attr.additional_deref_args(self.fields);
300
301                            Ok(quote! {
302                                derive_more::__private::DebugTuple::field(
303                                    #out,
304                                    &derive_more::core::format_args!(#fmt_attr, #(#deref_args),*),
305                                )
306                            })
307                        }
308                        None => {
309                            let ident = format_ident!("_{i}");
310                            Ok(quote! {
311                                derive_more::__private::DebugTuple::field(#out, &#ident)
312                            })
313                        }
314                    },
315                )?;
316                Ok(if exhaustive {
317                    quote! { derive_more::__private::DebugTuple::finish(#out) }
318                } else {
319                    quote! { derive_more::__private::DebugTuple::finish_non_exhaustive(#out) }
320                })
321            }
322            syn::Fields::Named(named) => {
323                let mut exhaustive = true;
324                let ident = self.ident.to_string();
325
326                let out = quote! {
327                    &mut derive_more::core::fmt::Formatter::debug_struct(
328                        __derive_more_f,
329                        #ident,
330                    )
331                };
332                let out = named.named.iter().try_fold(out, |out, field| {
333                    let field_ident = field.ident.as_ref().unwrap_or_else(|| {
334                        unreachable!("`syn::Fields::Named`");
335                    });
336                    let field_str = field_ident.unraw().to_string();
337                    match FieldAttribute::parse_attrs(&field.attrs, self.attr_name)?
338                        .map(Spanning::into_inner)
339                    {
340                        Some(FieldAttribute::Left(_skip)) => {
341                            exhaustive = false;
342                            Ok::<_, syn::Error>(out)
343                        }
344                        Some(FieldAttribute::Right(fmt_attr)) => {
345                            let deref_args =
346                                fmt_attr.additional_deref_args(self.fields);
347
348                            Ok(quote! {
349                                derive_more::core::fmt::DebugStruct::field(
350                                    #out,
351                                    #field_str,
352                                    &derive_more::core::format_args!(
353                                        #fmt_attr, #(#deref_args),*
354                                    ),
355                                )
356                            })
357                        }
358                        None => Ok(quote! {
359                            derive_more::core::fmt::DebugStruct::field(
360                                #out, #field_str, &#field_ident
361                            )
362                        }),
363                    }
364                })?;
365                Ok(if exhaustive {
366                    quote! { derive_more::core::fmt::DebugStruct::finish(#out) }
367                } else {
368                    quote! { derive_more::core::fmt::DebugStruct::finish_non_exhaustive(#out) }
369                })
370            }
371        }
372    }
373
374    /// Generates trait bounds for a struct or an enum variant.
375    fn generate_bounds(&self) -> syn::Result<Vec<syn::WherePredicate>> {
376        let mut out = self.attr.bounds.0.clone().into_iter().collect::<Vec<_>>();
377
378        if let Some(fmt) = self.attr.fmt.as_ref() {
379            out.extend(fmt.bounded_types(self.fields).filter_map(
380                |(ty, trait_name)| {
381                    if !ty.contains_generics(self.type_params) {
382                        return None;
383                    }
384
385                    let trait_ident = format_ident!("{trait_name}");
386
387                    Some(parse_quote! { #ty: derive_more::core::fmt::#trait_ident })
388                },
389            ));
390            Ok(out)
391        } else {
392            self.fields.iter().try_fold(out, |mut out, field| {
393                let ty = &field.ty;
394
395                if !ty.contains_generics(self.type_params) {
396                    return Ok(out);
397                }
398
399                match FieldAttribute::parse_attrs(&field.attrs, self.attr_name)?
400                    .map(Spanning::into_inner)
401                {
402                    Some(FieldAttribute::Right(fmt_attr)) => {
403                        out.extend(fmt_attr.bounded_types(self.fields).map(
404                            |(ty, trait_name)| {
405                                let trait_ident = format_ident!("{trait_name}");
406
407                                parse_quote! { #ty: derive_more::core::fmt::#trait_ident }
408                            },
409                        ));
410                    }
411                    Some(FieldAttribute::Left(_skip)) => {}
412                    None => out.extend([parse_quote! { #ty: derive_more::core::fmt::Debug }]),
413                }
414                Ok(out)
415            })
416        }
417    }
418}