From c5e5e82c0ade464539c21b055bd26378579e26e8 Mon Sep 17 00:00:00 2001 From: Kai Ren Date: Sat, 1 Feb 2025 16:33:09 +0100 Subject: [PATCH] Fix unsized fields usage in `Display`/`Debug` derives (#440, #432) --- CHANGELOG.md | 2 + impl/src/fmt/mod.rs | 8 +- tests/debug.rs | 238 ++++++++++++++++++++++++++++++++++++++++++++ tests/display.rs | 203 +++++++++++++++++++++++++++++++++++++ 4 files changed, 449 insertions(+), 2 deletions(-) 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 diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs index 70ed4076..63041a06 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,9 @@ 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::>(); 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 diff --git a/tests/display.rs b/tests/display.rs index 4120e3ac..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::*; @@ -697,6 +771,60 @@ mod structs { } } + mod r#unsized { + use super::*; + + mod interpolated { + use core::ptr; + + 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 { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple1) + }; + assert_eq!(t1.to_string(), "3.14"); + let t2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Tuple2) + }; + assert_eq!(t2.to_string(), "3.14"); + let s1 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct1) + }; + assert_eq!(s1.to_string(), "3.14"); + let s2 = unsafe { + &*(ptr::addr_of!(dat) as *const [i32] as *const Struct2) + }; + assert_eq!(s2.to_string(), "3.14"); + } + } + } + #[cfg(nightly)] mod never { use super::*; @@ -2969,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