Skip to content

Commit

Permalink
Merge pull request 293 from LukasKalbertodt/use-stable-backtrace
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Dec 26, 2023
2 parents 5121cd2 + 49eeec1 commit d6acd22
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 46 deletions.
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,18 @@ anyhow = "1.0"
}
```

- If using the nightly channel, or stable with `features = ["backtrace"]`, a
backtrace is captured and printed with the error if the underlying error type
does not already provide its own. In order to see backtraces, they must be
enabled through the environment variables described in [`std::backtrace`]:
- If using Rust ≥ 1.65 or `features = ["backtrace"]`, a backtrace is captured
and printed with the error if the underlying error type does not already
provide its own. In order to see backtraces, they must be enabled through the
environment variables described in [`std::backtrace`]:

- If you want panics and errors to both have backtraces, set
`RUST_BACKTRACE=1`;
- If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
- If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
`RUST_LIB_BACKTRACE=0`.

The tracking issue for this feature is [rust-lang/rust#53487].

[`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
[rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487

- Anyhow works with any error type that has an impl of `std::error::Error`,
including ones defined in your crate. We do not bundle a `derive(Error)` macro
Expand Down
15 changes: 14 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,20 @@ const PROBE: &str = r#"
"#;

fn main() {
// If the compile probe succeeds, i.e. if a nightly compiler is used and the
// provide API exists, then `std::backtrace` is also usable. So `cfg
// (provide_api)` implies `cfg(backtrace)`. We do set `cfg(backtrace)` in
// this script explicitly anyway to make the `cfg()` attributes in the main
// code less complex.
let mut nightly_backtrace_support = false;
if cfg!(feature = "std") {
println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP");

match compile_probe() {
Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
Some(status) if status.success() => {
println!("cargo:rustc-cfg=provide_api");
nightly_backtrace_support = true;
}
_ => {}
}
}
Expand All @@ -75,6 +84,10 @@ fn main() {
// https://github.com/rust-lang/rust/issues/71668
println!("cargo:rustc-cfg=anyhow_no_unsafe_op_in_unsafe_fn_lint");
}

if nightly_backtrace_support || (cfg!(feature = "std") && rustc >= 65) {
println!("cargo:rustc-cfg=backtrace");
}
}

fn compile_probe() -> Option<ExitStatus> {
Expand Down
4 changes: 2 additions & 2 deletions src/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ macro_rules! backtrace {
};
}

#[cfg(backtrace)]
#[cfg(provide_api)]
macro_rules! backtrace_if_absent {
($err:expr) => {
match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
Expand All @@ -45,7 +45,7 @@ macro_rules! backtrace_if_absent {
};
}

#[cfg(all(feature = "std", not(backtrace), feature = "backtrace"))]
#[cfg(all(feature = "std", not(provide_api), any(backtrace, feature = "backtrace")))]
macro_rules! backtrace_if_absent {
($err:expr) => {
backtrace!()
Expand Down
6 changes: 3 additions & 3 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{Context, Error, StdError};
use core::convert::Infallible;
use core::fmt::{self, Debug, Display, Write};

#[cfg(backtrace)]
#[cfg(provide_api)]
use std::error::Request;

mod ext {
Expand Down Expand Up @@ -143,7 +143,7 @@ where
Some(&self.error)
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, request: &mut Request<'a>) {
StdError::provide(&self.error, request);
}
Expand All @@ -157,7 +157,7 @@ where
Some(unsafe { crate::ErrorImpl::error(self.error.inner.by_ref()) })
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, request: &mut Request<'a>) {
Error::provide(&self.error, request);
}
Expand Down
40 changes: 18 additions & 22 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::mem::ManuallyDrop;
#[cfg(not(anyhow_no_ptr_addr_of))]
use core::ptr;
use core::ptr::NonNull;
#[cfg(backtrace)]
#[cfg(provide_api)]
use std::error::{self, Request};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -99,7 +99,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<E>,
object_drop_rest: object_drop_front::<E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -124,7 +124,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -150,7 +150,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand Down Expand Up @@ -178,7 +178,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_downcast_mut::<C, E>,
object_drop_rest: context_drop_rest::<C, E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -204,7 +204,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand Down Expand Up @@ -317,7 +317,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_chain_downcast_mut::<C>,
object_drop_rest: context_chain_drop_rest::<C>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: context_backtrace::<C>,
};

Expand Down Expand Up @@ -345,19 +345,15 @@ impl Error {
///
/// # Stability
///
/// Standard library backtraces are only available on the nightly channel.
/// Tracking issue: [rust-lang/rust#53487][tracking].
///
/// On stable compilers, this function is only available if the crate's
/// Standard library backtraces are only available when using Rust ≥ 1.65.
/// On older compilers, this function is only available if the crate's
/// "backtrace" feature is enabled, and will use the `backtrace` crate as
/// the underlying backtrace implementation.
///
/// ```toml
/// [dependencies]
/// anyhow = { version = "1.0", features = ["backtrace"] }
/// ```
///
/// [tracking]: https://github.com/rust-lang/rust/issues/53487
#[cfg(any(backtrace, feature = "backtrace"))]
#[cfg_attr(doc_cfg, doc(cfg(any(nightly, feature = "backtrace"))))]
pub fn backtrace(&self) -> &impl_backtrace!() {
Expand Down Expand Up @@ -523,7 +519,7 @@ impl Error {
}
}

#[cfg(backtrace)]
#[cfg(provide_api)]
pub(crate) fn provide<'a>(&'a self, request: &mut Request<'a>) {
unsafe { ErrorImpl::provide(self.inner.by_ref(), request) }
}
Expand All @@ -533,7 +529,7 @@ impl Error {
// deref'ing to dyn Error where the provide implementation would include
// only the original error's Backtrace from before it got wrapped into an
// anyhow::Error.
#[cfg(backtrace)]
#[cfg(provide_api)]
#[doc(hidden)]
pub fn thiserror_provide<'a>(&'a self, request: &mut Request<'a>) {
Self::provide(self, request);
Expand Down Expand Up @@ -602,7 +598,7 @@ struct ErrorVTable {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: unsafe fn(Mut<ErrorImpl>, TypeId) -> Option<Mut<()>>,
object_drop_rest: unsafe fn(Own<ErrorImpl>, TypeId),
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: unsafe fn(Ref<ErrorImpl>) -> Option<&Backtrace>,
}

Expand Down Expand Up @@ -707,7 +703,7 @@ where
}
}

#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
fn no_backtrace(e: Ref<ErrorImpl>) -> Option<&Backtrace> {
let _ = e;
None
Expand Down Expand Up @@ -828,7 +824,7 @@ where
}

// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
#[allow(clippy::unnecessary_wraps)]
unsafe fn context_backtrace<C>(e: Ref<ErrorImpl>) -> Option<&Backtrace>
where
Expand Down Expand Up @@ -908,15 +904,15 @@ impl ErrorImpl {
.backtrace
.as_ref()
.or_else(|| {
#[cfg(backtrace)]
#[cfg(provide_api)]
return error::request_ref::<Backtrace>(unsafe { Self::error(this) });
#[cfg(not(backtrace))]
#[cfg(not(provide_api))]
return unsafe { (vtable(this.ptr).object_backtrace)(this) };
})
.expect("backtrace capture failed")
}

#[cfg(backtrace)]
#[cfg(provide_api)]
unsafe fn provide<'a>(this: Ref<'a, Self>, request: &mut Request<'a>) {
if let Some(backtrace) = unsafe { &this.deref().backtrace } {
request.provide_ref(backtrace);
Expand All @@ -938,7 +934,7 @@ where
unsafe { ErrorImpl::error(self.erase()).source() }
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, request: &mut Request<'a>) {
unsafe { ErrorImpl::provide(self.erase(), request) }
}
Expand Down
14 changes: 5 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,22 +128,18 @@
//! # ;
//! ```
//!
//! - If using the nightly channel, or stable with `features = ["backtrace"]`, a
//! backtrace is captured and printed with the error if the underlying error
//! type does not already provide its own. In order to see backtraces, they
//! must be enabled through the environment variables described in
//! [`std::backtrace`]:
//! - If using Rust ≥ 1.65 or `features = ["backtrace"]`, a backtrace is captured
//! and printed with the error if the underlying error type does not already
//! provide its own. In order to see backtraces, they must be enabled through the
//! environment variables described in [`std::backtrace`]:
//!
//! - If you want panics and errors to both have backtraces, set
//! `RUST_BACKTRACE=1`;
//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
//! `RUST_LIB_BACKTRACE=0`.
//!
//! The tracking issue for this feature is [rust-lang/rust#53487].
//!
//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
//! [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487
//!
//! - Anyhow works with any error type that has an impl of `std::error::Error`,
//! including ones defined in your crate. We do not bundle a `derive(Error)`
Expand Down Expand Up @@ -211,7 +207,7 @@
//! non-Anyhow error type inside a function that returns Anyhow's error type.

#![doc(html_root_url = "https://docs.rs/anyhow/1.0.76")]
#![cfg_attr(backtrace, feature(error_generic_member_access))]
#![cfg_attr(provide_api, feature(error_generic_member_access))]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(dead_code, unused_imports, unused_mut)]
Expand Down
4 changes: 2 additions & 2 deletions src/wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::StdError;
use core::fmt::{self, Debug, Display};

#[cfg(backtrace)]
#[cfg(provide_api)]
use std::error::Request;

#[repr(transparent)]
Expand Down Expand Up @@ -74,7 +74,7 @@ impl StdError for BoxedError {
self.0.source()
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, request: &mut Request<'a>) {
self.0.provide(request);
}
Expand Down

0 comments on commit d6acd22

Please sign in to comment.