From 62400df27da3d88f380f0ee63b574f0ace2fc145 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 17:38:02 +0200 Subject: [PATCH 1/6] Showcase --- tests/display.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/display.rs b/tests/display.rs index 4120e3ac..3b502056 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -697,6 +697,54 @@ mod structs { } } + mod r#unsized { + use super::*; + + mod interpolated { + use super::*; + + #[derive(Display)] + #[display("{}.{}", _0, _1)] + struct Tuple1(char, str); + + #[derive(Display)] + #[display("{_0}.{_1}")] + struct Tuple2(char, str); + + #[derive(Display)] + #[display("{}.{}", head, tail)] + struct Struct1 { + head: char, + tail: str, + } + + #[derive(Display)] + #[display("{head}.{tail}")] + struct Struct2 { + head: char, + tail: str, + } + + #[test] + fn assert() { + let dat = [51i32, 3028017]; + + let t1 = + unsafe { &*(&raw const dat as *const [i32] as *const Tuple1) }; + assert_eq!(t1.to_string(), "3.14"); + let t2 = + unsafe { &*(&raw const dat as *const [i32] as *const Tuple2) }; + assert_eq!(t2.to_string(), "3.14"); + let s1 = + unsafe { &*(&raw const dat as *const [i32] as *const Struct1) }; + assert_eq!(s1.to_string(), "3.14"); + let s2 = + unsafe { &*(&raw const dat as *const [i32] as *const Struct2) }; + assert_eq!(s2.to_string(), "3.14"); + } + } + } + #[cfg(nightly)] mod never { use super::*; From b577bca4485f34ee2e884685c5ba4b251a8efbbf Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 17:55:40 +0200 Subject: [PATCH 2/6] Introduce fix --- impl/src/fmt/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 70ed4076..9fa9b846 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -306,7 +306,9 @@ 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 + /// [`syn::Fields`] and requiring it ([`fmt::Pointer`] ones). + /// + /// [`fmt::Pointer`]: std::fmt::Pointer fn additional_deref_args<'fmt: 'ret, 'fields: 'ret, 'ret>( &'fmt self, fields: &'fields syn::Fields, @@ -314,7 +316,7 @@ impl FmtAttribute { let used_args = Placeholder::parse_fmt_string(&self.lit.value()) .into_iter() .filter_map(|placeholder| match placeholder.arg { - Parameter::Named(name) => Some(name), + Parameter::Named(name) if placeholder.trait_name == "Pointer" => Some(name), _ => None, }) .collect::>(); From 0b74b2a3a6fa35cfa2e40e0ca9b4771170f8c456 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 18:03:15 +0200 Subject: [PATCH 3/6] Satisfy MSRV --- impl/src/fmt/mod.rs | 4 +++- tests/display.rs | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 9fa9b846..63041a06 100644 --- a/impl/src/fmt/mod.rs +++ b/impl/src/fmt/mod.rs @@ -316,7 +316,9 @@ impl FmtAttribute { let used_args = Placeholder::parse_fmt_string(&self.lit.value()) .into_iter() .filter_map(|placeholder| match placeholder.arg { - Parameter::Named(name) if placeholder.trait_name == "Pointer" => Some(name), + Parameter::Named(name) if placeholder.trait_name == "Pointer" => { + Some(name) + } _ => None, }) .collect::>(); diff --git a/tests/display.rs b/tests/display.rs index 3b502056..74364527 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -701,6 +701,8 @@ mod structs { use super::*; mod interpolated { + use core::ptr; + use super::*; #[derive(Display)] @@ -729,17 +731,21 @@ mod structs { fn assert() { let dat = [51i32, 3028017]; - let t1 = - unsafe { &*(&raw const dat as *const [i32] as *const Tuple1) }; + let t1 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple1) + }; assert_eq!(t1.to_string(), "3.14"); - let t2 = - unsafe { &*(&raw const dat as *const [i32] as *const Tuple2) }; + let t2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple2) + }; assert_eq!(t2.to_string(), "3.14"); - let s1 = - unsafe { &*(&raw const dat as *const [i32] as *const Struct1) }; + let s1 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct1) + }; assert_eq!(s1.to_string(), "3.14"); - let s2 = - unsafe { &*(&raw const dat as *const [i32] as *const Struct2) }; + let s2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct2) + }; assert_eq!(s2.to_string(), "3.14"); } } From 1006baf08915bc74d7ea90beee8721bb674027b9 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 19:24:59 +0200 Subject: [PATCH 4/6] More tests to the God of Tests! --- tests/display.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/tests/display.rs b/tests/display.rs index 74364527..478e1d94 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -530,6 +530,80 @@ mod structs { } } + mod r#unsized { + use core::ptr; + + use super::*; + + #[derive(Display)] + struct Tuple(str); + + #[derive(Display)] + struct Struct { + tail: str, + } + + #[test] + fn assert() { + let dat = "14"; + + let t = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple) }; + assert_eq!(t.to_string(), "14"); + let s = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct) }; + assert_eq!(s.to_string(), "14"); + } + + mod interpolated { + use core::ptr; + + use super::*; + + #[derive(Display)] + #[display("{}.", _0)] + struct Tuple1(str); + + #[derive(Display)] + #[display("{_0}.")] + struct Tuple2(str); + + #[derive(Display)] + #[display("{}.", tail)] + struct Struct1 { + tail: str, + } + + #[derive(Display)] + #[display("{tail}.")] + struct Struct2 { + tail: str, + } + + #[test] + fn assert() { + let dat = "14"; + + let t1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(t1.to_string(), "14."); + let t2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(t2.to_string(), "14."); + let s1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct1) + }; + assert_eq!(s1.to_string(), "14."); + let s2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct2) + }; + assert_eq!(s2.to_string(), "14."); + } + } + } + #[cfg(nightly)] mod never { use super::*; @@ -3023,6 +3097,81 @@ mod generic { } } } + + mod r#unsized { + use core::ptr; + + use super::*; + + #[derive(Display)] + struct Tuple(T); + + #[derive(Display)] + struct Struct { + tail: T, + } + + #[test] + fn assert() { + let dat = "14"; + + let t = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple) }; + assert_eq!(t.to_string(), "14"); + let s = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct) + }; + assert_eq!(s.to_string(), "14"); + } + + mod interpolated { + use core::ptr; + + use super::*; + + #[derive(Display)] + #[display("{}.", _0)] + struct Tuple1(T); + + #[derive(Display)] + #[display("{_0}.")] + struct Tuple2(T); + + #[derive(Display)] + #[display("{}.", tail)] + struct Struct1 { + tail: T, + } + + #[derive(Display)] + #[display("{tail}.")] + struct Struct2 { + tail: T, + } + + #[test] + fn assert() { + let dat = "14"; + + let t1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(t1.to_string(), "14."); + let t2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(t2.to_string(), "14."); + let s1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct1) + }; + assert_eq!(s1.to_string(), "14."); + let s2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct2) + }; + assert_eq!(s2.to_string(), "14."); + } + } + } } // See: https://github.com/JelteF/derive_more/issues/363 From 696617431a6f6f228677521aa7a3d69097a07cfa Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 19:39:52 +0200 Subject: [PATCH 5/6] Cover `Debug` with similar tests too --- tests/debug.rs | 238 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/tests/debug.rs b/tests/debug.rs index 32728282..8210d3e3 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -419,6 +419,84 @@ mod structs { } } + mod r#unsized { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + struct Tuple(str); + + #[derive(Debug)] + struct Struct { + tail: str, + } + + #[test] + fn assert() { + let dat = "14"; + + let t = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple) }; + assert_eq!(format!("{t:?}"), r#"Tuple("14")"#); + let s = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct) }; + assert_eq!(format!("{s:?}"), r#"Struct { tail: "14" }"#); + } + + mod interpolated { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + #[debug("{}.", _0)] + struct Tuple1(str); + + #[derive(Debug)] + #[debug("{_0}.")] + struct Tuple2(str); + + #[derive(Debug)] + #[debug("{}.", tail)] + struct Struct1 { + tail: str, + } + + #[derive(Debug)] + #[debug("{tail}.")] + struct Struct2 { + tail: str, + } + + #[test] + fn assert() { + let dat = "14"; + + let t1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(format!("{t1:?}"), "14."); + let t2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(format!("{t2:?}"), "14."); + let s1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct1) + }; + assert_eq!(format!("{s1:?}"), "14."); + let s2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct2) + }; + assert_eq!(format!("{s2:?}"), "14."); + } + } + } + #[cfg(nightly)] mod never { use derive_more::Debug; @@ -691,6 +769,87 @@ mod structs { } } + mod r#unsized { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + struct Tuple(char, str); + + #[derive(Debug)] + struct Struct { + head: char, + tail: str, + } + + #[test] + fn assert() { + let dat = [51i32, 3028017]; + + let t = + unsafe { &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple) }; + assert_eq!(format!("{t:?}"), r#"Tuple('3', "14")"#); + let s = + unsafe { &*(ptr::addr_of!(dat) as *const [i32] as *const Struct) }; + assert_eq!(format!("{s:?}"), r#"Struct { head: '3', tail: "14" }"#); + } + + mod interpolated { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + #[debug("{}.{}", _0, _1)] + struct Tuple1(char, str); + + #[derive(Debug)] + #[debug("{_0}.{_1}")] + struct Tuple2(char, str); + + #[derive(Debug)] + #[debug("{}.{}", head, tail)] + struct Struct1 { + head: char, + tail: str, + } + + #[derive(Debug)] + #[debug("{head}.{tail}")] + struct Struct2 { + head: char, + tail: str, + } + + #[test] + fn assert() { + let dat = [51i32, 3028017]; + + let t1 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(format!("{t1:?}"), "3.14"); + let t2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(format!("{t2:?}"), "3.14"); + let s1 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct1) + }; + assert_eq!(format!("{s1:?}"), "3.14"); + let s2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct2) + }; + assert_eq!(format!("{s2:?}"), "3.14"); + } + } + } + #[cfg(nightly)] mod never { use derive_more::Debug; @@ -2178,6 +2337,85 @@ mod generic { } } } + + mod r#unsized { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + struct Tuple(T); + + #[derive(Debug)] + struct Struct { + tail: T, + } + + #[test] + fn assert() { + let dat = "14"; + + let t = + unsafe { &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple) }; + assert_eq!(format!("{t:?}"), r#"Tuple("14")"#); + let s = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct) + }; + assert_eq!(format!("{s:?}"), r#"Struct { tail: "14" }"#); + } + + mod interpolated { + #[cfg(not(feature = "std"))] + use alloc::format; + use core::ptr; + + use derive_more::Debug; + + #[derive(Debug)] + #[debug("{}.", _0)] + struct Tuple1(T); + + #[derive(Debug)] + #[debug("{_0}.")] + struct Tuple2(T); + + #[derive(Debug)] + #[debug("{}.", tail)] + struct Struct1 { + tail: T, + } + + #[derive(Debug)] + #[debug("{tail}.")] + struct Struct2 { + tail: T, + } + + #[test] + fn assert() { + let dat = "14"; + + let t1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(format!("{t1:?}"), "14."); + let t2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(format!("{t2:?}"), "14."); + let s1 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct1) + }; + assert_eq!(format!("{s1:?}"), "14."); + let s2 = unsafe { + &*(ptr::addr_of!(*dat) as *const [i32] as *const Struct2) + }; + assert_eq!(format!("{s2:?}"), "14."); + } + } + } } // See: https://github.com/JelteF/derive_more/issues/301 From 7e7ad4abbe974c44e8071f8a59f3aac97ca33b08 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jan 2025 19:41:15 +0200 Subject: [PATCH 6/6] Mention in CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf7bfc9..5a7ac1fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Top-level `#[display("...")]` attribute on an enum not working transparently for directly specified fields. ([#438](https://github.com/JelteF/derive_more/pull/438)) +- Incorrect dereferencing of unsized fields in `Debug` and `Display` expansions. + ([#440](https://github.com/JelteF/derive_more/pull/440)) ## 1.0.0 - 2024-08-07