diff --git a/crates/tor-circmgr/src/lib.rs b/crates/tor-circmgr/src/lib.rs index 430f7d45e..fe9849dee 100644 --- a/crates/tor-circmgr/src/lib.rs +++ b/crates/tor-circmgr/src/lib.rs @@ -39,6 +39,7 @@ use tor_basic_utils::retry::RetryDelay; use tor_chanmgr::ChanMgr; +use tor_error::ErrorReport; use tor_linkspec::ChanTarget; use tor_netdir::{DirEvent, NetDir, NetDirProvider, Timeliness}; use tor_proto::circuit::{CircParameters, ClientCirc, UniqId}; @@ -642,7 +643,7 @@ impl CircMgr { match statemgr.try_lock() { Err(e) => { - error!("Problem with state lock file: {}", e); + error!("Problem with state lock file: {}", e.report()); break; } Ok(NewlyAcquired) => { diff --git a/crates/tor-error/src/internal.rs b/crates/tor-error/src/internal.rs index 803ba1e10..5babd4071 100644 --- a/crates/tor-error/src/internal.rs +++ b/crates/tor-error/src/internal.rs @@ -291,6 +291,17 @@ mod test { assert!(s.contains("Couldn't wobble the wobbling device.")); #[cfg(feature = "backtrace")] assert!(s.contains("internal_macro_test")); + + #[derive(thiserror::Error, Debug)] + enum Wrap { + #[error("Internal error")] + Internal(#[from] Bug), + } + + let w: Wrap = e.into(); + let s = format!("Got: {}", w.report()); + dbg!(&s); + assert!(s.contains("Couldn't wobble the wobbling device.")); } #[test] diff --git a/crates/tor-error/src/report.rs b/crates/tor-error/src/report.rs index 803a0dd9f..96f828902 100644 --- a/crates/tor-error/src/report.rs +++ b/crates/tor-error/src/report.rs @@ -1,5 +1,6 @@ //! The Report type which reports errors nicely +use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; /// Wraps any Error, providing a nicely-reporting Display impl @@ -7,15 +8,15 @@ use std::fmt::{self, Debug, Display}; #[allow(clippy::exhaustive_structs)] // this is a transparent wrapper pub struct Report(pub E) where - E: AsRef; + E: AsRef; impl Display for Report where - E: AsRef, + E: AsRef, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// Non-generic inner function avoids code bloat - fn inner(mut e: &dyn std::error::Error, f: &mut fmt::Formatter) -> fmt::Result { + fn inner(mut e: &dyn StdError, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "error")?; let mut last = String::new(); loop { @@ -44,7 +45,7 @@ where #[allow(clippy::print_stderr)] // this is the point of this function pub fn report_and_exit(e: E) -> R where - E: AsRef, + E: AsRef, { /// Non-generic inner function avoids code bloat fn eprint_progname() { @@ -58,10 +59,41 @@ where std::process::exit(127) } +/// Helper type for reporting errors that are concrete implementors of `StdError` +/// +/// This is an opaque type, only constructable via the `ErrorExt` helper trait +/// and only useable via its `AsRef` implementation. +// +// We need this because Rust's trait object handling rules, and provided AsRef impls, +// are rather anaemic. We cannot simply put a &dyn Error into Report, because +// &dyn Error doesn't impl AsRef even though the implementation is trivial. +// We can't provide that AsRef impl ourselves due to trait coherency rules. +// So instead, we wrap up the &dyn Error in a newtype, for which we *can* provide the AsRef. +pub struct ReportHelper<'e>(&'e (dyn StdError + 'static)); +impl<'e> AsRef for ReportHelper<'e> { + fn as_ref(&self) -> &(dyn StdError + 'static) { + self.0 + } +} + +/// Extension trait providing `.report()` method on concrete errors +/// +/// This is implemented for types that directly implement [`std::error::Error`]` + 'static`. +/// For types like `anyhow::Error` that `impl AsRef`, +/// use `tor_error::Report(err)` directly. +pub trait ErrorReport: StdError + Sized + 'static { + /// Return an object that displays the error and its causes + // + // We would ideally have returned `Report>` but that's TAIT. + fn report(&self) -> Report { + Report(ReportHelper(self as _)) + } +} +impl ErrorReport for E {} + #[cfg(test)] mod test { use super::*; - use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/crates/tor-proto/src/channel/reactor.rs b/crates/tor-proto/src/channel/reactor.rs index 40b6f2c0a..b57928220 100644 --- a/crates/tor-proto/src/channel/reactor.rs +++ b/crates/tor-proto/src/channel/reactor.rs @@ -495,7 +495,7 @@ pub(crate) mod test { .build() .unwrap(); let send1 = send1.sink_map_err(|e| { - trace!("got sink error: {}", e); + trace!("got sink error: {:?}", e); CodecError::DecCell(tor_cell::Error::ChanProto("dummy message".into())) }); let (chan, reactor) = crate::channel::Channel::new(