commit
2e2c126342
14
README.md
14
README.md
|
@ -137,6 +137,20 @@ pub enum DataStoreError {
|
|||
}
|
||||
```
|
||||
|
||||
- If a field is both a source (named `source`, or has `#[source]` or `#[from]`
|
||||
attribute) *and* is marked `#[backtrace]`, then the Error trait's
|
||||
`backtrace()` method is forwarded to the source's backtrace.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
Io {
|
||||
#[backtrace]
|
||||
source: io::Error,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Errors may use `error(transparent)` to forward the source and Display methods
|
||||
straight through to an underlying error without adding an additional message.
|
||||
This would be appropriate for enums that need an "anything else" variant.
|
||||
|
|
|
@ -58,7 +58,9 @@ fn impl_struct(input: Struct) -> TokenStream {
|
|||
self.#source.as_dyn_error().backtrace()
|
||||
}
|
||||
};
|
||||
let combinator = if type_is_option(backtrace_field.ty) {
|
||||
let combinator = if source == backtrace {
|
||||
source_backtrace
|
||||
} else if type_is_option(backtrace_field.ty) {
|
||||
quote! {
|
||||
#source_backtrace.or(self.#backtrace.as_ref())
|
||||
}
|
||||
|
@ -128,7 +130,7 @@ fn impl_struct(input: Struct) -> TokenStream {
|
|||
});
|
||||
|
||||
let from_impl = input.from_field().map(|from_field| {
|
||||
let backtrace_field = input.backtrace_field();
|
||||
let backtrace_field = input.distinct_backtrace_field();
|
||||
let from = from_field.ty;
|
||||
let body = from_initializer(from_field, backtrace_field);
|
||||
quote! {
|
||||
|
@ -238,6 +240,27 @@ fn impl_enum(input: Enum) -> TokenStream {
|
|||
}
|
||||
}
|
||||
}
|
||||
(Some(backtrace_field), Some(source_field))
|
||||
if backtrace_field.member == source_field.member =>
|
||||
{
|
||||
let backtrace = &backtrace_field.member;
|
||||
let varsource = quote!(source);
|
||||
let source_backtrace = if type_is_option(source_field.ty) {
|
||||
quote_spanned! {backtrace.span()=>
|
||||
#varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace())
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {backtrace.span()=>
|
||||
#varsource.as_dyn_error().backtrace()
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#ty::#ident {#backtrace: #varsource, ..} => {
|
||||
use thiserror::private::AsDynError;
|
||||
#source_backtrace
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(backtrace_field), _) => {
|
||||
let backtrace = &backtrace_field.member;
|
||||
let body = if type_is_option(backtrace_field.ty) {
|
||||
|
@ -326,7 +349,7 @@ fn impl_enum(input: Enum) -> TokenStream {
|
|||
|
||||
let from_impls = input.variants.iter().filter_map(|variant| {
|
||||
let from_field = variant.from_field()?;
|
||||
let backtrace_field = variant.backtrace_field();
|
||||
let backtrace_field = variant.distinct_backtrace_field();
|
||||
let variant = &variant.ident;
|
||||
let from = from_field.ty;
|
||||
let body = from_initializer(from_field, backtrace_field);
|
||||
|
|
|
@ -13,6 +13,11 @@ impl Struct<'_> {
|
|||
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
||||
backtrace_field(&self.fields)
|
||||
}
|
||||
|
||||
pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
|
||||
let backtrace_field = self.backtrace_field()?;
|
||||
distinct_backtrace_field(backtrace_field, self.from_field())
|
||||
}
|
||||
}
|
||||
|
||||
impl Enum<'_> {
|
||||
|
@ -54,6 +59,11 @@ impl Variant<'_> {
|
|||
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
||||
backtrace_field(&self.fields)
|
||||
}
|
||||
|
||||
pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
|
||||
let backtrace_field = self.backtrace_field()?;
|
||||
distinct_backtrace_field(backtrace_field, self.from_field())
|
||||
}
|
||||
}
|
||||
|
||||
impl Field<'_> {
|
||||
|
@ -100,6 +110,20 @@ fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
|
|||
None
|
||||
}
|
||||
|
||||
// The #[backtrace] field, if it is not the same as the #[from] field.
|
||||
fn distinct_backtrace_field<'a, 'b>(
|
||||
backtrace_field: &'a Field<'b>,
|
||||
from_field: Option<&Field>,
|
||||
) -> Option<&'a Field<'b>> {
|
||||
if from_field.map_or(false, |from_field| {
|
||||
from_field.member == backtrace_field.member
|
||||
}) {
|
||||
None
|
||||
} else {
|
||||
Some(backtrace_field)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_is_backtrace(ty: &Type) -> bool {
|
||||
let path = match ty {
|
||||
Type::Path(ty) => &ty.path,
|
||||
|
|
16
src/lib.rs
16
src/lib.rs
|
@ -161,6 +161,22 @@
|
|||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! - If a field is both a source (named `source`, or has `#[source]` or
|
||||
//! `#[from]` attribute) *and* is marked `#[backtrace]`, then the Error
|
||||
//! trait's `backtrace()` method is forwarded to the source's backtrace.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum MyError {
|
||||
//! Io {
|
||||
//! #[backtrace]
|
||||
//! source: io::Error,
|
||||
//! },
|
||||
//! }
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! - Errors may use `error(transparent)` to forward the source and Display
|
||||
//! methods straight through to an underlying error without adding an
|
||||
//! additional message. This would be appropriate for enums that need an
|
||||
|
|
|
@ -10,9 +10,16 @@ use thiserror::Error;
|
|||
#[error("...")]
|
||||
pub struct Inner;
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct InnerBacktrace {
|
||||
backtrace: std::backtrace::Backtrace,
|
||||
}
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod structs {
|
||||
use super::Inner;
|
||||
use super::{Inner, InnerBacktrace};
|
||||
use std::backtrace::Backtrace;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
@ -54,6 +61,14 @@ pub mod structs {
|
|||
backtrace: Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct CombinedBacktraceFrom {
|
||||
#[from]
|
||||
#[backtrace]
|
||||
source: InnerBacktrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptBacktraceFrom {
|
||||
|
@ -97,6 +112,11 @@ pub mod structs {
|
|||
let error = BacktraceFrom::from(Inner);
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
let error = CombinedBacktraceFrom::from(InnerBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
});
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
let error = OptBacktraceFrom::from(Inner);
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
|
@ -107,7 +127,7 @@ pub mod structs {
|
|||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod enums {
|
||||
use super::Inner;
|
||||
use super::{Inner, InnerBacktrace};
|
||||
use std::backtrace::Backtrace;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
@ -157,6 +177,16 @@ pub mod enums {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CombinedBacktraceFrom {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[from]
|
||||
#[backtrace]
|
||||
source: InnerBacktrace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptBacktraceFrom {
|
||||
#[error("...")]
|
||||
|
@ -204,6 +234,11 @@ pub mod enums {
|
|||
let error = BacktraceFrom::from(Inner);
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
let error = CombinedBacktraceFrom::from(InnerBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
});
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
let error = OptBacktraceFrom::from(Inner);
|
||||
assert!(error.backtrace().is_some());
|
||||
|
||||
|
|
Loading…
Reference in New Issue