From efa8f4dfcf290883f1b2f84803eb21917526154e Mon Sep 17 00:00:00 2001 From: Danny McClanahan <1305167+cosmicexplorer@users.noreply.github.com> Date: Wed, 16 Jun 2021 10:57:40 -0700 Subject: [PATCH] remove workaround --- src/expand.rs | 28 ++----------------- src/lib.rs | 74 ++++----------------------------------------------- 2 files changed, 7 insertions(+), 95 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 7591697..64f7f4b 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -288,12 +288,7 @@ fn ensure_display_in_where_clause_for_type(where_clause: &mut WhereClause, ident Some(WherePredicate::Type(ref mut pred_ty)) => { add_display_constraint_to_type_predicate(pred_ty) } - x => { - unreachable!( - "we ensured this by pushing a type predicate just above, but got: {:?}", - x - ) - } + _ => unreachable!("we ensured this by pushing a type predicate just above"), } }) } @@ -307,15 +302,7 @@ fn ensure_where_clause_has_display_for_all_members( let param_constraint_mapping = extract_trait_constraints_from_source(where_clause, type_params); for (ident, known_bounds) in param_constraint_mapping.into_iter() { - // NB: if the type param implements *anything* named "Display", we will - // assume that means they implement core::fmt::Display! This is not - // a perfect heuristic! - let probably_impls_display: bool = known_bounds.iter().any(|trait_bound| { - assert!(!trait_bound.path.segments.is_empty()); - let final_segment_ident: &Ident = &trait_bound.path.segments.last().unwrap().ident; - &format!("{}", final_segment_ident) == "Display" - }); - if !probably_impls_display { + if known_bounds.is_empty() { ensure_display_in_where_clause_for_type(where_clause, ident); } } @@ -334,17 +321,6 @@ fn ensure_where_clause_has_display_for_all_members( /// parameter may not be used in a way that affects whether the enum cases impl Display. It /// would be nice to be able to ask rustc "does this type argument impl (e.g.) /// core::fmt::Display?". -/// However, the solution in this method also allows the user to bypass this check in the case they -/// are already impling `Display` for that type param somehow, by adding something like: -/// mod inner { trait Display = core::any::Any; } -/// #[derive(Display)] -/// pub enum Error { -/// // We will allow the normal compiler failure to occur -/// // if T ends up not impling Display. Here, we demonstrate -/// // the use of Debug. -/// /// failure of type T: {0:?} -/// ParameterizedFailure(T), -/// } fn generate_where_clause(generics: &Generics, where_clause: Option<&WhereClause>) -> WhereClause { let mut where_clause = where_clause.cloned().unwrap_or_else(new_empty_where_clause); let type_params: Vec<&TypeParam> = generics.type_params().collect(); diff --git a/src/lib.rs b/src/lib.rs index aaa4e2b..4c94a42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,83 +135,19 @@ use syn::{parse_macro_input, DeriveInput}; /// formatted with `{:?}` via [`displaydoc`][crate], and a generic type /// parameter must implement `Debug` to do that, then that struct or enum /// definition will need to propagate the `Debug` constraint to every type -/// parameter it's instantiated with. This can become a problem when writing -/// generic code: -/// ```compile_fail -/// use core::fmt::Debug; -/// use displaydoc::Display; -/// -/// #[derive(Debug)] -/// pub struct MyType; -/// -/// // This docstring uses the `Debug` format specifier ":?": -/// /// oh no, an error: {0:?} -/// #[derive(Display)] -/// pub struct Error(pub E); -/// -/// // Error: the trait `std::fmt::Display` is not implemented for `MyType`. -/// assert!("oh no, an error: MyType" == &format!("{}", Error(MyType))); -/// ``` -/// If this is a problem for you, it is recommended to instead: -/// 1. require [`fmt::Display`][core::fmt::Display] for any desired -/// parameterized type, -/// 2. use the `{}` format specifier for the struct or enum field, and -/// 3. make use of the workaround described in -/// [Generic Type Parameters][crate#generic-type-parameters] to avoid pinning -/// specifically the `fmt::Display` trait at the definition site. -/// -/// Consumers of your generic API should be able to use `displaydoc::Display` to -/// implement `fmt::Display` for the concrete types they provide to your -/// parameterized errors without much boilerplate. -/// -/// #### Trick the Macro to Avoid Requiring Everything to `Display` to `Display` -/// However, there may be cases where some type parameter can't be made to -/// implement `fmt::Display` or can't use the `displaydoc::Display` derive macro -/// for some reason. For example, this won't work immediately: -/// ```compile_fail +/// parameter it's instantiated with: +/// ```rust /// use core::fmt::Debug; /// use displaydoc::Display; /// -/// #[derive(Debug)] -/// pub struct MyType; -/// /// /// oh no, an error: {0:?} /// #[derive(Display)] /// pub struct Error(pub E); /// -/// // Still fails to compile because `displaydoc`'s `fmt::Display` implementation -/// // assumes all type parameters need to require `fmt::Display` themselves: -/// assert!("oh no, an error: MyType" == &format!("{}", Error(MyType))); -/// ``` -/// We would like to be able to implement our own `Display` for any struct or -/// enum without requiring the type parameter to itself implement `Display`. In -/// such cases, the macro can be misled away from adding a `where E: Display` -/// requirement to its generated `fmt::Display` implementation by creating -/// a local trait *named* "Display". This allows the above example to -/// successfully compile: -/// ```rust -/// use core::fmt::Debug; -/// use displaydoc::Display; -/// -/// #[derive(Debug)] -/// pub struct MyType; -/// -/// mod inner { -/// use core::fmt::Debug; -/// // `#[derive(Display)]` checks for any constraint named "Display" and will -/// // avoid placing a `where E: core::fmt::Display` on that type param if it -/// // sees one. -/// pub trait Display: Debug {} -/// // This means the type is basically `Error` though. -/// impl Display for E where E: Debug {} -/// } -/// -/// /// oh no, an error: {0:?} -/// #[derive(Display)] -/// pub struct Error(pub E); +/// // `E: Debug` now has to propagate to callers. +/// fn generate_error(e: E) -> Error { Error(e) } /// -/// // MyType still does not itself need to impl `Display` at all! -/// assert!("oh no, an error: MyType" == &format!("{}", Error(MyType))); +/// assert!("oh no, an error: \"cool\"" == &format!("{}", generate_error("cool"))); /// ``` #[proc_macro_derive(Display, attributes(ignore_extra_doc_attributes))] pub fn derive_error(input: TokenStream) -> TokenStream {