Skip to content

Commit

Permalink
Initial test of having a separate Display derive
Browse files Browse the repository at this point in the history
  • Loading branch information
Santiago Cingolani committed Apr 16, 2024
1 parent 508ece8 commit 3069bd4
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 36 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Expand Up @@ -11,6 +11,10 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/dtolnay/thiserror"
rust-version = "1.56"

[features]
default = ["std"]
std = []

[dependencies]
thiserror-impl = { version = "=1.0.58", path = "impl" }

Expand Down
97 changes: 65 additions & 32 deletions impl/src/expand.rs
Expand Up @@ -7,52 +7,73 @@ use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::collections::BTreeSet as Set;
use syn::{DeriveInput, GenericArgument, Member, PathArguments, Result, Token, Type};

pub fn derive(input: &DeriveInput) -> TokenStream {
match try_expand(input) {
#[derive(Clone, Copy)]
pub enum DeriveType {
Error,
Display,
}

pub fn derive(input: &DeriveInput, derive_type: DeriveType) -> TokenStream {
match try_expand(input, derive_type) {
Ok(expanded) => expanded,
// If there are invalid attributes in the input, expand to an Error impl
// anyway to minimize spurious knock-on errors in other code that uses
// this type as an Error.
Err(error) => fallback(input, error),
Err(error) => fallback(input, error, derive_type),
}
}

fn try_expand(input: &DeriveInput) -> Result<TokenStream> {
fn try_expand(input: &DeriveInput, derive_type: DeriveType) -> Result<TokenStream> {
let input = Input::from_syn(input)?;
input.validate()?;
Ok(match input {
Input::Struct(input) => impl_struct(input),
Input::Enum(input) => impl_enum(input),
Input::Struct(input) => impl_struct(input, derive_type),
Input::Enum(input) => impl_enum(input, derive_type),
})
}

fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream {
fn fallback(input: &DeriveInput, error: syn::Error, derive_type: DeriveType) -> TokenStream {
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

let error = error.to_compile_error();

quote! {
#error

let error_impl = quote! {
#[allow(unused_qualifications)]
impl #impl_generics std::error::Error for #ty #ty_generics #where_clause
where
// Work around trivial bounds being unstable.
// https://github.com/rust-lang/rust/issues/48214
for<'workaround> #ty #ty_generics: ::core::fmt::Debug,
{}
};

#[allow(unused_qualifications)]
impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::unreachable!()
let display_impl = quote! { #[allow(unused_qualifications)]
impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::unreachable!()
}
}};

match derive_type {
DeriveType::Error => quote! {
#error

#error_impl

#display_impl
},
DeriveType::Display => {
quote! {
#error

#display_impl
}
}
}
}

fn impl_struct(input: Struct) -> TokenStream {
fn impl_struct(input: Struct, derive_type: DeriveType) -> TokenStream {
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut error_inferred_bounds = InferredBounds::new();
Expand Down Expand Up @@ -209,18 +230,24 @@ fn impl_struct(input: Struct) -> TokenStream {
}
let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);

quote! {
#[allow(unused_qualifications)]
impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause {
#source_method
#provide_method
}
#display_impl
#from_impl
match derive_type {
DeriveType::Error => quote! {
#[allow(unused_qualifications)]
impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause {
#source_method
#provide_method
}
#display_impl
#from_impl
},
DeriveType::Display => quote! {
#display_impl
#from_impl
},
}
}

fn impl_enum(input: Enum) -> TokenStream {
fn impl_enum(input: Enum, derive_type: DeriveType) -> TokenStream {
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut error_inferred_bounds = InferredBounds::new();
Expand Down Expand Up @@ -465,14 +492,20 @@ fn impl_enum(input: Enum) -> TokenStream {
}
let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);

quote! {
#[allow(unused_qualifications)]
impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause {
#source_method
#provide_method
}
#display_impl
#(#from_impls)*
match derive_type {
DeriveType::Error => quote! {
#[allow(unused_qualifications)]
impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause {
#source_method
#provide_method
}
#display_impl
#(#from_impls)*
},
DeriveType::Display => quote! {
#display_impl
#(#from_impls)*
},
}
}

Expand Down
8 changes: 7 additions & 1 deletion impl/src/lib.rs
Expand Up @@ -32,5 +32,11 @@ use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Error, attributes(backtrace, error, from, source))]
pub fn derive_error(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive(&input).into()
expand::derive(&input, expand::DeriveType::Error).into()
}

#[proc_macro_derive(Display, attributes(backtrace, error, from, source))]
pub fn derive_display(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive(&input, expand::DeriveType::Display).into()
}
5 changes: 4 additions & 1 deletion src/display.rs
@@ -1,4 +1,5 @@
use std::fmt::Display;
use core::fmt::Display;
#[cfg(feature = "std")]
use std::path::{self, Path, PathBuf};

#[doc(hidden)]
Expand All @@ -21,6 +22,7 @@ where
}
}

#[cfg(feature = "std")]
impl<'a> AsDisplay<'a> for Path {
type Target = path::Display<'a>;

Expand All @@ -30,6 +32,7 @@ impl<'a> AsDisplay<'a> for Path {
}
}

#[cfg(feature = "std")]
impl<'a> AsDisplay<'a> for PathBuf {
type Target = path::Display<'a>;

Expand Down
10 changes: 8 additions & 2 deletions src/lib.rs
Expand Up @@ -228,6 +228,7 @@
//!
//! [`anyhow`]: https://github.com/dtolnay/anyhow

#![no_std]
#![doc(html_root_url = "https://docs.rs/thiserror/1.0.58")]
#![allow(
clippy::module_name_repetitions,
Expand All @@ -240,21 +241,26 @@
#[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))]
compile_error!("Build script probe failed to compile.");

#[cfg(feature = "std")]
extern crate std;

#[cfg(feature = "std")]
mod aserror;
mod display;
#[cfg(error_generic_member_access)]
#[cfg(all(error_generic_member_access, feature = "std"))]
mod provide;

pub use thiserror_impl::*;

// Not public API.
#[doc(hidden)]
pub mod __private {
#[cfg(feature = "std")]
#[doc(hidden)]
pub use crate::aserror::AsDynError;
#[doc(hidden)]
pub use crate::display::AsDisplay;
#[cfg(error_generic_member_access)]
#[cfg(all(error_generic_member_access, feature = "std"))]
#[doc(hidden)]
pub use crate::provide::ThiserrorProvide;
}

0 comments on commit 3069bd4

Please sign in to comment.