Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/double reference #354

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions impl/src/fmt/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use crate::utils::{attr::ParseMultiple as _, Spanning};

use super::{trait_name_to_attribute_name, ContainerAttributes};

const POINTER_TRAIT_NAME: &str = "Pointer";

/// Expands a [`fmt::Display`]-like derive macro.
///
/// Available macros:
Expand Down Expand Up @@ -94,8 +96,14 @@ fn expand_struct(
.ident
.clone()
.map_or_else(|| syn::Member::Unnamed(i.into()), syn::Member::Named);
quote! {
let #var = &self.#member;
if let syn::Type::Reference(_) = f.ty {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict this only works for references, and will fail as soon as you use other pointer types, such as Box<T>.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I will think about it more.

quote! {
let #var = self.#member;
}
} else {
quote! {
let #var = &self.#member;
}
}
});

Expand Down Expand Up @@ -251,7 +259,7 @@ impl<'a> Expansion<'a> {
let trait_ident = self.trait_ident;

Ok(quote! {
derive_more::core::fmt::#trait_ident::fmt(#ident, __derive_more_f)
derive_more::core::fmt::#trait_ident::fmt(&#ident, __derive_more_f)
})
}
_ => Err(syn::Error::new(
Expand All @@ -265,6 +273,30 @@ impl<'a> Expansion<'a> {
}
}

/// Get the type to be bound by the trait to be derived.
/// For example, deriving `Display` for `Struct<E>(E)` requires the type `E`
/// to have the trait bound `Display`.
///
/// For traits other than `Pointer`, all layers of `&` on the outside are
/// stripped because the trait's format function does the same.
fn get_bound_constrained_type<'b, 'c>(
&'c self,
input_type: &'b syn::Type,
trait_ident: &'b syn::Ident
) -> &'b syn::Type {
let mut ty = input_type;
if !trait_ident.eq(POINTER_TRAIT_NAME) {
loop {
if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
ty = elem;
} else {
break;
}
}
}
ty
}

/// Generates trait bounds for a struct or an enum variant.
fn generate_bounds(&self) -> Vec<syn::WherePredicate> {
let Some(fmt) = &self.attrs.fmt else {
Expand All @@ -273,7 +305,7 @@ impl<'a> Expansion<'a> {
.iter()
.next()
.map(|f| {
let ty = &f.ty;
let ty = self.get_bound_constrained_type(&f.ty, &self.trait_ident);
let trait_ident = &self.trait_ident;
vec![parse_quote! { #ty: derive_more::core::fmt::#trait_ident }]
})
Expand Down
49 changes: 49 additions & 0 deletions tests/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,55 @@ mod structs {
}
}
}

mod unnamed_pointer {
use super::*;
use std::fmt;

#[derive(Display)]
#[display("{_0:p}")]
struct UnnamedPointer<'a>(&'a u32);

#[derive(Display)]
#[display("{}", format!("{_0:p}"))]
struct UnnamedPointerWithFormat<'a>(&'a u32);

#[derive(Pointer)]
struct UnnamedPointerDerivePointer<'a, E>(&'a E);

#[test]
fn assert_no_double_reference() {
let i: u32 = line!();
assert_eq!(
format!("{}", UnnamedPointer(&i)),
format!("{:p}", &i)
);
}

#[test]
fn assert_no_double_reference_with_format() {
let i: u32 = line!();
assert_eq!(
format!("{}", UnnamedPointerWithFormat(&i)),
format!("{:p}", &i)
);
}

#[test]
fn assert_no_double_reference_deriving_pointer() {
let i: u32 = line!();
let j: &u32 = &i;
let k: &&u32 = &j;
assert_eq!(
format!("{:p}", UnnamedPointerDerivePointer(j)),
format!("{:p}", j)
);
assert_eq!(
format!("{:p}", UnnamedPointerDerivePointer(k)),
format!("{:p}", k)
);
}
}
}

mod enums {
Expand Down
Loading