diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 07f04aa2b911b..5514876c5d3b8 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -914,106 +914,109 @@ impl dyn Error + Send + Sync { /// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace /// ``` -// /// TODO: Report doesn't yet support return from `main` gracefully, fix in followup (yaahc) -// /// ## Return from `main` -// /// -// /// `Report` also implements `From` for all types that implement [`Error`], this when combined with -// /// the `Debug` output means `Report` is an ideal starting place for formatting errors returned -// /// from `main`. -// /// -// /// ``` -// /// #![feature(error_reporter)] -// /// use std::error::Report; -// /// # use std::error::Error; -// /// # use std::fmt; -// /// # #[derive(Debug)] -// /// # struct SuperError { -// /// # source: SuperErrorSideKick, -// /// # } -// /// # impl fmt::Display for SuperError { -// /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// /// # write!(f, "SuperError is here!") -// /// # } -// /// # } -// /// # impl Error for SuperError { -// /// # fn source(&self) -> Option<&(dyn Error + 'static)> { -// /// # Some(&self.source) -// /// # } -// /// # } -// /// # #[derive(Debug)] -// /// # struct SuperErrorSideKick; -// /// # impl fmt::Display for SuperErrorSideKick { -// /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// /// # write!(f, "SuperErrorSideKick is here!") -// /// # } -// /// # } -// /// # impl Error for SuperErrorSideKick {} -// /// # fn get_super_error() -> Result<(), SuperError> { -// /// # Err(SuperError { source: SuperErrorSideKick }) -// /// # } -// /// -// /// fn main() -> Result<(), Report> { -// /// get_super_error()?; -// /// } -// /// ``` -// /// -// /// This example produces the following output: -// /// -// /// ```console -// /// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 -// /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -// /// ``` -// /// -// /// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line -// /// output format, if you want to make sure your `Report`s are pretty printed and include backtrace -// /// you will need to manually convert and enable those flags. -// /// -// /// ``` -// /// #![feature(error_reporter)] -// /// use std::error::Report; -// /// # use std::error::Error; -// /// # use std::fmt; -// /// # #[derive(Debug)] -// /// # struct SuperError { -// /// # source: SuperErrorSideKick, -// /// # } -// /// # impl fmt::Display for SuperError { -// /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// /// # write!(f, "SuperError is here!") -// /// # } -// /// # } -// /// # impl Error for SuperError { -// /// # fn source(&self) -> Option<&(dyn Error + 'static)> { -// /// # Some(&self.source) -// /// # } -// /// # } -// /// # #[derive(Debug)] -// /// # struct SuperErrorSideKick; -// /// # impl fmt::Display for SuperErrorSideKick { -// /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// /// # write!(f, "SuperErrorSideKick is here!") -// /// # } -// /// # } -// /// # impl Error for SuperErrorSideKick {} -// /// # fn get_super_error() -> Result<(), SuperError> { -// /// # Err(SuperError { source: SuperErrorSideKick }) -// /// # } -// /// -// /// fn main() -> Result<(), Report> { -// /// get_super_error() -// /// .map_err(Report::new) -// /// .map_err(|r| r.pretty(true).show_backtrace(true))?; -// /// } -// /// ``` -// /// -// /// This example produces the following output: -// /// -// /// ```console -// /// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 -// /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -// /// ``` +/// +/// ## Return from `main` +/// +/// `Report` also implements `From` for all types that implement [`Error`], this when combined with +/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned +/// from `main`. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error()?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line +/// output format, if you want to make sure your `Report`s are pretty printed and include backtrace +/// you will need to manually convert and enable those flags. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error() +/// .map_err(Report::from) +/// .map_err(|r| r.pretty(true).show_backtrace(true))?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here! +/// +/// Caused by: +/// SuperErrorSideKick is here! +/// ``` #[unstable(feature = "error_reporter", issue = "90172")] -pub struct Report { +pub struct Report> { /// The error being reported. error: E, /// Whether a backtrace should be included as part of the report. @@ -1024,14 +1027,16 @@ pub struct Report { impl Report where - E: Error, + Report: From, { /// Create a new `Report` from an input error. #[unstable(feature = "error_reporter", issue = "90172")] pub fn new(error: E) -> Report { - Report { error, show_backtrace: false, pretty: false } + Self::from(error) } +} +impl Report { /// Enable pretty-printing the report across multiple lines. /// /// # Examples @@ -1232,7 +1237,81 @@ where self.show_backtrace = show_backtrace; self } +} + +impl Report +where + E: Error, +{ + fn backtrace(&self) -> Option<&Backtrace> { + // have to grab the backtrace on the first error directly since that error may not be + // 'static + let backtrace = self.error.backtrace(); + let backtrace = backtrace.or_else(|| { + self.error + .source() + .map(|source| source.chain().find_map(|source| source.backtrace())) + .flatten() + }); + backtrace + } + + /// Format the report as a single line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let sources = self.error.source().into_iter().flat_map(::chain); + + for cause in sources { + write!(f, ": {}", cause)?; + } + + Ok(()) + } + + /// Format the report as multiple lines, with each error cause on its own line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = &self.error; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + + for (ind, error) in cause.chain().enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + }; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } + } + } + + if self.show_backtrace { + let backtrace = self.backtrace(); + + if let Some(backtrace) = backtrace { + let backtrace = backtrace.to_string(); + + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; + } + } + + Ok(()) + } +} +impl Report> +{ fn backtrace(&self) -> Option<&Backtrace> { // have to grab the backtrace on the first error directly since that error may not be // 'static @@ -1306,7 +1385,18 @@ where E: Error, { fn from(error: E) -> Self { - Report::new(error) + Report { error, show_backtrace: false, pretty: false } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl<'a, E> From for Report> +where + E: Error + 'a, +{ + fn from(error: E) -> Self { + let error = box error; + Report { error, show_backtrace: false, pretty: false } } } @@ -1320,12 +1410,20 @@ where } } +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Display for Report> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } + } +} + // This type intentionally outputs the same format for `Display` and `Debug`for // situations where you unwrap a `Report` or return it from main. #[unstable(feature = "error_reporter", issue = "90172")] impl fmt::Debug for Report where - E: Error, + Report: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f)