From a42fc2c21846ba459df43a3f8b4996a2074909cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 19 Feb 2026 07:59:31 -0800 Subject: [PATCH] Remove `feature = "backtrace"` conditional code --- build.rs | 5 - src/backtrace.rs | 366 +---------------------------------------------- src/error.rs | 51 ++----- src/fmt.rs | 2 +- 4 files changed, 17 insertions(+), 407 deletions(-) diff --git a/build.rs b/build.rs index 2086d29..8da6808 100644 --- a/build.rs +++ b/build.rs @@ -9,11 +9,6 @@ use std::path::Path; use std::process::{self, Command, Stdio}; use std::str; -#[cfg(all(feature = "backtrace", not(feature = "std")))] -compile_error! { - "`backtrace` feature without `std` feature is not supported" -} - fn main() { let mut error_generic_member_access = false; if cfg!(feature = "std") { diff --git a/src/backtrace.rs b/src/backtrace.rs index aa2ccdc..ff775f0 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -1,34 +1,17 @@ #[cfg(std_backtrace)] pub(crate) use std::backtrace::{Backtrace, BacktraceStatus}; -#[cfg(all(not(std_backtrace), feature = "backtrace"))] -pub(crate) use self::capture::{Backtrace, BacktraceStatus}; - -#[cfg(not(any(std_backtrace, feature = "backtrace")))] +#[cfg(not(std_backtrace))] pub(crate) enum Backtrace {} #[cfg(std_backtrace)] -macro_rules! impl_backtrace { - () => { - std::backtrace::Backtrace - }; -} - -#[cfg(all(not(std_backtrace), feature = "backtrace"))] -macro_rules! impl_backtrace { - () => { - impl core::fmt::Debug + core::fmt::Display - }; -} - -#[cfg(any(std_backtrace, feature = "backtrace"))] macro_rules! backtrace { () => { Some(crate::backtrace::Backtrace::capture()) }; } -#[cfg(not(any(std_backtrace, feature = "backtrace")))] +#[cfg(not(std_backtrace))] macro_rules! backtrace { () => { None @@ -48,7 +31,7 @@ macro_rules! backtrace_if_absent { #[cfg(all( any(feature = "std", not(anyhow_no_core_error)), not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") + std_backtrace ))] macro_rules! backtrace_if_absent { ($err:expr) => { @@ -56,354 +39,13 @@ macro_rules! backtrace_if_absent { }; } -#[cfg(all( - any(feature = "std", not(anyhow_no_core_error)), - not(std_backtrace), - not(feature = "backtrace"), -))] +#[cfg(all(any(feature = "std", not(anyhow_no_core_error)), not(std_backtrace)))] macro_rules! backtrace_if_absent { ($err:expr) => { None }; } -#[cfg(all(not(std_backtrace), feature = "backtrace"))] -mod capture { - use alloc::borrow::{Cow, ToOwned as _}; - use alloc::vec::Vec; - use backtrace::{BacktraceFmt, BytesOrWideString, Frame, PrintFmt, SymbolName}; - use core::cell::UnsafeCell; - use core::fmt::{self, Debug, Display}; - use core::sync::atomic::{AtomicUsize, Ordering}; - use std::env; - use std::path::{self, Path, PathBuf}; - use std::sync::Once; - - pub(crate) struct Backtrace { - inner: Inner, - } - - pub(crate) enum BacktraceStatus { - Unsupported, - Disabled, - Captured, - } - - enum Inner { - Unsupported, - Disabled, - Captured(LazilyResolvedCapture), - } - - struct Capture { - actual_start: usize, - resolved: bool, - frames: Vec, - } - - struct BacktraceFrame { - frame: Frame, - symbols: Vec, - } - - struct BacktraceSymbol { - name: Option>, - filename: Option, - lineno: Option, - colno: Option, - } - - enum BytesOrWide { - Bytes(Vec), - Wide(Vec), - } - - impl Debug for Backtrace { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let capture = match &self.inner { - Inner::Unsupported => return fmt.write_str(""), - Inner::Disabled => return fmt.write_str(""), - Inner::Captured(c) => c.force(), - }; - - let frames = &capture.frames[capture.actual_start..]; - - write!(fmt, "Backtrace ")?; - - let mut dbg = fmt.debug_list(); - - for frame in frames { - if frame.frame.ip().is_null() { - continue; - } - - dbg.entries(&frame.symbols); - } - - dbg.finish() - } - } - - impl Debug for BacktraceFrame { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut dbg = fmt.debug_list(); - dbg.entries(&self.symbols); - dbg.finish() - } - } - - impl Debug for BacktraceSymbol { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{{ ")?; - - if let Some(fn_name) = self.name.as_ref().map(|b| SymbolName::new(b)) { - write!(fmt, "fn: \"{:#}\"", fn_name)?; - } else { - write!(fmt, "fn: ")?; - } - - if let Some(fname) = self.filename.as_ref() { - write!(fmt, ", file: \"{:?}\"", fname)?; - } - - if let Some(line) = self.lineno { - write!(fmt, ", line: {:?}", line)?; - } - - write!(fmt, " }}") - } - } - - impl Debug for BytesOrWide { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - output_filename( - fmt, - match self { - BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), - BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), - }, - PrintFmt::Short, - env::current_dir().as_ref().ok(), - ) - } - } - - impl Backtrace { - fn enabled() -> bool { - static ENABLED: AtomicUsize = AtomicUsize::new(0); - match ENABLED.load(Ordering::Relaxed) { - 0 => {} - 1 => return false, - _ => return true, - } - let enabled = match env::var_os("RUST_LIB_BACKTRACE") { - Some(s) => s != "0", - None => match env::var_os("RUST_BACKTRACE") { - Some(s) => s != "0", - None => false, - }, - }; - ENABLED.store(enabled as usize + 1, Ordering::Relaxed); - enabled - } - - #[inline(never)] // want to make sure there's a frame here to remove - pub(crate) fn capture() -> Backtrace { - if Backtrace::enabled() { - Backtrace::create(Backtrace::capture as usize) - } else { - let inner = Inner::Disabled; - Backtrace { inner } - } - } - - // Capture a backtrace which starts just before the function addressed - // by `ip` - fn create(ip: usize) -> Backtrace { - let mut frames = Vec::new(); - let mut actual_start = None; - backtrace::trace(|frame| { - frames.push(BacktraceFrame { - frame: frame.clone(), - symbols: Vec::new(), - }); - if frame.symbol_address() as usize == ip && actual_start.is_none() { - actual_start = Some(frames.len() + 1); - } - true - }); - - // If no frames came out, assume that this is an unsupported platform - // since `backtrace` doesn't provide a way of learning this right - // now, and this should be a good enough approximation. - let inner = if frames.is_empty() { - Inner::Unsupported - } else { - Inner::Captured(LazilyResolvedCapture::new(Capture { - actual_start: actual_start.unwrap_or(0), - frames, - resolved: false, - })) - }; - - Backtrace { inner } - } - - pub(crate) fn status(&self) -> BacktraceStatus { - match self.inner { - Inner::Unsupported => BacktraceStatus::Unsupported, - Inner::Disabled => BacktraceStatus::Disabled, - Inner::Captured(_) => BacktraceStatus::Captured, - } - } - } - - impl Display for Backtrace { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let capture = match &self.inner { - Inner::Unsupported => return fmt.write_str("unsupported backtrace"), - Inner::Disabled => return fmt.write_str("disabled backtrace"), - Inner::Captured(c) => c.force(), - }; - - let full = fmt.alternate(); - let (frames, style) = if full { - (&capture.frames[..], PrintFmt::Full) - } else { - (&capture.frames[capture.actual_start..], PrintFmt::Short) - }; - - // When printing paths we try to strip the cwd if it exists, - // otherwise we just print the path as-is. Note that we also only do - // this for the short format, because if it's full we presumably - // want to print everything. - let cwd = env::current_dir(); - let mut print_path = move |fmt: &mut fmt::Formatter, path: BytesOrWideString| { - output_filename(fmt, path, style, cwd.as_ref().ok()) - }; - - let mut f = BacktraceFmt::new(fmt, style, &mut print_path); - f.add_context()?; - for frame in frames { - let mut f = f.frame(); - if frame.symbols.is_empty() { - f.print_raw(frame.frame.ip(), None, None, None)?; - } else { - for symbol in frame.symbols.iter() { - f.print_raw_with_column( - frame.frame.ip(), - symbol.name.as_ref().map(|b| SymbolName::new(b)), - symbol.filename.as_ref().map(|b| match b { - BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), - BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), - }), - symbol.lineno, - symbol.colno, - )?; - } - } - } - f.finish()?; - Ok(()) - } - } - - struct LazilyResolvedCapture { - sync: Once, - capture: UnsafeCell, - } - - impl LazilyResolvedCapture { - fn new(capture: Capture) -> Self { - LazilyResolvedCapture { - sync: Once::new(), - capture: UnsafeCell::new(capture), - } - } - - fn force(&self) -> &Capture { - self.sync.call_once(|| { - // Safety: This exclusive reference can't overlap with any - // others. `Once` guarantees callers will block until this - // closure returns. `Once` also guarantees only a single caller - // will enter this closure. - unsafe { &mut *self.capture.get() }.resolve(); - }); - - // Safety: This shared reference can't overlap with the exclusive - // reference above. - unsafe { &*self.capture.get() } - } - } - - // Safety: Access to the inner value is synchronized using a thread-safe - // `Once`. So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too - unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {} - - impl Capture { - fn resolve(&mut self) { - // If we're already resolved, nothing to do! - if self.resolved { - return; - } - self.resolved = true; - - for frame in self.frames.iter_mut() { - let symbols = &mut frame.symbols; - let frame = &frame.frame; - backtrace::resolve_frame(frame, |symbol| { - symbols.push(BacktraceSymbol { - name: symbol.name().map(|m| m.as_bytes().to_vec()), - filename: symbol.filename_raw().map(|b| match b { - BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), - BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), - }), - lineno: symbol.lineno(), - colno: symbol.colno(), - }); - }); - } - } - } - - // Prints the filename of the backtrace frame. - fn output_filename( - fmt: &mut fmt::Formatter, - bows: BytesOrWideString, - print_fmt: PrintFmt, - cwd: Option<&PathBuf>, - ) -> fmt::Result { - let file: Cow = match bows { - #[cfg(unix)] - BytesOrWideString::Bytes(bytes) => { - use std::os::unix::ffi::OsStrExt; - Path::new(std::ffi::OsStr::from_bytes(bytes)).into() - } - #[cfg(not(unix))] - BytesOrWideString::Bytes(bytes) => { - Path::new(std::str::from_utf8(bytes).unwrap_or("")).into() - } - #[cfg(windows)] - BytesOrWideString::Wide(wide) => { - use std::os::windows::ffi::OsStringExt; - Cow::Owned(std::ffi::OsString::from_wide(wide).into()) - } - #[cfg(not(windows))] - BytesOrWideString::Wide(_wide) => Path::new("").into(), - }; - if print_fmt == PrintFmt::Short && file.is_absolute() { - if let Some(cwd) = cwd { - if let Ok(stripped) = file.strip_prefix(&cwd) { - if let Some(s) = stripped.to_str() { - return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s); - } - } - } - } - Display::fmt(&file.display(), fmt) - } -} - fn _assert_send_sync() { fn assert() {} assert::(); diff --git a/src/error.rs b/src/error.rs index d9444f2..11d3405 100644 --- a/src/error.rs +++ b/src/error.rs @@ -155,10 +155,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::, object_downcast: object_downcast::, object_drop_rest: object_drop_front::, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: no_backtrace, }; @@ -182,10 +179,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::>, object_downcast: object_downcast::, object_drop_rest: object_drop_front::, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: no_backtrace, }; @@ -210,10 +204,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::>, object_downcast: object_downcast::, object_drop_rest: object_drop_front::, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: no_backtrace, }; @@ -244,10 +235,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::>, object_downcast: context_downcast::, object_drop_rest: context_drop_rest::, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: no_backtrace, }; @@ -272,10 +260,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::, object_downcast: object_downcast::>, object_drop_rest: object_drop_front::>, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: no_backtrace, }; @@ -387,10 +372,7 @@ impl Error { object_reallocate_boxed: object_reallocate_boxed::>, object_downcast: context_chain_downcast::, object_drop_rest: context_chain_drop_rest::, - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: context_backtrace::, }; @@ -428,8 +410,8 @@ impl Error { /// [dependencies] /// anyhow = { version = "1.0", features = ["backtrace"] } /// ``` - #[cfg(any(std_backtrace, feature = "backtrace"))] - pub fn backtrace(&self) -> &impl_backtrace!() { + #[cfg(std_backtrace)] + pub fn backtrace(&self) -> &std::backtrace::Backtrace { unsafe { ErrorImpl::backtrace(self.inner.by_ref()) } } @@ -746,10 +728,7 @@ struct ErrorVTable { object_reallocate_boxed: unsafe fn(Own) -> Box, object_downcast: unsafe fn(Ref, TypeId) -> Option>, object_drop_rest: unsafe fn(Own, TypeId), - #[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") - ))] + #[cfg(all(not(error_generic_member_access), std_backtrace))] object_backtrace: unsafe fn(Ref) -> Option<&Backtrace>, } @@ -825,10 +804,7 @@ where } } -#[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") -))] +#[cfg(all(not(error_generic_member_access), std_backtrace))] fn no_backtrace(e: Ref) -> Option<&Backtrace> { let _ = e; None @@ -912,10 +888,7 @@ where } // Safety: requires layout of *e to match ErrorImpl>. -#[cfg(all( - not(error_generic_member_access), - any(std_backtrace, feature = "backtrace") -))] +#[cfg(all(not(error_generic_member_access), std_backtrace))] #[allow(clippy::unnecessary_wraps)] unsafe fn context_backtrace(e: Ref) -> Option<&Backtrace> where @@ -981,7 +954,7 @@ impl ErrorImpl { } } - #[cfg(any(std_backtrace, feature = "backtrace"))] + #[cfg(std_backtrace)] pub(crate) unsafe fn backtrace(this: Ref) -> &Backtrace { // This unwrap can only panic if the underlying error's backtrace method // is nondeterministic, which would only happen in maliciously diff --git a/src/fmt.rs b/src/fmt.rs index 85c84a9..376ed33 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -40,7 +40,7 @@ impl ErrorImpl { } } - #[cfg(any(std_backtrace, feature = "backtrace"))] + #[cfg(std_backtrace)] { use crate::backtrace::BacktraceStatus; use alloc::string::ToString;