Merge pull request #146 from dtolnay/backtrace

Fix up PR 137 for merge
This commit is contained in:
David Tolnay 2021-08-28 14:16:40 -07:00 committed by GitHub
commit 2e2c126342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 5 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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());