Skip to content

Commit

Permalink
Replace DisplayAsDisplay and PathAsDisplay with AsDisplay trait
Browse files Browse the repository at this point in the history
Rather than having separate traits implementing as_display method,
replace DisplayAsDisplay and PathAsDisplay traits with a AsDisplay
trait.  The difference between those two traits is in the result
returned by the as_display method.  With AsDisplay trait this is
captured by an associated type.

The main motivation for the change is making it simpler to support
no_std builds in the future.  Previously, PathAsDisplay would have to
be handled specially in such builds on the side of macro expansion.
Now, thiserror-impl doesn’t need to be aware of any complications
around AsDisplay since they are all contained in thiserror crate.
  • Loading branch information
mina86 committed Sep 1, 2023
1 parent 0495eaa commit 97eeb45
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 33 deletions.
32 changes: 15 additions & 17 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,7 @@ fn impl_struct(input: Struct) -> TokenStream {
})
} else if let Some(display) = &input.attrs.display {
display_implied_bounds = display.implied_bounds.clone();
let use_as_display = if display.has_bonus_display {
Some(quote! {
#[allow(unused_imports)]
use thiserror::__private::{DisplayAsDisplay, PathAsDisplay};
})
} else {
None
};
let use_as_display = impl_use_as_display(display.has_bonus_display);
let pat = fields_pat(&input.fields);
Some(quote! {
#use_as_display
Expand Down Expand Up @@ -351,19 +344,13 @@ fn impl_enum(input: Enum) -> TokenStream {

let display_impl = if input.has_display() {
let mut display_inferred_bounds = InferredBounds::new();
let use_as_display = if input.variants.iter().any(|v| {
let use_as_display = input.variants.iter().any(|v| {
v.attrs
.display
.as_ref()
.map_or(false, |display| display.has_bonus_display)
}) {
Some(quote! {
#[allow(unused_imports)]
use thiserror::__private::{DisplayAsDisplay, PathAsDisplay};
})
} else {
None
};
});
let use_as_display = impl_use_as_display(use_as_display);
let void_deref = if input.variants.is_empty() {
Some(quote!(*))
} else {
Expand Down Expand Up @@ -466,6 +453,17 @@ fn fields_pat(fields: &[Field]) -> TokenStream {
}
}

fn impl_use_as_display(needs_as_display: bool) -> Option<TokenStream> {
if needs_as_display {
Some(quote! {
#[allow(unused_imports)]
use thiserror::__private::AsDisplay as _;
})
} else {
None
}
}

fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream {
let from_member = &from_field.member;
let some_source = if type_is_option(from_field.ty) {
Expand Down
53 changes: 39 additions & 14 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,55 @@
use std::fmt::Display;
use std::path::{self, Path, PathBuf};
use std::path::{Path, PathBuf};

#[doc(hidden)]
pub trait DisplayAsDisplay {
fn as_display(&self) -> Self;
pub trait AsDisplay {
type Target: Display + ?Sized;

fn as_display(&self) -> &Self::Target;
}

impl<T: Display> DisplayAsDisplay for &T {
fn as_display(&self) -> Self {
impl<T: Display> AsDisplay for &T {
type Target = T;

fn as_display(&self) -> &Self::Target {
self
}
}

#[doc(hidden)]
pub trait PathAsDisplay {
fn as_display(&self) -> path::Display<'_>;
impl AsDisplay for Path {
type Target = PathDisplay;

#[inline]
fn as_display(&self) -> &Self::Target {
PathDisplay::new(self)
}
}

impl PathAsDisplay for Path {
fn as_display(&self) -> path::Display<'_> {
self.display()
impl AsDisplay for PathBuf {
type Target = PathDisplay;

#[inline]
fn as_display(&self) -> &Self::Target {
PathDisplay::new(self.as_path())
}
}

#[doc(hidden)]
#[repr(transparent)]
pub struct PathDisplay(Path);

impl PathDisplay {
#[inline]
fn new(path: &Path) -> &Self {
// SAFETY: PathDisplay is repr(transparent) so casting pointers between
// it and its payload is safe.
unsafe { &*(path as *const Path as *const Self) }
}
}

impl PathAsDisplay for PathBuf {
fn as_display(&self) -> path::Display<'_> {
self.display()
impl Display for PathDisplay {
#[inline]
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.display().fmt(fmtr)
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ pub mod __private {
#[doc(hidden)]
pub use crate::aserror::AsDynError;
#[doc(hidden)]
pub use crate::display::{DisplayAsDisplay, PathAsDisplay};
pub use crate::display::AsDisplay;
#[cfg(error_generic_member_access)]
#[doc(hidden)]
pub use crate::provide::ThiserrorProvide;
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/no-display.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ error[E0599]: the method `as_display` exists for reference `&NoDisplay`, but its
|
= note: the following trait bounds were not satisfied:
`NoDisplay: std::fmt::Display`
which is required by `&NoDisplay: DisplayAsDisplay`
which is required by `&NoDisplay: AsDisplay`
note: the trait `std::fmt::Display` must be implemented
--> $RUST/core/src/fmt/mod.rs
|
Expand Down

0 comments on commit 97eeb45

Please sign in to comment.