Skip to content

Commit ab1260c

Browse files
committed
hide helper traits from calling code
Makes a couple of changes to avoid helper traits from appearing anywhere in the caller code. It changes the name of the `Display` trait method to reduce the chance that an inherent method conflicts with the type in question, causing undesirable results. It also wraps the helper trait and declarations into an anonymous const context, meaning the helper traits aren't visible to the calling context.
1 parent eb369ed commit ab1260c

File tree

2 files changed

+49
-38
lines changed

2 files changed

+49
-38
lines changed

src/expand.rs

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,46 @@ use quote::{format_ident, quote};
44
use syn::{Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Result};
55

66
pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
7-
match &input.data {
7+
let impls = match &input.data {
88
Data::Struct(data) => impl_struct(input, data),
99
Data::Enum(data) => impl_enum(input, data),
1010
Data::Union(_) => Err(Error::new_spanned(input, "Unions are not supported")),
11-
}
11+
}?;
12+
13+
let helpers = specialization();
14+
Ok(quote! {
15+
const _: () = {
16+
#helpers
17+
#impls
18+
};
19+
})
1220
}
1321

1422
#[cfg(feature = "std")]
1523
fn specialization() -> TokenStream {
1624
quote! {
1725
trait DisplayToDisplayDoc {
18-
fn get_display(&self) -> Self;
26+
fn __displaydoc_display(&self) -> &Self;
1927
}
2028

21-
impl<T: core::fmt::Display> DisplayToDisplayDoc for &T {
22-
fn get_display(&self) -> Self {
29+
impl<T: core::fmt::Display> DisplayToDisplayDoc for T {
30+
fn __displaydoc_display(&self) -> &Self {
2331
self
2432
}
2533
}
2634

2735
trait PathToDisplayDoc {
28-
fn get_display(&self) -> std::path::Display<'_>;
36+
fn __displaydoc_display(&self) -> std::path::Display<'_>;
2937
}
3038

3139
impl PathToDisplayDoc for std::path::Path {
32-
fn get_display(&self) -> std::path::Display<'_> {
40+
fn __displaydoc_display(&self) -> std::path::Display<'_> {
3341
self.display()
3442
}
3543
}
3644

3745
impl PathToDisplayDoc for std::path::PathBuf {
38-
fn get_display(&self) -> std::path::Display<'_> {
46+
fn __displaydoc_display(&self) -> std::path::Display<'_> {
3947
self.display()
4048
}
4149
}
@@ -74,13 +82,7 @@ fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
7482
}
7583
});
7684

77-
let needed_traits = specialization();
78-
79-
Ok(quote! {
80-
#needed_traits
81-
82-
#display
83-
})
85+
Ok(quote! { #display })
8486
}
8587

8688
fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
@@ -93,7 +95,7 @@ fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
9395
.map(|variant| attr::display(&variant.attrs))
9496
.collect::<Result<Vec<_>>>()?;
9597

96-
let display = if displays.iter().any(Option::is_some) {
98+
if displays.iter().any(Option::is_some) {
9799
let arms = data
98100
.variants
99101
.iter()
@@ -115,7 +117,7 @@ fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
115117
})
116118
})
117119
.collect::<Result<Vec<_>>>()?;
118-
Some(quote! {
120+
Ok(quote! {
119121
impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
120122
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
121123
#[allow(unused_variables)]
@@ -126,14 +128,6 @@ fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
126128
}
127129
})
128130
} else {
129-
return Err(Error::new_spanned(input, "Missing doc comments"));
130-
};
131-
132-
let needed_traits = specialization();
133-
134-
Ok(quote! {
135-
#needed_traits
136-
137-
#display
138-
})
131+
Err(Error::new_spanned(input, "Missing doc comments"))
132+
}
139133
}

src/fmt.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ use proc_macro2::TokenStream;
33
use quote::quote_spanned;
44
use syn::{Ident, LitStr};
55

6-
#[cfg(feature = "std")]
7-
const IS_STD: bool = true;
8-
#[cfg(not(feature = "std"))]
9-
const IS_STD: bool = false;
10-
116
macro_rules! peek_next {
127
($read:ident) => {
138
match $read.chars().next() {
@@ -49,8 +44,8 @@ impl Display {
4944

5045
let next = peek_next!(read);
5146

52-
let arg = if IS_STD && next == '}' {
53-
quote_spanned!(span=> , (&#ident).get_display())
47+
let arg = if cfg!(feature = "std") && next == '}' {
48+
quote_spanned!(span=> , #ident.__displaydoc_display())
5449
} else {
5550
quote_spanned!(span=> , #ident)
5651
};
@@ -120,11 +115,33 @@ mod tests {
120115
assert(
121116
"{v} {v:?} {0} {0:?}",
122117
"{} {:?} {} {:?}",
123-
", ( & v ) . get_display ( ) , v , ( & _0 ) . get_display ( ) , _0",
118+
", v . __displaydoc_display ( ) , v , _0 . __displaydoc_display ( ) , _0",
119+
);
120+
assert(
121+
"error {var}",
122+
"error {}",
123+
", var . __displaydoc_display ( )",
124+
);
125+
126+
assert(
127+
"The path {0}",
128+
"The path {}",
129+
", _0 . __displaydoc_display ( )",
130+
);
131+
assert("The path {0:?}", "The path {:?}", ", _0");
132+
}
133+
134+
#[test]
135+
#[cfg_attr(feature = "std", ignore)]
136+
fn test_nostd_expand() {
137+
assert(
138+
"{v} {v:?} {0} {0:?}",
139+
"{} {:?} {} {:?}",
140+
", v , v , _0 , _0",
124141
);
125-
assert("error {var}", "error {}", ", ( & var ) . get_display ( )");
142+
assert("error {var}", "error {}", ", var");
126143

127-
// assert("The path {0.display()}", "The path {}", "0.display()");
128-
// assert("The path {0.display():?}", "The path {:?}", "0.display()");
144+
assert("The path {0}", "The path {}", ", _0");
145+
assert("The path {0:?}", "The path {:?}", ", _0");
129146
}
130147
}

0 commit comments

Comments
 (0)