Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up PR 137 for merge #146

Merged
merged 12 commits into from Aug 28, 2021
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down
29 changes: 26 additions & 3 deletions impl/src/expand.rs
Expand Up @@ -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())
}
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
24 changes: 24 additions & 0 deletions impl/src/prop.rs
Expand Up @@ -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<'_> {
Expand Down Expand Up @@ -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<'_> {
Expand Down Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions src/lib.rs
Expand Up @@ -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
Expand Down
39 changes: 37 additions & 2 deletions tests/test_backtrace.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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());

Expand All @@ -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;
Expand Down Expand Up @@ -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("...")]
Expand Down Expand Up @@ -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());

Expand Down