Skip to content

Commit

Permalink
Lift trait bounds from #[debug(skip)] fields (#283, #281)
Browse files Browse the repository at this point in the history
## Synopsis

> ```rs
> struct NoDebug;
>                                      
> #[derive(derive_more::Debug)]
> struct HasDebug<T>(#[debug(skip)] T);
>                                      
> dbg!(HasDebug(NoDebug));
> ```
>
> I'd be fine with needing to manually specify something like
`#[debug(bounds())]`.




## Solution

Fix how `ignore`/`skip` attributes are considered in bounding of
`derive(Debug)` macro.
  • Loading branch information
tyranron authored Jul 31, 2023
1 parent 77f8002 commit ad301a4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 27 deletions.
7 changes: 4 additions & 3 deletions impl/doc/debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ trait Trait { type Type; }
```

The following where clauses would be generated:
* `T1: Display + Pointer`
* `<T2 as Trait>::Type: Debug`
* `Bar<T3>: Display`
- `T1: Display`
- `<T2 as Trait>::Type: Display`
- `Vec<T3>: Debug`
- `&'a T1: Pointer`



Expand Down
29 changes: 11 additions & 18 deletions impl/src/fmt/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,26 +346,19 @@ impl<'a> Expansion<'a> {
self.fields.iter().try_fold(
self.attr.bounds.0.clone().into_iter().collect::<Vec<_>>(),
|mut out, field| {
let fmt_attr =
FieldAttribute::parse_attrs(&field.attrs)?.and_then(|attr| {
match attr {
FieldAttribute::Fmt(fmt) => Some(fmt),
FieldAttribute::Skip => None,
}
});
let ty = &field.ty;

if let Some(attr) = fmt_attr {
out.extend(attr.bounded_types(self.fields).map(
|(ty, trait_name)| {
let trait_name = format_ident!("{trait_name}");
parse_quote! { #ty: ::core::fmt::#trait_name }
},
));
} else {
out.extend([parse_quote! { #ty: ::core::fmt::Debug }]);
match FieldAttribute::parse_attrs(&field.attrs)? {
Some(FieldAttribute::Fmt(attr)) => {
out.extend(attr.bounded_types(self.fields).map(
|(ty, trait_name)| {
let trait_name = format_ident!("{trait_name}");
parse_quote! { #ty: ::core::fmt::#trait_name }
},
));
}
Some(FieldAttribute::Skip) => {}
None => out.extend([parse_quote! { #ty: ::core::fmt::Debug }]),
}

Ok(out)
},
)
Expand Down
58 changes: 52 additions & 6 deletions tests/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,17 +574,16 @@ mod enums {
mod generic {
#[cfg(not(feature = "std"))]
use alloc::format;
use core::fmt;

use derive_more::Debug;

struct NotDebug;

trait Bound {}

impl Bound for () {}

fn display_bound<T: Bound>(_: &T) -> &'static str {
"()"
impl fmt::Display for NotDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("NotDebug").finish()
}
}

#[derive(Debug)]
Expand All @@ -603,6 +602,23 @@ mod generic {
);
}

#[derive(Debug)]
struct NamedGenericStructIgnored<T> {
#[debug(ignore)]
field: T,
}
#[test]
fn named_generic_struct_ignored() {
assert_eq!(
format!("{:?}", NamedGenericStructIgnored { field: NotDebug }),
"NamedGenericStructIgnored { .. }",
);
assert_eq!(
format!("{:#?}", NamedGenericStructIgnored { field: NotDebug }),
"NamedGenericStructIgnored { .. }",
);
}

#[derive(Debug)]
struct InterpolatedNamedGenericStruct<T> {
#[debug("{field}.{}", field)]
Expand All @@ -618,6 +634,14 @@ mod generic {
format!("{:#?}", InterpolatedNamedGenericStruct { field: 1 }),
"InterpolatedNamedGenericStruct {\n field: 1.1,\n}",
);
assert_eq!(
format!("{:?}", InterpolatedNamedGenericStruct { field: NotDebug }),
"InterpolatedNamedGenericStruct { field: NotDebug.NotDebug }",
);
assert_eq!(
format!("{:#?}", InterpolatedNamedGenericStruct { field: NotDebug }),
"InterpolatedNamedGenericStruct {\n field: NotDebug.NotDebug,\n}",
);
}

#[derive(Debug)]
Expand Down Expand Up @@ -722,6 +746,20 @@ mod generic {
);
}

#[derive(Debug)]
struct UnnamedGenericStructIgnored<T>(#[debug(skip)] T);
#[test]
fn unnamed_generic_struct_ignored() {
assert_eq!(
format!("{:?}", UnnamedGenericStructIgnored(NotDebug)),
"UnnamedGenericStructIgnored(..)",
);
assert_eq!(
format!("{:#?}", UnnamedGenericStructIgnored(NotDebug)),
"UnnamedGenericStructIgnored(..)",
);
}

#[derive(Debug)]
struct InterpolatedUnnamedGenericStruct<T>(#[debug("{}.{_0}", _0)] T);
#[test]
Expand All @@ -734,6 +772,14 @@ mod generic {
format!("{:#?}", InterpolatedUnnamedGenericStruct(2)),
"InterpolatedUnnamedGenericStruct(\n 2.2,\n)",
);
assert_eq!(
format!("{:?}", InterpolatedUnnamedGenericStruct(NotDebug)),
"InterpolatedUnnamedGenericStruct(NotDebug.NotDebug)",
);
assert_eq!(
format!("{:#?}", InterpolatedUnnamedGenericStruct(NotDebug)),
"InterpolatedUnnamedGenericStruct(\n NotDebug.NotDebug,\n)",
);
}

#[derive(Debug)]
Expand Down

0 comments on commit ad301a4

Please sign in to comment.