From 71794987a521df41e545047743c7d1539f397838 Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Wed, 31 Jul 2024 20:10:48 +0200 Subject: [PATCH] Fix incorrect `fmt::Pointer` implementations (#381, #328) Resolves #328 Requires #377 Requires #380 ## Synopsis `Debug` and `Display` derives allow referring fields via short syntax (`_0` for unnamed fields and `name` for named fields): ```rust #[derive(Display)] #[display("{_0:o}")] struct OctalInt(i32); ``` The way this works is by introducing a local binding in the macro expansion: ```rust let _0 = &self.0; ``` This, however, introduces double pointer indirection. For most of the `fmt` traits, this is totally OK. However, the `fmt::Pointer` is sensitive to that: ```rust #[derive(Display)] #[display("--> {_0:p}")] struct Int(&'static i32); // expands to impl fmt::Display for Int { fn fmt(&self, f: fmt::Formatter<'_>) -> fmt::Result { let _0 = &self.0; // has `&&i32` type, not `&i32` write!(f, "--> {_0:p}") // so, prints address of the `_0` local binding, // not the one of the `self.0` field as we would expect } } ``` ## Solution Pass all local bindings also as named parameters and dereference them there. This allows `"{_0:p}"` to work as expected. Positional arguments and expressions still have the previous behaviour. This seems okay IMHO, as we can explain that in expressions these local bindings are references and that you need to dereference when needed, such as for `Pointer`. A downside of the current implementation is that users cannot use the names of our named parameters as names for their own named parameters, because we already use them. With some additional code this is fixable, but it doesn't seem important enough to fix. People can simply use a different name when creating their own named parameters, which is a good idea anyway because it will be less confusing to any reader of the code. If it turns out to be important to support this after all, we can still start to support it in a backwards compatible way (because now it causes a compilation failure). Co-authored-by: Kai Ren --- CHANGELOG.md | 4 ++ impl/doc/debug.md | 36 +++++++--- impl/doc/display.md | 27 ++++++-- impl/src/fmt/debug.rs | 80 ++++++++++++++-------- impl/src/fmt/display.rs | 29 ++++++-- impl/src/fmt/mod.rs | 56 +++++++++++++++- impl/src/parsing.rs | 6 ++ tests/debug.rs | 85 +++++++++++++++++++++++- tests/display.rs | 144 +++++++++++++++++++++++++++++----------- 9 files changed, 380 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73d17971..ea006ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,10 @@ 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. + ([#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 3d95b90a..17c3d1f6 100644 --- a/impl/doc/debug.md +++ b/impl/doc/debug.md @@ -14,8 +14,28 @@ 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 +use derive_more::Debug; + +#[derive(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 @@ -90,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); @@ -110,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); @@ -121,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); diff --git a/impl/doc/display.md b/impl/doc/display.md index 6a7139e6..211ffced 100644 --- a/impl/doc/display.md +++ b/impl/doc/display.md @@ -22,9 +22,28 @@ inner variable. If there is no such variable, or there is more than 1, an error 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 +74,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..ea6b61d6 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. @@ -250,9 +250,22 @@ 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() { - quote! { derive_more::core::fmt::#trait_ident::fmt(&(#expr), __derive_more_f) } + 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) } } else { - quote! { derive_more::core::write!(__derive_more_f, #fmt) } + let deref_args = fmt.additional_deref_args(self.fields); + + quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) } }); }; @@ -288,12 +301,16 @@ 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 deref_args = fmt_attr.additional_deref_args(self.fields); + + Ok(quote! { + derive_more::__private::DebugTuple::field( + #out, + &derive_more::core::format_args!(#fmt_attr, #(#deref_args),*), + ) + }) + } None => { let ident = format_ident!("_{i}"); Ok(quote! { @@ -319,29 +336,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)) => Ok(quote! { + 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), + &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 429e78b7..b44dadc0 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -11,7 +11,7 @@ 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. @@ -278,13 +278,30 @@ impl<'a> Expansion<'a> { body = match &self.attrs.fmt { Some(fmt) => { if has_shared_attr { - quote! { &derive_more::core::format_args!(#fmt) } + 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 + .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) + derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) } } else { - quote! { derive_more::core::write!(__derive_more_f, #fmt) } + 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() => { @@ -332,8 +349,10 @@ impl<'a> Expansion<'a> { if has_shared_attr { if let Some(shared_fmt) = &self.shared_attr { + let deref_args = shared_fmt.additional_deref_args(self.fields); + let shared_body = quote! { - derive_more::core::write!(__derive_more_f, #shared_fmt) + 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 62b57f09..15c76c08 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, @@ -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,33 @@ impl FmtAttribute { }) } + /// 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() + .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),*`. fn check_legacy_fmt(input: ParseStream<'_>) -> syn::Result<()> { let fork = input.fork(); @@ -523,6 +552,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; @@ -638,6 +668,26 @@ 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. + /// + /// [`syn::Ident`]: struct@syn::Ident + // 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) -> Vec { + self.iter() + .enumerate() + .map(|(i, f)| f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))) + .collect() + } +} + #[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 0c4b180a..40bdf19a 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,53 @@ 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, + } + + #[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; + 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), + ); + assert_eq!( + format!("{:?}", TupleTransparent(&a)), + format!("{0:p}", &a), + ); + assert_eq!( + format!("{:?}", StructTransparent { field: &a }), + format!("{0:p}", &a), + ); + } + } } mod ignore { @@ -527,6 +574,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}")] + struct Tuple<'a, 'b>(&'a u8, &'b bool); + + #[derive(Debug)] + #[debug("{a:p} * {b:p}")] + 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 +755,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..2a9dbb8b 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), ); } } @@ -445,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, } @@ -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), + ); } } @@ -996,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 }, @@ -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), + ); } } @@ -1100,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 }, } @@ -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), + ); } } } @@ -1246,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 }, @@ -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), ); } } @@ -1437,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!( + Pointer::A(&a).to_string(), + format!("Pointer {0:p} A {0:p}", &a), + ); + assert_eq!( + Pointer::B(&b).to_string(), + format!("Pointer {0:p} B {0:p}", &b), + ); + } + } } } } @@ -2024,12 +2078,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}", 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: &42 }).len(), - 18, + format!("{:018p}", Enum::<&i8, _>::B { field: &a }), + format!("{:018p}", &a), ); } } @@ -2070,7 +2131,7 @@ mod generic { struct TupleUpperExp(T); #[derive(Display)] - #[display("{:p}", _0)] + #[display("{_0:p}")] struct TuplePointer(T); #[derive(Display)] @@ -2118,7 +2179,7 @@ mod generic { } #[derive(Display)] - #[display("{:p}", field)] + #[display("{field:p}")] struct StructPointer { field: T, } @@ -2181,7 +2242,7 @@ mod generic { #[derive(Display)] enum EnumPointer { - #[display("{:p}", _0)] + #[display("{_0:p}")] A(A), #[display("{field:p}")] B { field: B }, @@ -2245,12 +2306,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), ); } }