Skip to content

Commit a6c8a61

Browse files
authored
Merge branch 'master' into bump-backtrace-rs
2 parents 254309f + df42dc4 commit a6c8a61

16 files changed

+236
-271
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rust-version = "1.65.0"
1717
indenter = "0.3.0"
1818
once_cell = "1.18.0"
1919
owo-colors = "4.0"
20+
autocfg = "1.0"
2021

2122
[profile.dev.package.backtrace]
2223
opt-level = 3

README.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,12 @@ avoid using `eyre::Report` as your public error type.
122122
}
123123
```
124124

125-
- If using the nightly channel, a backtrace is captured and printed with the
126-
error if the underlying error type does not already provide its own. In order
127-
to see backtraces, they must be enabled through the environment variables
125+
- If using rust >1.65, a backtrace is captured and printed with the
126+
error.
127+
128+
On nightly eyre will use the underlying error's backtrace if it has one.
129+
130+
In order to see backtraces, they must be enabled through the environment variables
128131
described in [`std::backtrace`]:
129132

130133
- If you want panics and errors to both have backtraces, set
@@ -141,7 +144,7 @@ avoid using `eyre::Report` as your public error type.
141144
- Eyre works with any error type that has an impl of `std::error::Error`,
142145
including ones defined in your crate. We do not bundle a `derive(Error)` macro
143146
but you can write the impls yourself or use a standalone macro like
144-
[thiserror].
147+
[thiserror](https://github.com/dtolnay/thiserror).
145148

146149
```rust
147150
use thiserror::Error;
@@ -178,6 +181,15 @@ No-std support was removed in 2020 in [commit 608a16a] due to unaddressed upstre
178181
[commit 608a16a]:
179182
https://github.com/eyre-rs/eyre/pull/29/commits/608a16aa2c2c27eca6c88001cc94c6973c18f1d5
180183

184+
185+
## Backtrace support
186+
187+
The built in default handler has support for capturing backtrace using `rustc-1.65` or later.
188+
189+
Backtraces are captured when an error is converted to an `eyre::Report` (such as using `?` or `eyre!`).
190+
191+
If using the nightly toolchain, backtraces will also be captured and accessed from other errors using [error_generic_member_access](https://github.com/rust-lang/rfcs/pull/2895) if available.
192+
181193
## Comparison to failure
182194

183195
The `eyre::Report` type works something like `failure::Error`, but unlike
@@ -195,8 +207,6 @@ you need an error type that can be handled via match or reported. This is
195207
common in library crates where you don't know how your users will handle
196208
your errors.
197209

198-
[thiserror]: https://github.com/dtolnay/thiserror
199-
200210
## Compatibility with `anyhow`
201211

202212
This crate does its best to be usable as a drop in replacement of `anyhow` and

color-eyre/src/config.rs

-33
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,6 @@ use std::env;
1111
use std::fmt::Write as _;
1212
use std::{fmt, path::PathBuf, sync::Arc};
1313

14-
#[derive(Debug)]
15-
struct InstallError;
16-
17-
impl fmt::Display for InstallError {
18-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19-
f.write_str("could not install the BacktracePrinter as another was already installed")
20-
}
21-
}
22-
23-
impl std::error::Error for InstallError {}
24-
25-
#[derive(Debug)]
26-
struct InstallThemeError;
27-
28-
impl fmt::Display for InstallThemeError {
29-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30-
f.write_str("could not set the provided `Theme` globally as another was already set")
31-
}
32-
}
33-
34-
impl std::error::Error for InstallThemeError {}
35-
36-
#[derive(Debug)]
37-
struct InstallColorSpantraceThemeError;
38-
39-
impl fmt::Display for InstallColorSpantraceThemeError {
40-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41-
f.write_str("could not set the provided `Theme` via `color_spantrace::set_theme` globally as another was already set")
42-
}
43-
}
44-
45-
impl std::error::Error for InstallColorSpantraceThemeError {}
46-
4714
/// A struct that represents a theme that is used by `color_eyre`
4815
#[derive(Debug, Copy, Clone, Default)]
4916
pub struct Theme {

color-eyre/src/writers.rs

+8
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ impl<W> WriterExt for W {
2828
}
2929
}
3030

31+
#[cfg(feature = "issue-url")]
3132
pub(crate) trait DisplayExt: Sized + Display {
3233
fn with_header<H: Display>(self, header: H) -> Header<Self, H>;
3334
fn with_footer<F: Display>(self, footer: F) -> Footer<Self, F>;
3435
}
3536

37+
#[cfg(feature = "issue-url")]
3638
impl<T> DisplayExt for T
3739
where
3840
T: Display,
@@ -80,11 +82,13 @@ where
8082
}
8183
}
8284

85+
#[cfg(feature = "issue-url")]
8386
pub(crate) struct FooterWriter<W> {
8487
inner: W,
8588
had_output: bool,
8689
}
8790

91+
#[cfg(feature = "issue-url")]
8892
impl<W> fmt::Write for FooterWriter<W>
8993
where
9094
W: fmt::Write,
@@ -98,6 +102,7 @@ where
98102
}
99103
}
100104

105+
#[cfg(feature = "issue-url")]
101106
#[allow(explicit_outlives_requirements)]
102107
pub(crate) struct Footer<B, H>
103108
where
@@ -108,6 +113,7 @@ where
108113
footer: H,
109114
}
110115

116+
#[cfg(feature = "issue-url")]
111117
impl<B, H> fmt::Display for Footer<B, H>
112118
where
113119
B: Display,
@@ -129,6 +135,7 @@ where
129135
}
130136
}
131137

138+
#[cfg(feature = "issue-url")]
132139
#[allow(explicit_outlives_requirements)]
133140
pub(crate) struct Header<B, H>
134141
where
@@ -139,6 +146,7 @@ where
139146
h: H,
140147
}
141148

149+
#[cfg(feature = "issue-url")]
142150
impl<B, H> fmt::Display for Header<B, H>
143151
where
144152
B: Display,

color-eyre/tests/theme.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,12 @@ fn test_backwards_compatibility(target: String, file_name: &str) {
170170
fn normalize_backtrace(input: &str) -> String {
171171
input
172172
.lines()
173-
.take_while(|v| !v.contains("core::panic") && !v.contains("theme_test_helper::main"))
173+
.take_while(|v| {
174+
!v.contains("core::panic")
175+
&& !v.contains("theme_test_helper::main")
176+
&& !v.contains("theme::test_error_backwards_compatibility::closure")
177+
&& !v.contains("theme::test_error_backwards_compatibility::{{closure}}")
178+
})
174179
.collect::<Vec<_>>()
175180
.join("\n")
176181
}

eyre/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ indenter = { workspace = true }
2323
once_cell = { workspace = true }
2424
pyo3 = { version = "0.20", optional = true, default-features = false }
2525

26+
[build-dependencies]
27+
autocfg = { workspace = true }
28+
2629
[dev-dependencies]
2730
futures = { version = "0.3", default-features = false }
2831
rustversion = "1.0"

eyre/build.rs

+72-99
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,53 @@
1-
use std::env;
2-
use std::ffi::OsString;
3-
use std::fs;
4-
use std::path::Path;
5-
use std::process::{Command, ExitStatus};
6-
use std::str;
7-
8-
// This code exercises the surface area that we expect of the std Backtrace
9-
// type. If the current toolchain is able to compile it, we go ahead and use
10-
// backtrace in eyre.
11-
const BACKTRACE_PROBE: &str = r#"
12-
#![feature(backtrace)]
1+
use std::{
2+
env, fs,
3+
path::Path,
4+
process::{Command, ExitStatus},
5+
};
6+
7+
fn main() {
8+
let ac = autocfg::new();
9+
10+
// https://github.com/rust-lang/rust/issues/99301 [nightly]
11+
//
12+
// Autocfg does currently not support custom probes, or `nightly` only features
13+
match compile_probe(GENERIC_MEMBER_ACCESS_PROBE) {
14+
Some(status) if status.success() => autocfg::emit("generic_member_access"),
15+
_ => {}
16+
}
17+
18+
// https://github.com/rust-lang/rust/issues/47809 [rustc-1.46]
19+
ac.emit_expression_cfg("std::panic::Location::caller", "track_caller");
20+
21+
if ac.probe_rustc_version(1, 52) {
22+
autocfg::emit("eyre_no_fmt_arguments_as_str");
23+
}
24+
25+
if ac.probe_rustc_version(1, 58) {
26+
autocfg::emit("eyre_no_fmt_args_capture");
27+
}
28+
29+
if ac.probe_rustc_version(1, 65) {
30+
autocfg::emit("backtrace")
31+
}
32+
}
33+
34+
// This code exercises the surface area or the generic member access feature for the `std::error::Error` trait.
35+
//
36+
// This is use to detect and supply backtrace information through different errors types.
37+
const GENERIC_MEMBER_ACCESS_PROBE: &str = r#"
38+
#![feature(error_generic_member_access)]
1339
#![allow(dead_code)]
1440
15-
use std::backtrace::{Backtrace, BacktraceStatus};
16-
use std::error::Error;
41+
use std::error::{Error, Request};
1742
use std::fmt::{self, Display};
1843
1944
#[derive(Debug)]
20-
struct E;
45+
struct E {
46+
backtrace: MyBacktrace,
47+
}
48+
49+
#[derive(Debug)]
50+
struct MyBacktrace;
2151
2252
impl Display for E {
2353
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
@@ -26,59 +56,44 @@ const BACKTRACE_PROBE: &str = r#"
2656
}
2757
2858
impl Error for E {
29-
fn backtrace(&self) -> Option<&Backtrace> {
30-
let backtrace = Backtrace::capture();
31-
match backtrace.status() {
32-
BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
33-
}
34-
unimplemented!()
59+
fn provide<'a>(&'a self, request: &mut Request<'a>) {
60+
request
61+
.provide_ref::<MyBacktrace>(&self.backtrace);
3562
}
3663
}
3764
"#;
3865

39-
const TRACK_CALLER_PROBE: &str = r#"
40-
#![allow(dead_code)]
41-
42-
#[track_caller]
43-
fn foo() {
44-
let _location = std::panic::Location::caller();
45-
}
46-
"#;
47-
48-
fn main() {
49-
match compile_probe(BACKTRACE_PROBE) {
50-
Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
51-
_ => {}
52-
}
53-
54-
match compile_probe(TRACK_CALLER_PROBE) {
55-
Some(status) if status.success() => println!("cargo:rustc-cfg=track_caller"),
56-
_ => {}
57-
}
66+
fn compile_probe(probe: &str) -> Option<ExitStatus> {
67+
let rustc = env::var_os("RUSTC")?;
68+
let out_dir = env::var_os("OUT_DIR")?;
69+
let probefile = Path::new(&out_dir).join("probe.rs");
70+
fs::write(&probefile, probe).ok()?;
5871

59-
let version = match rustc_version_info() {
60-
Some(version) => version,
61-
None => return,
62-
};
72+
let rustc_wrapper = env::var_os("RUSTC_WRAPPER").filter(|wrapper| !wrapper.is_empty());
73+
let rustc_workspace_wrapper =
74+
env::var_os("RUSTC_WORKSPACE_WRAPPER").filter(|wrapper| !wrapper.is_empty());
75+
let mut rustc = rustc_wrapper
76+
.into_iter()
77+
.chain(rustc_workspace_wrapper)
78+
.chain(std::iter::once(rustc));
6379

64-
version.toolchain.set_feature();
80+
let mut cmd = Command::new(rustc.next().unwrap());
81+
cmd.args(rustc);
6582

66-
if version.minor < 52 {
67-
println!("cargo:rustc-cfg=eyre_no_fmt_arguments_as_str");
83+
if let Some(target) = env::var_os("TARGET") {
84+
cmd.arg("--target").arg(target);
6885
}
6986

70-
if version.minor < 58 {
71-
println!("cargo:rustc-cfg=eyre_no_fmt_args_capture");
87+
// If Cargo wants to set RUSTFLAGS, use that.
88+
if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
89+
if !rustflags.is_empty() {
90+
for arg in rustflags.split('\x1f') {
91+
cmd.arg(arg);
92+
}
93+
}
7294
}
73-
}
7495

75-
fn compile_probe(probe: &str) -> Option<ExitStatus> {
76-
let rustc = env::var_os("RUSTC")?;
77-
let out_dir = env::var_os("OUT_DIR")?;
78-
let probefile = Path::new(&out_dir).join("probe.rs");
79-
fs::write(&probefile, probe).ok()?;
80-
Command::new(rustc)
81-
.arg("--edition=2018")
96+
cmd.arg("--edition=2018")
8297
.arg("--crate-name=eyre_build")
8398
.arg("--crate-type=lib")
8499
.arg("--emit=metadata")
@@ -88,45 +103,3 @@ fn compile_probe(probe: &str) -> Option<ExitStatus> {
88103
.status()
89104
.ok()
90105
}
91-
92-
// TODO factor this toolchain parsing and related tests into its own file
93-
#[derive(PartialEq)]
94-
enum Toolchain {
95-
Stable,
96-
Beta,
97-
Nightly,
98-
}
99-
impl Toolchain {
100-
fn set_feature(self) {
101-
match self {
102-
Toolchain::Nightly => println!("cargo:rustc-cfg=nightly"),
103-
Toolchain::Beta => println!("cargo:rustc-cfg=beta"),
104-
Toolchain::Stable => println!("cargo:rustc-cfg=stable"),
105-
}
106-
}
107-
}
108-
109-
struct VersionInfo {
110-
minor: u32,
111-
toolchain: Toolchain,
112-
}
113-
114-
fn rustc_version_info() -> Option<VersionInfo> {
115-
let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
116-
let output = Command::new(rustc).arg("--version").output().ok()?;
117-
let version = str::from_utf8(&output.stdout).ok()?;
118-
let mut pieces = version.split(['.', ' ', '-']);
119-
if pieces.next() != Some("rustc") {
120-
return None;
121-
}
122-
let _major: u32 = pieces.next()?.parse().ok()?;
123-
let minor = pieces.next()?.parse().ok()?;
124-
let _patch: u32 = pieces.next()?.parse().ok()?;
125-
let toolchain = match pieces.next() {
126-
Some("beta") => Toolchain::Beta,
127-
Some("nightly") => Toolchain::Nightly,
128-
_ => Toolchain::Stable,
129-
};
130-
let version = VersionInfo { minor, toolchain };
131-
Some(version)
132-
}

0 commit comments

Comments
 (0)