From f416f66d5c55ff04d7dc17544b6523236ef01411 Mon Sep 17 00:00:00 2001 From: Michael Pfaff Date: Thu, 29 Jun 2023 02:14:03 -0400 Subject: [PATCH] Move Backtrace to a context element, capture along with Location --- src/context.rs | 43 +++++++++++++++++++++++++++++--- src/explain.rs | 17 ++++++++++++- src/how.rs | 67 ++++++-------------------------------------------- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/src/context.rs b/src/context.rs index 949314a..8ebbe38 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,10 +1,12 @@ use std::borrow::Cow; - +#[cfg(feature = "backtrace")] +use std::backtrace::BacktraceStatus; use std::fmt; use std::panic::Location; /// Provides context furthering the explanation of *how* you got to an error. -#[derive(Debug, Clone)] +#[derive(Debug)] +#[cfg_attr(feature = "clone-with-caveats", derive(Clone))] pub struct Context(pub(crate) ContextInner); impl fmt::Display for Context { @@ -14,17 +16,37 @@ impl fmt::Display for Context { } } -#[derive(Debug, Clone)] +#[derive(Debug)] +#[cfg_attr(feature = "clone-with-caveats", derive(Clone))] pub(crate) enum ContextInner { Elem(ContextElem), Compound(Vec), } -#[derive(Debug, Clone)] +#[derive(Debug)] +#[cfg_attr(feature = "clone-with-caveats", derive(Clone))] pub(crate) enum ContextElem { Str(&'static str), String(String), Location(Location<'static>), + #[cfg(feature = "backtrace")] + Backtrace(Backtrace), +} + +// will be replaced with std::backtrace::Backtrace if and when it is Clone +#[cfg(feature = "backtrace")] +#[derive(Debug)] +pub(crate) enum Backtrace { + Disabled, + Unsupported, + Other(BacktraceStatus, String), +} + +#[cfg(all(feature = "backtrace", feature = "clone-with-caveats"))] +impl Clone for Backtrace { + fn clone(&self) -> Self { + Backtrace::Disabled + } } impl fmt::Display for ContextInner { @@ -51,6 +73,19 @@ impl fmt::Display for ContextElem { Self::Str(s) => f.write_str(s), Self::String(s) => f.write_str(s), Self::Location(l) => write!(f, "At {l}"), + #[cfg(feature = "backtrace")] + Self::Backtrace(Backtrace::Unsupported) => f.write_str("\nI'd like to show you a backtrace,\n but it's not supported on your platform"), + #[cfg(feature = "backtrace")] + Self::Backtrace(Backtrace::Disabled) => f.write_str("\nIf you'd like a backtrace,\n try again with RUST_BACKTRACE=1"), + #[cfg(feature = "backtrace")] + Self::Backtrace(Backtrace::Other(status, bt)) => { + f.write_str(if *status == BacktraceStatus::Captured { + "\nHere is the backtrace:\n" + } else { + "\nI can't tell if backtraces are working,\n but I'll give it a go:\n" + })?; + write!(f, "{}", bt) + }, } } } diff --git a/src/explain.rs b/src/explain.rs index 715991a..5714d72 100644 --- a/src/explain.rs +++ b/src/explain.rs @@ -19,9 +19,24 @@ impl Explain for How { use crate::context::*; let mut context = context.into_context(); let loc = ContextElem::Location(*Location::caller()); + #[cfg(feature = "backtrace")] + let bt = { + use std::backtrace::BacktraceStatus::*; + let bt = std::backtrace::Backtrace::capture(); + let bt = match bt.status() { + Disabled => Backtrace::Disabled, + Unsupported => Backtrace::Unsupported, + status => Backtrace::Other(status, bt.to_string()), + }; + ContextElem::Backtrace(bt) + }; match context.0 { ContextInner::Elem(elem) => { - context.0 = ContextInner::Compound(vec![elem, loc]); + #[cfg(not(feature = "backtrace"))] + let vec = vec![elem, loc]; + #[cfg(feature = "backtrace")] + let vec = vec![elem, loc, bt]; + context.0 = ContextInner::Compound(vec); } ContextInner::Compound(ref mut vec) => { vec.insert(1, loc); diff --git a/src/how.rs b/src/how.rs index 95937cb..e49b6a7 100644 --- a/src/how.rs +++ b/src/how.rs @@ -1,6 +1,4 @@ use core::panic::Location; -#[cfg(feature = "backtrace")] -use std::backtrace::BacktraceStatus; use crate::*; @@ -11,26 +9,16 @@ use crate::report::report_write; /// By default, does not implement [`Clone`] because [`std::backtrace::Backtrace`] does not /// implement [`Clone`]. However, the `clone-with-caveats` feature may be used to enable a /// [`Clone`] impl that sets the cloned `backtrace` to [`std::backtrace::Backtrace::disabled`]. +#[cfg_attr(feature = "clone-with-caveats", derive(Clone))] pub struct How(Box); struct HowInner { location: &'static Location<'static>, - #[cfg(feature = "backtrace")] - backtrace: Backtrace, // TODO: consider storing this vec inline (sharing the allocation with rest of the struct. // Probably move after `backtrace`) context: Vec, } -// will be replaced with std::backtrace::Backtrace if and when it is Clone -#[cfg(feature = "backtrace")] -#[derive(Debug)] -enum Backtrace { - Disabled, - Unsupported, - Other(BacktraceStatus, String), -} - impl How { #[must_use] #[inline(never)] @@ -40,29 +28,10 @@ impl How { Self(Box::new(HowInner { location, context: Vec::with_capacity(4), - #[cfg(feature = "backtrace")] - backtrace: { - let bt = std::backtrace::Backtrace::capture(); - match bt.status() { - BacktraceStatus::Disabled => Backtrace::Disabled, - BacktraceStatus::Unsupported => Backtrace::Unsupported, - status => Backtrace::Other(status, bt.to_string()), - } - }, })) .context(context) } - #[must_use] - pub fn clone_without_backtrace(&self) -> Self { - Self(Box::new(HowInner { - location: self.0.location, - context: self.0.context.clone(), - #[cfg(feature = "backtrace")] - backtrace: Backtrace::Disabled, - })) - } - pub fn location(&self) -> &'static Location { self.0.location } @@ -103,8 +72,6 @@ impl How { let mut b = f.debug_struct(std::any::type_name::()); b.field("location", &(&self.0.location)); b.field("context", &(&self.0.context)); - #[cfg(feature = "backtrace")] - let b = b.field("backtrace", &self.0.backtrace); b.finish() } @@ -114,19 +81,18 @@ impl How { } #[cfg(feature = "clone-with-caveats")] -impl Clone for How { +impl Clone for HowInner { #[inline] fn clone(&self) -> Self { - self.clone_without_backtrace() + Self { + location: self.location, + context: self.context.clone(), + } } fn clone_from(&mut self, source: &Self) { - self.0.location = source.0.location; - self.0.context.clone_from(&source.0.context); - #[cfg(feature = "backtrace")] - { - self.0.backtrace = Backtrace::Disabled; - } + self.location = source.location; + self.context.clone_from(&source.context); } } @@ -141,23 +107,6 @@ impl std::fmt::Display for How { } report_write!(f, &opts.indent().next(), "{ctx}")?; } - #[cfg(feature = "backtrace")] - { - use std::backtrace::BacktraceStatus::*; - let bt = &self.0.backtrace; - match bt { - Backtrace::Unsupported => f.write_str("\nI'd like to show you a backtrace,\n but it's not supported on your platform")?, - Backtrace::Disabled => f.write_str("\nIf you'd like a backtrace,\n try again with RUST_BACKTRACE=1")?, - Backtrace::Other(status, bt) => { - f.write_str(if *status == Captured { - "\nHere is the backtrace:" - } else { - "\nI can't tell if backtraces are working,\n but I'll give it a go:" - })?; - report_write!(f, &opts, "\n{}", bt)?; - } - } - } Ok(()) } }