From 8ae3f4c64337a49eb2f592a271dbcd49ebb92470 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sat, 23 Dec 2023 02:55:45 +0100 Subject: [PATCH 1/6] Showcase --- tests/debug.rs | 67 +++++++++++++++++++++++++++++- tests/display.rs | 103 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 138 insertions(+), 32 deletions(-) diff --git a/tests/debug.rs b/tests/debug.rs index 0c4b180a..8944bd42 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -102,7 +102,7 @@ mod structs { assert_eq!(format!("{:03?}", UpperHex), "00B"); assert_eq!(format!("{:07?}", LowerExp), "03.15e0"); assert_eq!(format!("{:07?}", UpperExp), "03.15E0"); - assert_eq!(format!("{:018?}", Pointer).len(), 18); + assert_eq!(format!("{:018?}", Pointer), format!("{POINTER:018p}")); } mod omitted { @@ -246,6 +246,35 @@ mod structs { "Struct {\n field: 0.0,\n}", ); } + + mod pointer { + #[cfg(not(feature = "std"))] + use alloc::format; + + use derive_more::Debug; + + #[derive(Debug)] + struct Tuple<'a>(#[debug("{_0:p}.{:p}", self.0)] &'a i32); + + #[derive(Debug)] + struct Struct<'a> { + #[debug("{field:p}.{:p}", self.field)] + field: &'a i32, + } + + #[test] + fn assert() { + let a = 42; + assert_eq!( + format!("{:?}", Tuple(&a)), + format!("Tuple({0:p}.{0:p})", &a), + ); + assert_eq!( + format!("{:?}", Struct { field: &a }), + format!("Struct {{ field: {0:p}.{0:p} }}", &a), + ); + } + } } mod ignore { @@ -527,6 +556,37 @@ mod structs { assert_eq!(format!("{:?}", Tuple(10, true)), "10 * true"); assert_eq!(format!("{:?}", Struct { a: 10, b: true }), "10 * true"); } + + mod pointer { + #[cfg(not(feature = "std"))] + use alloc::format; + + use derive_more::Debug; + + #[derive(Debug)] + #[debug("{_0:p} * {_1:p}", _0 = self.0)] + struct Tuple<'a, 'b>(&'a u8, &'b bool); + + #[derive(Debug)] + #[debug("{a:p} * {b:p}", a = self.a)] + struct Struct<'a, 'b> { + a: &'a u8, + b: &'b bool, + } + + #[test] + fn assert() { + let (a, b) = (10, true); + assert_eq!( + format!("{:?}", Tuple(&a, &b)), + format!("{:p} * {:p}", &a, &b), + ); + assert_eq!( + format!("{:?}", Struct { a: &a, b: &b }), + format!("{:p} * {:p}", &a, &b), + ); + } + } } mod ignore { @@ -677,7 +737,10 @@ mod enums { assert_eq!(format!("{:03?}", Unit::UpperHex), "00B"); assert_eq!(format!("{:07?}", Unit::LowerExp), "03.15e0"); assert_eq!(format!("{:07?}", Unit::UpperExp), "03.15E0"); - assert_eq!(format!("{:018?}", Unit::Pointer).len(), 18); + assert_eq!( + format!("{:018?}", Unit::Pointer), + format!("{POINTER:018p}"), + ); } mod omitted { diff --git a/tests/display.rs b/tests/display.rs index 60de8182..0626fabc 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -139,7 +139,7 @@ mod structs { assert_eq!(format!("{:03}", UpperHex), "00B"); assert_eq!(format!("{:07}", LowerExp), "03.15e0"); assert_eq!(format!("{:07}", UpperExp), "03.15E0"); - assert_eq!(format!("{:018}", Pointer).len(), 18); + assert_eq!(format!("{:018}", Pointer), format!("{POINTER:018p}")); } } @@ -322,9 +322,10 @@ mod structs { format!("{:07E}", StructUpperExp { field: 42.0 }), "004.2E1", ); + let a = 42; assert_eq!( - format!("{:018p}", StructPointer { field: &42 }).len(), - 18, + format!("{:018p}", StructPointer { field: &a }), + format!("{:018p}", &a), ); } } @@ -394,9 +395,10 @@ mod structs { format!("{:07}", StructUpperExp { field: 42.0 }), "004.2E1", ); + let a = 42; assert_eq!( - format!("{:018}", StructPointer { field: &42 }).len(), - 18, + format!("{:018}", StructPointer { field: &a }), + format!("{:018p}", &a), ); } } @@ -466,9 +468,10 @@ mod structs { format!("{:07}", StructUpperExp { field: 42.0 }), "4.2E1", ); - assert_ne!( - format!("{:018}", StructPointer { field: &42 }).len(), - 18, + let a = 42; + assert_eq!( + format!("{:018}", StructPointer { field: &a }), + format!("{:p}", &a), ); } } @@ -671,9 +674,10 @@ mod structs { format!("{:07}", StructUpperExp { a: 41.0, b: 42.0 }), "004.2E1", ); + let (a, b) = (42, 43); assert_eq!( - format!("{:018}", StructPointer { a: &42, b: &43 }).len(), - 18, + format!("{:018}", StructPointer { a: &a, b: &b }), + format!("{:018p}", &b), ); } } @@ -764,7 +768,10 @@ mod enums { assert_eq!(format!("{:03}", Unit::UpperHex), "00B"); assert_eq!(format!("{:07}", Unit::LowerExp), "03.15e0"); assert_eq!(format!("{:07}", Unit::UpperExp), "03.15E0"); - assert_eq!(format!("{:018}", Unit::Pointer).len(), 18); + assert_eq!( + format!("{:018}", Unit::Pointer), + format!("{POINTER:018p}"), + ); } } @@ -922,8 +929,15 @@ mod enums { format!("{:07E}", UpperExp::B { field: 43.0 }), "004.3E1", ); - assert_eq!(format!("{:018p}", Pointer::A(&7)).len(), 18); - assert_eq!(format!("{:018p}", Pointer::B { field: &42 }).len(), 18); + let (a, b) = (7, 42); + assert_eq!( + format!("{:018p}", Pointer::A(&a)), + format!("{:018p}", &a), + ); + assert_eq!( + format!("{:018p}", Pointer::B { field: &b }), + format!("{:018p}", &b), + ); } } @@ -1026,8 +1040,15 @@ mod enums { format!("{:07}", UpperExp::B { field: 43.0 }), "004.3E1", ); - assert_eq!(format!("{:018}", Pointer::A(&7)).len(), 18); - assert_eq!(format!("{:018}", Pointer::B { field: &42 }).len(), 18); + let (a, b) = (7, 42); + assert_eq!( + format!("{:018}", Pointer::A(&a)), + format!("{:018p}", &a), + ); + assert_eq!( + format!("{:018}", Pointer::B { field: &b }), + format!("{:018p}", &b), + ); } } @@ -1124,8 +1145,12 @@ mod enums { assert_eq!(format!("{:07}", LowerExp::B { field: 43.0 }), "4.3e1"); assert_eq!(format!("{:07}", UpperExp::A(42.0)), "4.2E1"); assert_eq!(format!("{:07}", UpperExp::B { field: 43.0 }), "4.3E1"); - assert_ne!(format!("{:018}", Pointer::A(&7)).len(), 18); - assert_ne!(format!("{:018}", Pointer::B { field: &42 }).len(), 18); + let (a, b) = (7, 42); + assert_eq!(format!("{:018}", Pointer::A(&a)), format!("{:0p}", &a)); + assert_eq!( + format!("{:018}", Pointer::B { field: &b }), + format!("{:p}", &b), + ); } } } @@ -1276,10 +1301,14 @@ mod enums { format!("{:07}", UpperExp::B { a: 43.0, b: 52.0 }), "004.3E1", ); - assert_eq!(format!("{:018}", Pointer::A(&7.0, &8.3)).len(), 18); + let (a, b) = (8.3, 42.1); + assert_eq!( + format!("{:018}", Pointer::A(&7.0, &a)), + format!("{:018p}", &a), + ); assert_eq!( - format!("{:018}", Pointer::B { a: &42.1, b: &43.3 }).len(), - 18, + format!("{:018}", Pointer::B { a: &b, b: &43.3 }), + format!("{:018p}", &b), ); } } @@ -2024,12 +2053,19 @@ mod generic { format!("{:07E}", Enum::::B { field: 43.0 }), "004.3E1", ); - assert_eq!(format!("{:018p}", Tuple(&42)).len(), 18); - assert_eq!(format!("{:018p}", Struct { field: &42 }).len(), 18); - assert_eq!(format!("{:018p}", Enum::<_, &i8>::A(&7)).len(), 18); + let (a, b) = (42, 7); + assert_eq!(format!("{:018p}", Tuple(&a)), format!("{:018p}", &a)); assert_eq!( - format!("{:018p}", Enum::<&i8, _>::B { field: &42 }).len(), - 18, + format!("{:018p}", Struct { field: &a }), + format!("{:018p}", &a), + ); + assert_eq!( + format!("{:018p}", Enum::<_, &i8>::A(&b)), + format!("{:018p}", &b), + ); + assert_eq!( + format!("{:018p}", Enum::<&i8, _>::B { field: &a }), + format!("{:018p}", &a), ); } } @@ -2245,12 +2281,19 @@ mod generic { format!("{:07}", EnumUpperExp::::B { field: 43.0 }), "004.3E1", ); - assert_eq!(format!("{:018}", TuplePointer(&42)).len(), 18); - assert_eq!(format!("{:018}", StructPointer { field: &42 }).len(), 18); - assert_eq!(format!("{:018}", EnumPointer::<_, &i8>::A(&7)).len(), 18); + let (a, b) = (42, 7); + assert_eq!(format!("{:018}", TuplePointer(&a)), format!("{:018p}", &a)); + assert_eq!( + format!("{:018}", StructPointer { field: &a }), + format!("{:018p}", &a), + ); + assert_eq!( + format!("{:018}", EnumPointer::<_, &i8>::A(&b)), + format!("{:018p}", &b), + ); assert_eq!( - format!("{:018}", EnumPointer::<&i8, _>::B { field: &42 }).len(), - 18, + format!("{:018}", EnumPointer::<&i8, _>::B { field: &a }), + format!("{:018p}", &a), ); } } From 774a4f28d2f08f15f2d2b9a24d693dda9a23f66e Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Wed, 3 Jul 2024 19:12:31 +0200 Subject: [PATCH 2/6] Fix issue --- CHANGELOG.md | 3 +++ impl/doc/debug.md | 22 ++++++++++++++-- impl/doc/display.md | 30 ++++++++++++++++----- impl/src/fmt/debug.rs | 58 ++++++++++++++++++++++++++++++++++------- impl/src/fmt/display.rs | 46 ++++++++++++++++++++++++++++---- impl/src/fmt/mod.rs | 15 +++++++++-- tests/debug.rs | 26 +++++++++++++++--- tests/display.rs | 57 ++++++++++++++++++++++++++++------------ 8 files changed, 211 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73d17971..f8900dc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Hygiene of macro expansions in presence of custom `core` crate. ([#327](https://github.com/JelteF/derive_more/pull/327)) - Fix documentation of generated methods in `IsVariant` derive. +- Make `{field:p}` do the expected thing in format strings for `Display` and + `Debug`. Also document weirdness around `Pointer` formatting when using + expressions, due to field variables being references. ## 0.99.10 - 2020-09-11 diff --git a/impl/doc/debug.md b/impl/doc/debug.md index 3d95b90a..3bd5a9d6 100644 --- a/impl/doc/debug.md +++ b/impl/doc/debug.md @@ -14,8 +14,26 @@ This derive macro is a clever superset of `Debug` from standard library. Additio You supply a format by placing an attribute on a struct or enum variant, or its particular field: `#[debug("...", args...)]`. The format is exactly like in [`format!()`] or any other [`format_args!()`]-based macros. -The variables available in the arguments is `self` and each member of the struct or enum variant, with members of tuple -structs being named with a leading underscore and their index, i.e. `_0`, `_1`, `_2`, etc. +The variables available in the arguments is `self` and each member of the +struct or enum variant, with members of tuple structs being named with a +leading underscore and their index, i.e. `_0`, `_1`, `_2`, etc. Due to +ownership/lifetime limitations the member variables are all references to the +fields, except when used directly in the format string. For most purposes this +detail doesn't matter, but it is quite important when using `Pointer` +formatting. If you don't use the `{field:p}` syntax, you have to dereference +once to get the address of the field itself, instead of the address of the +reference to the field: + +```rust +#[derive(derive_more::Debug)] +#[debug("{field:p} {:p}", *field)] +struct RefInt<'a> { + field: &'a i32, +} + +let a = &123; +assert_eq!(format!("{:?}", RefInt{field: &a}), format!("{a:p} {:p}", a)); +``` ### Generic data types diff --git a/impl/doc/display.md b/impl/doc/display.md index 6a7139e6..1bdbd866 100644 --- a/impl/doc/display.md +++ b/impl/doc/display.md @@ -14,17 +14,33 @@ For enums, you can either specify it on each variant, or on the enum as a whole. For variants that don't have a format specified, it will simply defer to the format of the inner variable. If there is no such variable, or there is more than 1, an error is generated. - - - ## The format of the format You supply a format by attaching an attribute of the syntax: `#[display("...", args...)]`. The format supplied is passed verbatim to `write!`. -The variables available in the arguments is `self` and each member of the variant, -with members of tuple structs being named with a leading underscore and their index, -i.e. `_0`, `_1`, `_2`, etc. +The variables available in the arguments is `self` and each member of the +struct or enum variant, with members of tuple structs being named with a +leading underscore and their index, i.e. `_0`, `_1`, `_2`, etc. Due to +ownership/lifetime limitations the member variables are all references to the +fields, except when used directly in the format string. For most purposes this +detail doesn't matter, but it is quite important when using `Pointer` +formatting. If you don't use the `{field:p}` syntax, you have to dereference +once to get the address of the field itself, instead of the address of the +reference to the field: + +```rust +# use derive_more::Display; +# +#[derive(Display)] +#[display("{field:p} {:p}", *field)] +struct RefInt<'a> { + field: &'a i32, +} + +let a = &123; +assert_eq!(format!("{}", RefInt{field: &a}), format!("{a:p} {:p}", a)); +``` For enums you can also specify a shared format on the enum itself instead of the variant. This format is used for each of the variants, and can be @@ -55,7 +71,7 @@ E.g., for a structure `Foo` defined like this: # trait Trait { type Type; } # #[derive(Display)] -#[display("{} {} {:?} {:p}", a, b, c, d)] +#[display("{a} {b} {c:?} {d:p}")] struct Foo<'a, T1, T2: Trait, T3> { a: T1, b: ::Type, diff --git a/impl/src/fmt/debug.rs b/impl/src/fmt/debug.rs index 212860d3..837f193a 100644 --- a/impl/src/fmt/debug.rs +++ b/impl/src/fmt/debug.rs @@ -244,15 +244,49 @@ impl<'a> Expansion<'a> { Ok(()) } + fn field_idents(&self) -> impl Iterator + '_ { + self.fields + .iter() + .enumerate() + .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) + } + + fn field_params<'selff, 's>( + &'selff self, + fmt: &'a FmtAttribute, + ) -> impl Iterator + 's + where + 'a: 's, + 'selff: 's, + { + let used_arguments: Vec = fmt.placeholder_names().collect(); + self.field_idents().filter_map(move |var| { + if used_arguments.contains(&var.to_string()) { + Some(quote! { #var = *#var }) + } else { + None + } + }) + } + /// Generates [`Debug::fmt()`] implementation for a struct or an enum variant. /// /// [`Debug::fmt()`]: std::fmt::Debug::fmt() fn generate_body(&self) -> syn::Result { if let Some(fmt) = &self.attr.fmt { return Ok(if let Some((expr, trait_ident)) = fmt.transparent_call() { - quote! { derive_more::core::fmt::#trait_ident::fmt(&(#expr), __derive_more_f) } + let expr = if self.field_idents().any(|var| { + fmt.placeholder_names() + .any(|arg| arg == var.to_string().as_str()) + }) { + quote! { #expr } + } else { + quote! { &(#expr) } + }; + quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) } } else { - quote! { derive_more::core::write!(__derive_more_f, #fmt) } + let field_params = self.field_params(fmt); + quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#field_params),*) } }); }; @@ -288,12 +322,14 @@ impl<'a> Expansion<'a> { exhaustive = false; Ok::<_, syn::Error>(out) } - Some(FieldAttribute::Right(fmt_attr)) => Ok(quote! { - derive_more::__private::DebugTuple::field( - #out, - &derive_more::core::format_args!(#fmt_attr), + Some(FieldAttribute::Right(fmt_attr)) => { + let field_params = self.field_params(&fmt_attr); + Ok(quote! { + derive_more::__private::DebugTuple::field( + #out, + &derive_more::core::format_args!(#fmt_attr, #(#field_params),*), ) - }), + })}, None => { let ident = format_ident!("_{i}"); Ok(quote! { @@ -330,13 +366,15 @@ impl<'a> Expansion<'a> { exhaustive = false; Ok::<_, syn::Error>(out) } - Some(FieldAttribute::Right(fmt_attr)) => Ok(quote! { + Some(FieldAttribute::Right(fmt_attr)) => { + let field_params = self.field_params(&fmt_attr); + Ok(quote! { derive_more::core::fmt::DebugStruct::field( #out, #field_str, - &derive_more::core::format_args!(#fmt_attr), + &derive_more::core::format_args!(#fmt_attr, #(#field_params),*), ) - }), + })}, None => Ok(quote! { derive_more::core::fmt::DebugStruct::field(#out, #field_str, &#field_ident) }), diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 429e78b7..55e91eca 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -5,7 +5,7 @@ use std::fmt; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned as _}; +use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned as _, Ident}; use crate::utils::{attr::ParseMultiple as _, Spanning}; @@ -254,6 +254,31 @@ struct Expansion<'a> { } impl<'a> Expansion<'a> { + fn field_idents(&self) -> impl Iterator + '_ { + self.fields + .iter() + .enumerate() + .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) + } + + fn field_params<'selff, 's>( + &'selff self, + fmt: &'a FmtAttribute, + ) -> impl Iterator + 's + where + 'a: 's, + 'selff: 's, + { + let used_arguments: Vec = fmt.placeholder_names().collect(); + self.field_idents().filter_map(move |var| { + if used_arguments.contains(&var.to_string()) { + Some(quote! { #var = *#var }) + } else { + None + } + }) + } + /// Generates [`Display::fmt()`] implementation for a struct or an enum variant. /// /// # Errors @@ -278,13 +303,23 @@ impl<'a> Expansion<'a> { body = match &self.attrs.fmt { Some(fmt) => { if has_shared_attr { - quote! { &derive_more::core::format_args!(#fmt) } + let field_params = self.field_params(fmt); + quote! { &derive_more::core::format_args!(#fmt, #(#field_params),*) } } else if let Some((expr, trait_ident)) = fmt.transparent_call() { + let expr = if self.field_idents().any(|var| { + fmt.placeholder_names() + .any(|arg| arg == var.to_string().as_str()) + }) { + quote! { #expr } + } else { + quote! { &(#expr) } + }; quote! { - derive_more::core::fmt::#trait_ident::fmt(&(#expr), __derive_more_f) + derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) } } else { - quote! { derive_more::core::write!(__derive_more_f, #fmt) } + let field_params = self.field_params(fmt); + quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#field_params),*) } } } None if self.fields.is_empty() => { @@ -332,8 +367,9 @@ impl<'a> Expansion<'a> { if has_shared_attr { if let Some(shared_fmt) = &self.shared_attr { + let field_params = self.field_params(shared_fmt); let shared_body = quote! { - derive_more::core::write!(__derive_more_f, #shared_fmt) + derive_more::core::write!(__derive_more_f, #shared_fmt, #(#field_params),*) }; body = if body.is_empty() { diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 62b57f09..befa8529 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -113,14 +113,16 @@ impl Parse for FmtAttribute { fn parse(input: ParseStream<'_>) -> syn::Result { Self::check_legacy_fmt(input)?; - Ok(Self { + let mut parsed = Self { lit: input.parse()?, comma: input .peek(token::Comma) .then(|| input.parse()) .transpose()?, args: input.parse_terminated(FmtArgument::parse, token::Comma)?, - }) + }; + parsed.args.pop_punct(); + Ok(parsed) } } @@ -273,6 +275,15 @@ impl FmtAttribute { }) } + fn placeholder_names(&self) -> impl Iterator { + Placeholder::parse_fmt_string(&self.lit.value()) + .into_iter() + .filter_map(|placeholder| match placeholder.arg { + Parameter::Named(name) => Some(name), + _ => None, + }) + } + /// Errors in case legacy syntax is encountered: `fmt = "...", (arg),*`. fn check_legacy_fmt(input: ParseStream<'_>) -> syn::Result<()> { let fork = input.fork(); diff --git a/tests/debug.rs b/tests/debug.rs index 8944bd42..1c483ef2 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -262,6 +262,16 @@ mod structs { field: &'a i32, } + #[derive(Debug)] + #[debug("{_0:p}")] + struct TupleTransparent<'a>(&'a i32); + + #[derive(Debug)] + #[debug("{field:p}")] + struct StructTransparent<'a> { + field: &'a i32, + } + #[test] fn assert() { let a = 42; @@ -273,6 +283,14 @@ mod structs { format!("{:?}", Struct { field: &a }), format!("Struct {{ field: {0:p}.{0:p} }}", &a), ); + assert_eq!( + format!("{:?}", TupleTransparent(&a)), + format!("{0:p}", &a), + ); + assert_eq!( + format!("{:?}", StructTransparent { field: &a }), + format!("{0:p}", &a), + ); } } } @@ -564,11 +582,11 @@ mod structs { use derive_more::Debug; #[derive(Debug)] - #[debug("{_0:p} * {_1:p}", _0 = self.0)] + #[debug("{_0:p} * {_1:p}")] struct Tuple<'a, 'b>(&'a u8, &'b bool); #[derive(Debug)] - #[debug("{a:p} * {b:p}", a = self.a)] + #[debug("{a:p} * {b:p}")] struct Struct<'a, 'b> { a: &'a u8, b: &'b bool, @@ -1305,7 +1323,7 @@ mod generic { #[derive(Debug)] struct AliasedFieldNamedGenericStruct { - #[debug("{field1}", field1 = field2)] + #[debug("{field3}", field3 = field2)] field1: T, field2: i32, } @@ -1423,7 +1441,7 @@ mod generic { } #[derive(Debug)] - struct AliasedFieldUnnamedGenericStruct(#[debug("{_0}", _0 = _1)] T, i32); + struct AliasedFieldUnnamedGenericStruct(#[debug("{_2}", _2 = _1)] T, i32); #[test] fn aliased_field_unnamed_generic_struct() { assert_eq!( diff --git a/tests/display.rs b/tests/display.rs index 0626fabc..f0d480db 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -447,7 +447,7 @@ mod structs { } #[derive(Display)] - #[display("{}", format_args!("{field:p}"))] + #[display("{}", format_args!("{field:p}", field = *field))] struct StructPointer<'a> { field: &'a i32, } @@ -566,15 +566,15 @@ mod structs { #[derive(Display)] #[display( - "{_0} {ident} {_1} {} {}", - _1, _0 + _1, ident = 123, _1 = _0, + "{_0} {ident} {_2} {} {}", + _1, _0 + _1, ident = 123, _2 = _0, )] struct Tuple(i32, i32); #[derive(Display)] #[display( - "{field1} {ident} {field2} {} {}", - field2, field1 + field2, ident = 123, field2 = field1, + "{field1} {ident} {field3} {} {}", + field2, field1 + field2, ident = 123, field3 = field1, )] struct Struct { field1: i32, @@ -1010,7 +1010,7 @@ mod enums { #[derive(Display)] enum Pointer<'a> { - #[display("{:p}", _0)] + #[display("{:p}", *_0)] A(&'a i32), #[display("{field:p}")] B { field: &'a u8 }, @@ -1121,9 +1121,9 @@ mod enums { #[derive(Display)] enum Pointer<'a> { - #[display("{}", format_args!("{:p}", _0))] + #[display("{}", format_args!("{:p}", *_0))] A(&'a i32), - #[display("{}", format_args!("{field:p}"))] + #[display("{}", format_args!("{field:p}", field = *field))] B { field: &'a u8 }, } @@ -1166,13 +1166,13 @@ mod enums { #[display("named")] StrNamed { field1: i32, field2: i32 }, #[display( - "{_0} {ident} {_1} {} {}", - _1, _0 + _1, ident = 123, _1 = _0, + "{_0} {ident} {_2} {} {}", + _1, _0 + _1, ident = 123, _2 = _0, )] InterpolatedUnnamed(i32, i32), #[display( - "{field1} {ident} {field2} {} {}", - field2, field1 + field2, ident = 123, field2 = field1, + "{field1} {ident} {field3} {} {}", + field2, field1 + field2, ident = 123, field3 = field1, )] InterpolatedNamed { field1: i32, field2: i32 }, } @@ -1271,7 +1271,7 @@ mod enums { #[derive(Display)] enum Pointer<'a> { - #[display("{:p}", _1)] + #[display("{:p}", *_1)] A(&'a f64, &'a f32), #[display("{a:p}")] B { a: &'a f64, b: &'a f32 }, @@ -1466,6 +1466,31 @@ mod enums { assert_eq!(Enum::::C(9).to_string(), "Variant C 9"); } } + + mod pointer { + use super::*; + + #[derive(Display)] + #[display("Pointer {_0:p} {_variant} {_0:p}")] + enum Pointer<'a> { + #[display("A")] + A(&'a f64), + #[display("B")] + B(&'a f32), + } + #[test] + fn assert() { + let (a, b) = (8.3, 42.1); + assert_eq!( + format!("{}", Pointer::A(&a)), + format!("Pointer {0:p} A {0:p}", &a), + ); + assert_eq!( + format!("{}", Pointer::B(&b)), + format!("Pointer {0:p} B {0:p}", &b), + ); + } + } } } } @@ -2106,7 +2131,7 @@ mod generic { struct TupleUpperExp(T); #[derive(Display)] - #[display("{:p}", _0)] + #[display("{_0:p}")] struct TuplePointer(T); #[derive(Display)] @@ -2154,7 +2179,7 @@ mod generic { } #[derive(Display)] - #[display("{:p}", field)] + #[display("{field:p}")] struct StructPointer { field: T, } @@ -2217,7 +2242,7 @@ mod generic { #[derive(Display)] enum EnumPointer { - #[display("{:p}", _0)] + #[display("{_0:p}")] A(A), #[display("{field:p}")] B { field: B }, From 842ee72734122a113291ef7e7d75983f797d082b Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 31 Jul 2024 19:06:23 +0300 Subject: [PATCH 3/6] Polish and omit defining already defined arguments --- CHANGELOG.md | 1 + impl/doc/debug.md | 20 +++++---- impl/doc/display.md | 3 ++ impl/src/fmt/debug.rs | 93 +++++++++++++++++------------------------ impl/src/fmt/display.rs | 62 ++++++++++----------------- impl/src/fmt/mod.rs | 37 ++++++++++++++-- impl/src/parsing.rs | 6 +++ tests/debug.rs | 4 +- tests/display.rs | 20 ++++----- 9 files changed, 126 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8900dc0..ea006ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Make `{field:p}` do the expected thing in format strings for `Display` and `Debug`. Also document weirdness around `Pointer` formatting when using expressions, due to field variables being references. + ([#381](https://github.com/JelteF/derive_more/pull/381)) ## 0.99.10 - 2020-09-11 diff --git a/impl/doc/debug.md b/impl/doc/debug.md index 3bd5a9d6..b03d510d 100644 --- a/impl/doc/debug.md +++ b/impl/doc/debug.md @@ -25,7 +25,9 @@ once to get the address of the field itself, instead of the address of the reference to the field: ```rust -#[derive(derive_more::Debug)] +# use derive_more::Debug; +# +#[derive(Debug)] #[debug("{field:p} {:p}", *field)] struct RefInt<'a> { field: &'a i32, @@ -43,8 +45,8 @@ are bound by respective formatting trait. E.g., for a structure `Foo` defined like this: ```rust -use derive_more::Debug; - +# use derive_more::Debug; +# #[derive(Debug)] struct Foo<'a, T1, T2: Trait, T3, T4> { #[debug("{a}")] @@ -82,9 +84,9 @@ possible. Our aim is to avoid imposing additional bounds, as they can be added w In the example below, we can infer only that `V: Display`, other bounds have to be supplied by the user: ```rust -use std::fmt::Display; -use derive_more::Debug; - +# use std::fmt::Display; +# use derive_more::Debug; +# #[derive(Debug)] #[debug(bound(T: MyTrait, U: Display))] struct MyStruct { @@ -155,9 +157,9 @@ assert_eq!(format!("{:07?}", MyOctalInt(9)), "11"); ## Example usage ```rust -use std::path::PathBuf; -use derive_more::Debug; - +# use std::path::PathBuf; +# use derive_more::Debug; +# #[derive(Debug)] struct MyInt(i32); diff --git a/impl/doc/display.md b/impl/doc/display.md index 1bdbd866..211ffced 100644 --- a/impl/doc/display.md +++ b/impl/doc/display.md @@ -14,6 +14,9 @@ For enums, you can either specify it on each variant, or on the enum as a whole. For variants that don't have a format specified, it will simply defer to the format of the inner variable. If there is no such variable, or there is more than 1, an error is generated. + + + ## The format of the format You supply a format by attaching an attribute of the syntax: `#[display("...", args...)]`. diff --git a/impl/src/fmt/debug.rs b/impl/src/fmt/debug.rs index 837f193a..a3cee6f2 100644 --- a/impl/src/fmt/debug.rs +++ b/impl/src/fmt/debug.rs @@ -13,7 +13,7 @@ use crate::utils::{ use super::{ trait_name_to_attribute_name, ContainerAttributes, ContainsGenericsExt as _, - FmtAttribute, + FieldsExt as _, FmtAttribute, }; /// Expands a [`fmt::Debug`] derive macro. @@ -244,49 +244,23 @@ impl<'a> Expansion<'a> { Ok(()) } - fn field_idents(&self) -> impl Iterator + '_ { - self.fields - .iter() - .enumerate() - .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) - } - - fn field_params<'selff, 's>( - &'selff self, - fmt: &'a FmtAttribute, - ) -> impl Iterator + 's - where - 'a: 's, - 'selff: 's, - { - let used_arguments: Vec = fmt.placeholder_names().collect(); - self.field_idents().filter_map(move |var| { - if used_arguments.contains(&var.to_string()) { - Some(quote! { #var = *#var }) - } else { - None - } - }) - } - /// Generates [`Debug::fmt()`] implementation for a struct or an enum variant. /// /// [`Debug::fmt()`]: std::fmt::Debug::fmt() fn generate_body(&self) -> syn::Result { if let Some(fmt) = &self.attr.fmt { return Ok(if let Some((expr, trait_ident)) = fmt.transparent_call() { - let expr = if self.field_idents().any(|var| { - fmt.placeholder_names() - .any(|arg| arg == var.to_string().as_str()) - }) { + let expr = if self.fields.fmt_args_idents().any(|field| expr == field) { quote! { #expr } } else { quote! { &(#expr) } }; + quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) } } else { - let field_params = self.field_params(fmt); - quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#field_params),*) } + let deref_args = fmt.additional_deref_args(self.fields); + + quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) } }); }; @@ -323,13 +297,15 @@ impl<'a> Expansion<'a> { Ok::<_, syn::Error>(out) } Some(FieldAttribute::Right(fmt_attr)) => { - let field_params = self.field_params(&fmt_attr); + let deref_args = fmt_attr.additional_deref_args(self.fields); + Ok(quote! { derive_more::__private::DebugTuple::field( #out, - &derive_more::core::format_args!(#fmt_attr, #(#field_params),*), - ) - })}, + &derive_more::core::format_args!(#fmt_attr, #(#deref_args),*), + ) + }) + } None => { let ident = format_ident!("_{i}"); Ok(quote! { @@ -355,31 +331,38 @@ impl<'a> Expansion<'a> { ) }; let out = named.named.iter().try_fold(out, |out, field| { - let field_ident = field.ident.as_ref().unwrap_or_else(|| { - unreachable!("`syn::Fields::Named`"); - }); - let field_str = field_ident.to_string(); - match FieldAttribute::parse_attrs(&field.attrs, self.attr_name)? - .map(Spanning::into_inner) - { - Some(FieldAttribute::Left(_skip)) => { - exhaustive = false; - Ok::<_, syn::Error>(out) - } - Some(FieldAttribute::Right(fmt_attr)) => { - let field_params = self.field_params(&fmt_attr); + let field_ident = field.ident.as_ref().unwrap_or_else(|| { + unreachable!("`syn::Fields::Named`"); + }); + let field_str = field_ident.to_string(); + match FieldAttribute::parse_attrs(&field.attrs, self.attr_name)? + .map(Spanning::into_inner) + { + Some(FieldAttribute::Left(_skip)) => { + exhaustive = false; + Ok::<_, syn::Error>(out) + } + Some(FieldAttribute::Right(fmt_attr)) => { + let deref_args = + fmt_attr.additional_deref_args(self.fields); + Ok(quote! { derive_more::core::fmt::DebugStruct::field( #out, #field_str, - &derive_more::core::format_args!(#fmt_attr, #(#field_params),*), + &derive_more::core::format_args!( + #fmt_attr, #(#deref_args),* + ), ) - })}, - None => Ok(quote! { - derive_more::core::fmt::DebugStruct::field(#out, #field_str, &#field_ident) - }), + }) } - })?; + None => Ok(quote! { + derive_more::core::fmt::DebugStruct::field( + #out, #field_str, &#field_ident + ) + }), + } + })?; Ok(if exhaustive { quote! { derive_more::core::fmt::DebugStruct::finish(#out) } } else { diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 55e91eca..b217a779 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -5,13 +5,13 @@ use std::fmt; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned as _, Ident}; +use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned as _}; use crate::utils::{attr::ParseMultiple as _, Spanning}; use super::{ trait_name_to_attribute_name, ContainerAttributes, ContainsGenericsExt as _, - FmtAttribute, + FieldsExt as _, FmtAttribute, }; /// Expands a [`fmt::Display`]-like derive macro. @@ -254,31 +254,6 @@ struct Expansion<'a> { } impl<'a> Expansion<'a> { - fn field_idents(&self) -> impl Iterator + '_ { - self.fields - .iter() - .enumerate() - .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) - } - - fn field_params<'selff, 's>( - &'selff self, - fmt: &'a FmtAttribute, - ) -> impl Iterator + 's - where - 'a: 's, - 'selff: 's, - { - let used_arguments: Vec = fmt.placeholder_names().collect(); - self.field_idents().filter_map(move |var| { - if used_arguments.contains(&var.to_string()) { - Some(quote! { #var = *#var }) - } else { - None - } - }) - } - /// Generates [`Display::fmt()`] implementation for a struct or an enum variant. /// /// # Errors @@ -303,23 +278,27 @@ impl<'a> Expansion<'a> { body = match &self.attrs.fmt { Some(fmt) => { if has_shared_attr { - let field_params = self.field_params(fmt); - quote! { &derive_more::core::format_args!(#fmt, #(#field_params),*) } + let deref_args = fmt.additional_deref_args(self.fields); + + quote! { &derive_more::core::format_args!(#fmt, #(#deref_args),*) } } else if let Some((expr, trait_ident)) = fmt.transparent_call() { - let expr = if self.field_idents().any(|var| { - fmt.placeholder_names() - .any(|arg| arg == var.to_string().as_str()) - }) { - quote! { #expr } - } else { - quote! { &(#expr) } - }; + let expr = + if self.fields.fmt_args_idents().any(|field| expr == field) + { + quote! { #expr } + } else { + quote! { &(#expr) } + }; + quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) } } else { - let field_params = self.field_params(fmt); - quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#field_params),*) } + let deref_args = fmt.additional_deref_args(self.fields); + + quote! { + derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) + } } } None if self.fields.is_empty() => { @@ -367,9 +346,10 @@ impl<'a> Expansion<'a> { if has_shared_attr { if let Some(shared_fmt) = &self.shared_attr { - let field_params = self.field_params(shared_fmt); + let deref_args = shared_fmt.additional_deref_args(self.fields); + let shared_body = quote! { - derive_more::core::write!(__derive_more_f, #shared_fmt, #(#field_params),*) + derive_more::core::write!(__derive_more_f, #shared_fmt, #(#deref_args),*) }; body = if body.is_empty() { diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index befa8529..47686677 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -9,7 +9,7 @@ pub(crate) mod display; mod parsing; use proc_macro2::TokenStream; -use quote::{format_ident, ToTokens}; +use quote::{format_ident, quote, ToTokens}; use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, @@ -275,13 +275,28 @@ impl FmtAttribute { }) } - fn placeholder_names(&self) -> impl Iterator { - Placeholder::parse_fmt_string(&self.lit.value()) + /// Returns an [`Iterator`] over the additional formatting arguments doing the dereferencing + /// replacement in this [`FmtAttribute`] for those [`Placeholder`] representing the provided + /// [`syn::Fields`] and requiring it + fn additional_deref_args<'fmt: 'ret, 'fields: 'ret, 'ret>( + &'fmt self, + fields: &'fields syn::Fields, + ) -> impl Iterator + 'ret { + let used_args = Placeholder::parse_fmt_string(&self.lit.value()) .into_iter() .filter_map(|placeholder| match placeholder.arg { Parameter::Named(name) => Some(name), _ => None, }) + .collect::>(); + + fields.fmt_args_idents().filter_map(move |field_name| { + (used_args.iter().any(|arg| field_name == arg) + && !self.args.iter().any(|arg| { + arg.alias.as_ref().map_or(false, |(n, _)| n == &field_name) + })) + .then(|| quote! { #field_name = *#field_name }) + }) } /// Errors in case legacy syntax is encountered: `fmt = "...", (arg),*`. @@ -534,6 +549,7 @@ where } } +/// Extension of a [`syn::Type`] and a [`syn::Path`] allowing to travers its type parameters. trait ContainsGenericsExt { /// Checks whether this definition contains any of the provided `type_params`. fn contains_generics(&self, type_params: &[&syn::Ident]) -> bool; @@ -649,6 +665,21 @@ impl ContainsGenericsExt for syn::Path { } } +/// Extension of [`syn::Fields`] providing helpers for a [`FmtAttribute`]. +trait FieldsExt { + /// Returns an [`Iterator`] over [`syn::Ident`]s representing these [`syn::Fields`] in a + /// [`FmtAttribute`] as [`FmtArgument`]s or named [`Placeholder`]s. + fn fmt_args_idents(&self) -> impl Iterator + '_; +} + +impl FieldsExt for syn::Fields { + fn fmt_args_idents(&self) -> impl Iterator + '_ { + self.iter() + .enumerate() + .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) + } +} + #[cfg(test)] mod fmt_attribute_spec { use itertools::Itertools as _; diff --git a/impl/src/parsing.rs b/impl/src/parsing.rs index a6ea91a3..1bd2aa5f 100644 --- a/impl/src/parsing.rs +++ b/impl/src/parsing.rs @@ -71,6 +71,12 @@ impl Parse for Expr { } } +impl PartialEq for Expr { + fn eq(&self, other: &syn::Ident) -> bool { + self.ident().map_or(false, |i| i == other) + } +} + impl ToTokens for Expr { fn to_tokens(&self, tokens: &mut TokenStream) { match self { diff --git a/tests/debug.rs b/tests/debug.rs index 1c483ef2..40bdf19a 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -1323,7 +1323,7 @@ mod generic { #[derive(Debug)] struct AliasedFieldNamedGenericStruct { - #[debug("{field3}", field3 = field2)] + #[debug("{field1}", field1 = field2)] field1: T, field2: i32, } @@ -1441,7 +1441,7 @@ mod generic { } #[derive(Debug)] - struct AliasedFieldUnnamedGenericStruct(#[debug("{_2}", _2 = _1)] T, i32); + struct AliasedFieldUnnamedGenericStruct(#[debug("{_0}", _0 = _1)] T, i32); #[test] fn aliased_field_unnamed_generic_struct() { assert_eq!( diff --git a/tests/display.rs b/tests/display.rs index f0d480db..2a9dbb8b 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -566,15 +566,15 @@ mod structs { #[derive(Display)] #[display( - "{_0} {ident} {_2} {} {}", - _1, _0 + _1, ident = 123, _2 = _0, + "{_0} {ident} {_1} {} {}", + _1, _0 + _1, ident = 123, _1 = _0, )] struct Tuple(i32, i32); #[derive(Display)] #[display( - "{field1} {ident} {field3} {} {}", - field2, field1 + field2, ident = 123, field3 = field1, + "{field1} {ident} {field2} {} {}", + field2, field1 + field2, ident = 123, field2 = field1, )] struct Struct { field1: i32, @@ -1166,13 +1166,13 @@ mod enums { #[display("named")] StrNamed { field1: i32, field2: i32 }, #[display( - "{_0} {ident} {_2} {} {}", - _1, _0 + _1, ident = 123, _2 = _0, + "{_0} {ident} {_1} {} {}", + _1, _0 + _1, ident = 123, _1 = _0, )] InterpolatedUnnamed(i32, i32), #[display( - "{field1} {ident} {field3} {} {}", - field2, field1 + field2, ident = 123, field3 = field1, + "{field1} {ident} {field2} {} {}", + field2, field1 + field2, ident = 123, field2 = field1, )] InterpolatedNamed { field1: i32, field2: i32 }, } @@ -1482,11 +1482,11 @@ mod enums { fn assert() { let (a, b) = (8.3, 42.1); assert_eq!( - format!("{}", Pointer::A(&a)), + Pointer::A(&a).to_string(), format!("Pointer {0:p} A {0:p}", &a), ); assert_eq!( - format!("{}", Pointer::B(&b)), + Pointer::B(&b).to_string(), format!("Pointer {0:p} B {0:p}", &b), ); } From 8a8eac95dd02ad99987f9abead5b1b4aae60f98b Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 31 Jul 2024 19:09:49 +0300 Subject: [PATCH 4/6] Fix docs --- impl/src/fmt/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 47686677..6044745d 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -669,6 +669,8 @@ impl ContainsGenericsExt for syn::Path { trait FieldsExt { /// Returns an [`Iterator`] over [`syn::Ident`]s representing these [`syn::Fields`] in a /// [`FmtAttribute`] as [`FmtArgument`]s or named [`Placeholder`]s. + /// + /// [`syn::Ident`]: struct@syn::Ident fn fmt_args_idents(&self) -> impl Iterator + '_; } From 46362f653dce2e46e43a4b758f931e47be555a3c Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 31 Jul 2024 19:14:15 +0300 Subject: [PATCH 5/6] Compatibility with 1.65 MSRV --- impl/src/fmt/debug.rs | 7 ++++++- impl/src/fmt/display.rs | 17 ++++++++++------- impl/src/fmt/mod.rs | 24 +++++++++++++++--------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/impl/src/fmt/debug.rs b/impl/src/fmt/debug.rs index a3cee6f2..ea6b61d6 100644 --- a/impl/src/fmt/debug.rs +++ b/impl/src/fmt/debug.rs @@ -250,7 +250,12 @@ impl<'a> Expansion<'a> { fn generate_body(&self) -> syn::Result { if let Some(fmt) = &self.attr.fmt { return Ok(if let Some((expr, trait_ident)) = fmt.transparent_call() { - let expr = if self.fields.fmt_args_idents().any(|field| expr == field) { + let expr = if self + .fields + .fmt_args_idents() + .into_iter() + .any(|field| expr == field) + { quote! { #expr } } else { quote! { &(#expr) } diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index b217a779..b44dadc0 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -282,13 +282,16 @@ impl<'a> Expansion<'a> { quote! { &derive_more::core::format_args!(#fmt, #(#deref_args),*) } } else if let Some((expr, trait_ident)) = fmt.transparent_call() { - let expr = - if self.fields.fmt_args_idents().any(|field| expr == field) - { - quote! { #expr } - } else { - quote! { &(#expr) } - }; + let expr = if self + .fields + .fmt_args_idents() + .into_iter() + .any(|field| expr == field) + { + quote! { #expr } + } else { + quote! { &(#expr) } + }; quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 6044745d..15c76c08 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -290,13 +290,16 @@ impl FmtAttribute { }) .collect::>(); - fields.fmt_args_idents().filter_map(move |field_name| { - (used_args.iter().any(|arg| field_name == arg) - && !self.args.iter().any(|arg| { - arg.alias.as_ref().map_or(false, |(n, _)| n == &field_name) - })) - .then(|| quote! { #field_name = *#field_name }) - }) + fields + .fmt_args_idents() + .into_iter() + .filter_map(move |field_name| { + (used_args.iter().any(|arg| field_name == arg) + && !self.args.iter().any(|arg| { + arg.alias.as_ref().map_or(false, |(n, _)| n == &field_name) + })) + .then(|| quote! { #field_name = *#field_name }) + }) } /// Errors in case legacy syntax is encountered: `fmt = "...", (arg),*`. @@ -671,14 +674,17 @@ trait FieldsExt { /// [`FmtAttribute`] as [`FmtArgument`]s or named [`Placeholder`]s. /// /// [`syn::Ident`]: struct@syn::Ident - fn fmt_args_idents(&self) -> impl Iterator + '_; + // TODO: Return `impl Iterator + '_` once MSRV is bumped up to 1.75 or + // higher. + fn fmt_args_idents(&self) -> Vec; } impl FieldsExt for syn::Fields { - fn fmt_args_idents(&self) -> impl Iterator + '_ { + fn fmt_args_idents(&self) -> Vec { self.iter() .enumerate() .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) + .collect() } } From 3b67c97295bae2018700da6dd3d763c4b24a5644 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 31 Jul 2024 19:18:21 +0300 Subject: [PATCH 6/6] Show import in `Debug` docs --- impl/doc/debug.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/impl/doc/debug.md b/impl/doc/debug.md index b03d510d..17c3d1f6 100644 --- a/impl/doc/debug.md +++ b/impl/doc/debug.md @@ -25,8 +25,8 @@ once to get the address of the field itself, instead of the address of the reference to the field: ```rust -# use derive_more::Debug; -# +use derive_more::Debug; + #[derive(Debug)] #[debug("{field:p} {:p}", *field)] struct RefInt<'a> { @@ -45,8 +45,8 @@ are bound by respective formatting trait. E.g., for a structure `Foo` defined like this: ```rust -# use derive_more::Debug; -# +use derive_more::Debug; + #[derive(Debug)] struct Foo<'a, T1, T2: Trait, T3, T4> { #[debug("{a}")] @@ -84,9 +84,9 @@ possible. Our aim is to avoid imposing additional bounds, as they can be added w In the example below, we can infer only that `V: Display`, other bounds have to be supplied by the user: ```rust -# use std::fmt::Display; -# use derive_more::Debug; -# +use std::fmt::Display; +use derive_more::Debug; + #[derive(Debug)] #[debug(bound(T: MyTrait, U: Display))] struct MyStruct { @@ -110,8 +110,8 @@ If the top-level `#[debug("...", args...)]` attribute (the one for a whole struc and can be trivially substituted with a transparent delegation call to the inner type, then all the additional [formatting parameters][1] do work as expected: ```rust -# use derive_more::Debug; -# +use derive_more::Debug; + #[derive(Debug)] #[debug("{_0:o}")] // the same as calling `Octal::fmt()` struct MyOctalInt(i32); @@ -130,8 +130,8 @@ assert_eq!(format!("{:07?}", MyBinaryInt(2)), "10"); If, for some reason, transparency in trivial cases is not desired, it may be suppressed explicitly either with the [`format_args!()`] macro usage: ```rust -# use derive_more::Debug; -# +use derive_more::Debug; + #[derive(Debug)] #[debug("{}", format_args!("{_0:o}"))] // `format_args!()` obscures the inner type struct MyOctalInt(i32); @@ -141,8 +141,8 @@ assert_eq!(format!("{:07?}", MyOctalInt(9)), "11"); ``` Or by adding [formatting parameters][1] which cause no visual effects: ```rust -# use derive_more::Debug; -# +use derive_more::Debug; + #[derive(Debug)] #[debug("{_0:^o}")] // `^` is centering, but in absence of additional width has no effect struct MyOctalInt(i32); @@ -157,9 +157,9 @@ assert_eq!(format!("{:07?}", MyOctalInt(9)), "11"); ## Example usage ```rust -# use std::path::PathBuf; -# use derive_more::Debug; -# +use std::path::PathBuf; +use derive_more::Debug; + #[derive(Debug)] struct MyInt(i32);