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), ); } }