Skip to content

Commit

Permalink
Fix field transparency for top-level shared attribute in Display (#438
Browse files Browse the repository at this point in the history
)
  • Loading branch information
tyranron authored Jan 18, 2025
1 parent f14c7a7 commit d391493
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 29 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#431](https://github.com/JelteF/derive_more/pull/431))
- Incorrect rendering of raw identifiers as field names in `Debug` expansions.
([#431](https://github.com/JelteF/derive_more/pull/431))
- Top-level `#[display("...")]` attribute on an enum not working transparently
for directly specified fields.
([#438](https://github.com/JelteF/derive_more/pull/438))


## 1.0.0 - 2024-08-07
Expand Down
24 changes: 9 additions & 15 deletions impl/src/fmt/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::utils::{

use super::{
trait_name_to_attribute_name, ContainerAttributes, ContainsGenericsExt as _,
FieldsExt as _, FmtAttribute,
FmtAttribute,
};

/// Expands a [`fmt::Debug`] derive macro.
Expand Down Expand Up @@ -250,23 +250,17 @@ impl Expansion<'_> {
/// [`Debug::fmt()`]: std::fmt::Debug::fmt()
fn generate_body(&self) -> syn::Result<TokenStream> {
if let Some(fmt) = &self.attr.fmt {
return Ok(if let Some((expr, trait_ident)) = fmt.transparent_call() {
let expr = if let Some(field) = self
.fields
.fmt_args_idents()
.find(|field| expr == *field || expr == field.unraw())
return Ok(
if let Some((expr, trait_ident)) =
fmt.transparent_call_on_fields(self.fields)
{
quote! { #field }
quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) }
} else {
quote! { &(#expr) }
};

quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) }
} else {
let deref_args = fmt.additional_deref_args(self.fields);
let deref_args = fmt.additional_deref_args(self.fields);

quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) }
});
quote! { derive_more::core::write!(__derive_more_f, #fmt, #(#deref_args),*) }
},
);
};

match self.fields {
Expand Down
26 changes: 12 additions & 14 deletions impl/src/fmt/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::utils::{attr::ParseMultiple as _, Spanning};

use super::{
trait_name_to_attribute_name, ContainerAttributes, ContainsGenericsExt as _,
FieldsExt as _, FmtAttribute,
FmtAttribute,
};

/// Expands a [`fmt::Display`]-like derive macro.
Expand Down Expand Up @@ -291,17 +291,9 @@ impl Expansion<'_> {
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 let Some(field) = self
.fields
.fmt_args_idents()
.find(|field| expr == *field || expr == field.unraw())
{
quote! { #field }
} else {
quote! { &(#expr) }
};

} else if let Some((expr, trait_ident)) =
fmt.transparent_call_on_fields(self.fields)
{
quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) }
} else {
let deref_args = fmt.additional_deref_args(self.fields);
Expand Down Expand Up @@ -358,8 +350,14 @@ impl Expansion<'_> {
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, #(#deref_args),*)
let shared_body = if let Some((expr, trait_ident)) =
shared_fmt.transparent_call_on_fields(self.fields)
{
quote! { derive_more::core::fmt::#trait_ident::fmt(#expr, __derive_more_f) }
} else {
quote! {
derive_more::core::write!(__derive_more_f, #shared_fmt, #(#deref_args),*)
}
};

body = if body.is_empty() {
Expand Down
25 changes: 25 additions & 0 deletions impl/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use quote::{format_ident, quote, ToTokens};
use syn::{
ext::IdentExt as _,
parse::{Parse, ParseStream},
parse_quote,
punctuated::Punctuated,
spanned::Spanned as _,
token,
Expand Down Expand Up @@ -199,6 +200,30 @@ impl FmtAttribute {
Some((expr, format_ident!("{trait_name}")))
}

/// Same as [`transparent_call()`], but additionally checks the returned [`Expr`] whether it's
/// one of the [`fmt_args_idents`] of the provided [`syn::Fields`], and makes it suitable for
/// passing directly into the transparent call of the delegated formatting trait.
///
/// [`fmt_args_idents`]: FieldsExt::fmt_args_idents
/// [`transparent_call()`]: FmtAttribute::transparent_call
fn transparent_call_on_fields(
&self,
fields: &syn::Fields,
) -> Option<(Expr, syn::Ident)> {
self.transparent_call().map(|(expr, trait_ident)| {
let expr = if let Some(field) = fields
.fmt_args_idents()
.find(|field| expr == *field || expr == field.unraw())
{
field.into()
} else {
parse_quote! { &(#expr) }
};

(expr, trait_ident)
})
}

/// Returns an [`Iterator`] over bounded [`syn::Type`]s (and correspondent trait names) by this
/// [`FmtAttribute`].
fn bounded_types<'a>(
Expand Down
Loading

0 comments on commit d391493

Please sign in to comment.