diff --git a/Cargo.toml b/Cargo.toml index 698c937..c57be4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ rust-version = "1.39" [features] default = ["std"] std = [] +track_caller = [] [dependencies] backtrace = { version = "0.3.51", optional = true } diff --git a/src/backtrace.rs b/src/backtrace.rs index 23c0c85..2a13344 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -35,6 +35,20 @@ macro_rules! backtrace { }; } +#[cfg(feature = "track_caller")] +macro_rules! caller { + () => { + Some(core::panic::Location::caller()) + } +} + +#[cfg(not(feature = "track_caller"))] +macro_rules! caller { + () => { + None + } +} + #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { diff --git a/src/context.rs b/src/context.rs index 9df8693..9240d19 100644 --- a/src/context.rs +++ b/src/context.rs @@ -25,11 +25,12 @@ mod ext { C: Display + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&self); - Error::from_context(context, self, backtrace) + Error::from_context(context, self, backtrace, caller!()) } } impl StdError for Error { + #[cfg_attr(feature = "track_caller", track_caller)] fn ext_context(self, context: C) -> Error where C: Display + Send + Sync + 'static, @@ -43,6 +44,7 @@ impl Context for Result where E: ext::StdError + Send + Sync + 'static, { + #[cfg_attr(feature = "track_caller", track_caller)] fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, @@ -55,6 +57,7 @@ where } } + #[cfg_attr(feature = "track_caller", track_caller)] fn with_context(self, context: F) -> Result where C: Display + Send + Sync + 'static, @@ -88,6 +91,7 @@ where /// } /// ``` impl Context for Option { + #[cfg_attr(feature = "track_caller", track_caller)] fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, @@ -96,10 +100,11 @@ impl Context for Option { // backtrace. match self { Some(ok) => Ok(ok), - None => Err(Error::from_display(context, backtrace!())), + None => Err(Error::from_display(context, backtrace!(), caller!())), } } + #[cfg_attr(feature = "track_caller", track_caller)] fn with_context(self, context: F) -> Result where C: Display + Send + Sync + 'static, @@ -107,7 +112,7 @@ impl Context for Option { { match self { Some(ok) => Ok(ok), - None => Err(Error::from_display(context(), backtrace!())), + None => Err(Error::from_display(context(), backtrace!(), caller!())), } } } diff --git a/src/error.rs b/src/error.rs index 9f6ce8c..284aaa4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,7 @@ use core::mem::ManuallyDrop; #[cfg(not(anyhow_no_ptr_addr_of))] use core::ptr; use core::ptr::NonNull; +use core::panic::Location; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; @@ -34,7 +35,7 @@ impl Error { E: StdError + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&error); - Error::from_std(error, backtrace) + Error::from_std(error, backtrace, caller!()) } /// Create a new error object from a printable error message. @@ -80,12 +81,12 @@ impl Error { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message, backtrace!(), caller!()) } #[cfg(feature = "std")] #[cold] - pub(crate) fn from_std(error: E, backtrace: Option) -> Self + pub(crate) fn from_std(error: E, backtrace: Option, caller: Option<&'static Location<'static>>) -> Self where E: StdError + Send + Sync + 'static, { @@ -104,11 +105,11 @@ impl Error { }; // Safety: passing vtable that operates on the right type E. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller) } } #[cold] - pub(crate) fn from_adhoc(message: M, backtrace: Option) -> Self + pub(crate) fn from_adhoc(message: M, backtrace: Option, caller: Option<&'static Location<'static>>) -> Self where M: Display + Debug + Send + Sync + 'static, { @@ -130,11 +131,11 @@ impl Error { // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller) } } #[cold] - pub(crate) fn from_display(message: M, backtrace: Option) -> Self + pub(crate) fn from_display(message: M, backtrace: Option, caller: Option<&'static Location<'static>>) -> Self where M: Display + Send + Sync + 'static, { @@ -156,12 +157,12 @@ impl Error { // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller) } } #[cfg(feature = "std")] #[cold] - pub(crate) fn from_context(context: C, error: E, backtrace: Option) -> Self + pub(crate) fn from_context(context: C, error: E, backtrace: Option, caller: Option<&'static Location<'static>>) -> Self where C: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, @@ -183,7 +184,7 @@ impl Error { }; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller) } } #[cfg(feature = "std")] @@ -191,6 +192,7 @@ impl Error { pub(crate) fn from_boxed( error: Box, backtrace: Option, + caller: Option<&'static Location<'static>> ) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); @@ -210,7 +212,7 @@ impl Error { // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller) } } // Takes backtrace as argument rather than capturing it here so that the @@ -223,6 +225,7 @@ impl Error { error: E, vtable: &'static ErrorVTable, backtrace: Option, + caller: Option<&'static Location<'static>>, ) -> Self where E: StdError + Send + Sync + 'static, @@ -230,6 +233,7 @@ impl Error { let inner: Box> = Box::new(ErrorImpl { vtable, backtrace, + caller, _object: error, }); // Erase the concrete type of E from the compile-time type system. This @@ -298,6 +302,7 @@ impl Error { /// ``` #[cold] #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] pub fn context(self, context: C) -> Self where C: Display + Send + Sync + 'static, @@ -325,7 +330,7 @@ impl Error { let backtrace = None; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, caller!()) } } /// Get the backtrace for this Error. @@ -545,7 +550,7 @@ where #[cold] fn from(error: E) -> Self { let backtrace = backtrace_if_absent!(&error); - Error::from_std(error, backtrace) + Error::from_std(error, backtrace, caller!()) } } @@ -840,6 +845,7 @@ where pub(crate) struct ErrorImpl { vtable: &'static ErrorVTable, backtrace: Option, + caller: Option<&'static Location<'static>>, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, @@ -876,6 +882,10 @@ impl ErrorImpl { (vtable(this.ptr).object_ref)(this).deref() } + pub(crate) unsafe fn caller(this: Ref) -> Option<&Location<'static>> { + this.deref().caller + } + #[cfg(feature = "std")] pub(crate) unsafe fn error_mut(this: Mut) -> &mut (dyn StdError + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right diff --git a/src/fmt.rs b/src/fmt.rs index 03d8fd3..1da9886 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -24,6 +24,9 @@ impl ErrorImpl { } write!(f, "{}", error)?; + if let Some(location) = Self::caller(this) { + writeln!(f, "\n at {}:{}", location.file(), location.line())?; + } if let Some(cause) = error.source() { write!(f, "\n\nCaused by:")?; diff --git a/src/kind.rs b/src/kind.rs index f47fe44..872d296 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -67,7 +67,7 @@ impl Adhoc { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message, backtrace!(), caller!()) } } @@ -111,6 +111,6 @@ impl Boxed { #[cold] pub fn new(self, error: Box) -> Error { let backtrace = backtrace_if_absent!(&*error); - Error::from_boxed(error, backtrace) + Error::from_boxed(error, backtrace, caller!()) } }