From 59df6c8eb917cba41c15e3366f29ee780c9c74df Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Fri, 22 Oct 2021 12:21:10 -0500 Subject: [PATCH 01/13] Try commiting again --- library/std/src/error.rs | 245 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 1 deletion(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 6ae0bc47a9462..9fb8f2b9b8bc5 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; use crate::char; -use crate::fmt::{self, Debug, Display}; +use crate::fmt::{self, Debug, Display, Write}; use crate::mem::transmute; use crate::num; use crate::str; @@ -807,3 +807,246 @@ impl dyn Error + Send + Sync { }) } } + +/// An error reporter that exposes the entire error chain for printing. +/// It also exposes options for formatting the error chain, either entirely on a single line, +/// or in multi-line format with each cause in the error chain on a new line. +/// +/// # Examples +/// +/// ``` +/// #![feature(error_reporter)] +/// +/// use std::error::{Error, Report}; +/// use std::fmt; +/// +/// #[derive(Debug)] +/// struct SuperError { +/// side: 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.side) +/// } +/// } +/// +/// #[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 main() { +/// let error = SuperError { side: SuperErrorSideKick }; +/// let report = Report::new(&error).pretty(); +/// +/// println!("{}", report); +/// } +/// ``` +#[unstable(feature = "error_reporter", issue = "90172")] +pub struct Report { + source: E, + show_backtrace: bool, + pretty: bool, +} + +impl Report +where + E: Error, +{ + /// Create a new `Report` from an input error. + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn new(source: E) -> Report { + Report { source, show_backtrace: false, pretty: false } + } + + /// Enable pretty-printing the report. + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn pretty(mut self) -> Self { + self.pretty = true; + self + } + + /// Enable showing a backtrace for the report. + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn show_backtrace(mut self) -> Self { + self.show_backtrace = true; + self + } + + /// 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.source)?; + + let sources = self.source.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.source; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + let format = if multiple { + Format::Numbered { ind: 0 } + } else { + Format::Uniform { indentation: " " } + }; + + for error in cause.chain() { + writeln!(f)?; + + let mut indented = Indented { inner: f, needs_indent: true, format }; + + write!(indented, "{}", error)?; + } + } + + if self.show_backtrace { + let backtrace = error.backtrace(); + + if let Some(backtrace) = backtrace { + let mut backtrace = backtrace.to_string(); + + write!(f, "\n\n")?; + writeln!(f, "Stack backtrace:")?; + + backtrace.truncate(backtrace.trim_end().len()); + + write!(f, "{}", backtrace)?; + } + } + + Ok(()) + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl From for Report +where + E: Error, +{ + fn from(source: E) -> Self { + Report::new(source) + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Display for Report +where + E: Error, +{ + 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, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// Encapsulates how error sources are indented and formatted. +struct Indented<'a, D: ?Sized> { + inner: &'a mut D, + needs_indent: bool, + format: Format, +} + +/// The possible variants that error sources can be formatted as. +#[derive(Clone, Copy)] +enum Format { + /// Insert uniform indentation before every line. + /// + /// This format takes a static string as input and inserts it after every newline. + Uniform { + /// The string to insert as indentation. + indentation: &'static str, + }, + /// Inserts a number before the first line. + /// + /// This format hard codes the indentation level to match the indentation from + /// `std::backtrace::Backtrace`. + Numbered { + /// The index to insert before the first line of output. + ind: usize, + }, +} + +impl Write for Indented<'_, D> +where + D: Write + ?Sized, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (ind, line) in s.split('\n').enumerate() { + if ind > 0 { + self.inner.write_char('\n')?; + self.needs_indent = true; + } + + if self.needs_indent { + if line.is_empty() { + continue; + } + + self.format.insert_indentation(ind, &mut self.inner)?; + self.needs_indent = false; + } + + self.inner.write_fmt(format_args!("{}", line))?; + } + + Ok(()) + } +} + +impl Format { + /// Write the specified formatting to the write buffer. + fn insert_indentation(&mut self, line: usize, f: &mut dyn Write) -> fmt::Result { + match self { + Format::Uniform { indentation } => { + write!(f, "{}", indentation) + } + Format::Numbered { ind } => { + if line == 0 { + write!(f, "{: >4}: ", ind)?; + *ind += 1; + Ok(()) + } else { + write!(f, " ") + } + } + } + } +} From 6a59d0e3aa26543fc4d3eaae1fa4cd48522045d2 Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Fri, 22 Oct 2021 13:43:42 -0500 Subject: [PATCH 02/13] Have `pretty` and `show_backtrace` accept booleans --- library/std/src/error.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 9fb8f2b9b8bc5..5988075836d54 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -850,7 +850,7 @@ impl dyn Error + Send + Sync { /// /// fn main() { /// let error = SuperError { side: SuperErrorSideKick }; -/// let report = Report::new(&error).pretty(); +/// let report = Report::new(&error).pretty(true); /// /// println!("{}", report); /// } @@ -874,15 +874,15 @@ where /// Enable pretty-printing the report. #[unstable(feature = "error_reporter", issue = "90172")] - pub fn pretty(mut self) -> Self { - self.pretty = true; + pub fn pretty(mut self, pretty: bool) -> Self { + self.pretty = pretty; self } /// Enable showing a backtrace for the report. #[unstable(feature = "error_reporter", issue = "90172")] - pub fn show_backtrace(mut self) -> Self { - self.show_backtrace = true; + pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { + self.show_backtrace = show_backtrace; self } From c6de41331c732a8f70088ceab12a5049e3db0caa Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Fri, 22 Oct 2021 13:47:05 -0500 Subject: [PATCH 03/13] Change `source` field to `error` --- library/std/src/error.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 5988075836d54..52f3ebbdee47e 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -857,7 +857,7 @@ impl dyn Error + Send + Sync { /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { - source: E, + error: E, show_backtrace: bool, pretty: bool, } @@ -868,8 +868,8 @@ where { /// Create a new `Report` from an input error. #[unstable(feature = "error_reporter", issue = "90172")] - pub fn new(source: E) -> Report { - Report { source, show_backtrace: false, pretty: false } + pub fn new(error: E) -> Report { + Report { error, show_backtrace: false, pretty: false } } /// Enable pretty-printing the report. @@ -889,9 +889,9 @@ where /// 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.source)?; + write!(f, "{}", self.error)?; - let sources = self.source.source().into_iter().flat_map(::chain); + let sources = self.error.source().into_iter().flat_map(::chain); for cause in sources { write!(f, ": {}", cause)?; @@ -903,7 +903,7 @@ where /// 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.source; + let error = &self.error; write!(f, "{}", error)?; @@ -950,8 +950,8 @@ impl From for Report where E: Error, { - fn from(source: E) -> Self { - Report::new(source) + fn from(error: E) -> Self { + Report::new(error) } } From c0f14cb9301eacb51fc660f1d461cbc783f4aaa7 Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Wed, 27 Oct 2021 13:03:53 -0500 Subject: [PATCH 04/13] Attempt to fix tidy errors --- library/std/src/error.rs | 133 ++++++--------- library/std/src/error/tests.rs | 291 +++++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+), 79 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 52f3ebbdee47e..d8859cf1e552e 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; use crate::char; -use crate::fmt::{self, Debug, Display, Write}; +use crate::fmt::{self, Debug, Display}; use crate::mem::transmute; use crate::num; use crate::str; @@ -816,6 +816,7 @@ impl dyn Error + Send + Sync { /// /// ``` /// #![feature(error_reporter)] +/// #![feature(negative_impls)] /// /// use std::error::{Error, Report}; /// use std::fmt; @@ -848,6 +849,10 @@ impl dyn Error + Send + Sync { /// /// impl Error for SuperErrorSideKick {} /// +/// // Note that the error doesn't need to be `Send` or `Sync`. +/// impl !Send for SuperError {} +/// impl !Sync for SuperError {} +/// /// fn main() { /// let error = SuperError { side: SuperErrorSideKick }; /// let report = Report::new(&error).pretty(true); @@ -855,10 +860,37 @@ impl dyn Error + Send + Sync { /// println!("{}", report); /// } /// ``` +/// +/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the +/// wrapped error be `Send`, `Sync`, or `'static`. +/// +/// ```rust +/// # #![feature(error_reporter)] +/// # use std::fmt; +/// # use std::error::{Error, Report}; +/// #[derive(Debug)] +/// struct SuperError<'a> { +/// side: &'a str, +/// } +/// impl<'a> fmt::Display for SuperError<'a> { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "SuperError is here: {}", self.side) +/// } +/// } +/// impl<'a> Error for SuperError<'a> {} +/// fn main() { +/// let msg = String::from("Huzzah!"); +/// let report = Report::new(SuperError { side: &msg }); +/// println!("{}", report); +/// } +/// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { + /// The error being reported. error: E, + /// Whether a backtrace should be included as part of the report. show_backtrace: bool, + /// Whether the report should be pretty-printed. pretty: bool, } @@ -911,18 +943,15 @@ where write!(f, "\n\nCaused by:")?; let multiple = cause.source().is_some(); - let format = if multiple { - Format::Numbered { ind: 0 } - } else { - Format::Uniform { indentation: " " } - }; - for error in cause.chain() { + for (ind, error) in cause.chain().enumerate() { writeln!(f)?; - let mut indented = Indented { inner: f, needs_indent: true, format }; - - write!(indented, "{}", error)?; + if multiple { + write!(f, "{: >4}: {}", ind, Indented { source: error })?; + } else { + write!(f, " {}", error)?; + } } } @@ -930,14 +959,10 @@ where let backtrace = error.backtrace(); if let Some(backtrace) = backtrace { - let mut backtrace = backtrace.to_string(); - - write!(f, "\n\n")?; - writeln!(f, "Stack backtrace:")?; + let backtrace = backtrace.to_string(); - backtrace.truncate(backtrace.trim_end().len()); - - write!(f, "{}", backtrace)?; + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; } } @@ -977,76 +1002,26 @@ where } } -/// Encapsulates how error sources are indented and formatted. -struct Indented<'a, D: ?Sized> { - inner: &'a mut D, - needs_indent: bool, - format: Format, -} - -/// The possible variants that error sources can be formatted as. -#[derive(Clone, Copy)] -enum Format { - /// Insert uniform indentation before every line. - /// - /// This format takes a static string as input and inserts it after every newline. - Uniform { - /// The string to insert as indentation. - indentation: &'static str, - }, - /// Inserts a number before the first line. - /// - /// This format hard codes the indentation level to match the indentation from - /// `std::backtrace::Backtrace`. - Numbered { - /// The index to insert before the first line of output. - ind: usize, - }, +/// Wrapper type for indenting the inner source. +struct Indented { + source: D, } -impl Write for Indented<'_, D> +impl fmt::Display for Indented where - D: Write + ?Sized, + D: fmt::Display, { - fn write_str(&mut self, s: &str) -> fmt::Result { - for (ind, line) in s.split('\n').enumerate() { - if ind > 0 { - self.inner.write_char('\n')?; - self.needs_indent = true; - } - - if self.needs_indent { - if line.is_empty() { - continue; - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let source = self.source.to_string(); - self.format.insert_indentation(ind, &mut self.inner)?; - self.needs_indent = false; + for (ind, line) in source.trim().split('\n').filter(|l| !l.is_empty()).enumerate() { + if ind > 0 { + write!(f, "\n {}", line)?; + } else { + write!(f, "{}", line)?; } - - self.inner.write_fmt(format_args!("{}", line))?; } Ok(()) } } - -impl Format { - /// Write the specified formatting to the write buffer. - fn insert_indentation(&mut self, line: usize, f: &mut dyn Write) -> fmt::Result { - match self { - Format::Uniform { indentation } => { - write!(f, "{}", indentation) - } - Format::Numbered { ind } => { - if line == 0 { - write!(f, "{: >4}: ", ind)?; - *ind += 1; - Ok(()) - } else { - write!(f, " ") - } - } - } - } -} diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 66d6924f34d2b..c408915ca71a9 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -35,3 +35,294 @@ fn downcasting() { Err(e) => assert_eq!(*e.downcast::().unwrap(), A), } } + +use crate::backtrace; +use crate::env; +use crate::error::Report; + +#[derive(Debug)] +struct SuperError { + side: 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.side) + } +} + +#[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 {} + +#[test] +fn single_line_formatting() { + let error = SuperError { side: SuperErrorSideKick }; + let report = Report::new(&error); + let actual = report.to_string(); + let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn multi_line_formatting() { + let error = SuperError { side: SuperErrorSideKick }; + let report = Report::new(&error).pretty(true); + let actual = report.to_string(); + let expected = + String::from("SuperError is here!\n\nCaused by:\n SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_single_line_correctly() { + let report = Report::new(SuperErrorSideKick); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_multi_line_correctly() { + let report = Report::new(SuperErrorSideKick).pretty(true); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_backtrace_outputs_correctly() { + use backtrace::Backtrace; + + env::remove_var("RUST_BACKTRACE"); + + #[derive(Debug)] + struct ErrorWithBacktrace<'a> { + msg: &'a str, + trace: Backtrace, + } + + impl<'a> fmt::Display for ErrorWithBacktrace<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error with backtrace: {}", self.msg) + } + } + + impl<'a> Error for ErrorWithBacktrace<'a> { + fn backtrace(&self) -> Option<&Backtrace> { + Some(&self.trace) + } + } + + let msg = String::from("The source of the error"); + let report = Report::new(ErrorWithBacktrace { msg: &msg, trace: Backtrace::capture() }) + .pretty(true) + .show_backtrace(true); + + let expected = String::from( + "Error with backtrace: The source of the error\n\nStack backtrace:\ndisabled backtrace", + ); + + assert_eq!(expected, report.to_string()); +} + +#[derive(Debug)] +struct GenericError { + message: D, + source: Option>, +} + +impl GenericError { + fn new(message: D) -> GenericError { + Self { message, source: None } + } + + fn new_with_source(message: D, source: E) -> GenericError + where + E: Error + 'static, + { + let source: Box = Box::new(source); + let source = Some(source); + GenericError { message, source } + } +} + +impl fmt::Display for GenericError +where + D: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.message, f) + } +} + +impl Error for GenericError +where + D: fmt::Debug + fmt::Display, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_deref() + } +} + +#[test] +fn error_formats_single_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error); + let expected = r#"line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6"#; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn error_formats_multi_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = r#"line 1 +line 2 +line 3 +line 4 +line 5 +line 6 + +Caused by: + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"#; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_that_start_with_newline_formats_correctly() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("\nThe message\n") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = r#" +The message + + +Caused by: + 0: The message + 1: The message"#; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_string_interpolation_formats_correctly() { + #[derive(Debug)] + struct MyMessage(usize); + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got an error code: ({}). ", self.0)?; + write!(f, "What would you like to do in response?") + } + } + + let error = GenericError::new(MyMessage(10)); + let error = GenericError::new_with_source(MyMessage(20), error); + let report = Report::new(error).pretty(true); + let expected = r#"Got an error code: (20). What would you like to do in response? + +Caused by: + Got an error code: (10). What would you like to do in response?"#; + let actual = report.to_string(); + assert_eq!(expected, actual); +} From aa853bd31775db7fcb5074f78d8b989053ae101d Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Wed, 27 Oct 2021 13:04:42 -0500 Subject: [PATCH 05/13] Add `rust` annotation to doctest --- library/std/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index d8859cf1e552e..a6d36dbe5c794 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -814,7 +814,7 @@ impl dyn Error + Send + Sync { /// /// # Examples /// -/// ``` +/// ```rust /// #![feature(error_reporter)] /// #![feature(negative_impls)] /// From d2f49eeb176a670c76f3c8c6005208a9d3cc30ee Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Wed, 27 Oct 2021 13:18:22 -0500 Subject: [PATCH 06/13] Format doctest --- library/std/src/error.rs | 55 ++++++++-------------------------------- 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index a6d36dbe5c794..26fa6c38549d3 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -811,6 +811,8 @@ impl dyn Error + Send + Sync { /// An error reporter that exposes the entire error chain for printing. /// It also exposes options for formatting the error chain, either entirely on a single line, /// or in multi-line format with each cause in the error chain on a new line. +/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the +/// wrapped error be `Send`, `Sync`, or `'static`. /// /// # Examples /// @@ -822,68 +824,31 @@ impl dyn Error + Send + Sync { /// use std::fmt; /// /// #[derive(Debug)] -/// struct SuperError { -/// side: 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.side) -/// } +/// struct SuperError<'a> { +/// side: &'a str, /// } /// -/// #[derive(Debug)] -/// struct SuperErrorSideKick; -/// -/// impl fmt::Display for SuperErrorSideKick { +/// impl<'a> fmt::Display for SuperError<'a> { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// write!(f, "SuperErrorSideKick is here!") +/// write!(f, "SuperError is here: {}", self.side) /// } /// } /// -/// impl Error for SuperErrorSideKick {} +/// impl<'a> Error for SuperError<'a> {} /// /// // Note that the error doesn't need to be `Send` or `Sync`. /// impl !Send for SuperError {} /// impl !Sync for SuperError {} /// /// fn main() { -/// let error = SuperError { side: SuperErrorSideKick }; +/// let msg = String::from("Huzzah!"); +/// let error = SuperError { side: &msg }; /// let report = Report::new(&error).pretty(true); /// /// println!("{}", report); /// } /// ``` -/// -/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the -/// wrapped error be `Send`, `Sync`, or `'static`. -/// -/// ```rust -/// # #![feature(error_reporter)] -/// # use std::fmt; -/// # use std::error::{Error, Report}; -/// #[derive(Debug)] -/// struct SuperError<'a> { -/// side: &'a str, -/// } -/// impl<'a> fmt::Display for SuperError<'a> { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// write!(f, "SuperError is here: {}", self.side) -/// } -/// } -/// impl<'a> Error for SuperError<'a> {} -/// fn main() { -/// let msg = String::from("Huzzah!"); -/// let report = Report::new(SuperError { side: &msg }); -/// println!("{}", report); -/// } -/// ``` + #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { /// The error being reported. From 32bcb8113f750db36b9590b516a9fc40c0fa99df Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Wed, 27 Oct 2021 13:59:02 -0500 Subject: [PATCH 07/13] Fix broken doctest --- library/std/src/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 26fa6c38549d3..b45cfa3450684 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -837,8 +837,8 @@ impl dyn Error + Send + Sync { /// impl<'a> Error for SuperError<'a> {} /// /// // Note that the error doesn't need to be `Send` or `Sync`. -/// impl !Send for SuperError {} -/// impl !Sync for SuperError {} +/// impl<'a> !Send for SuperError<'a> {} +/// impl<'a> !Sync for SuperError<'a> {} /// /// fn main() { /// let msg = String::from("Huzzah!"); From 1386a15529f5241402125b37eda7a5bb03fbd247 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Tue, 14 Dec 2021 13:56:49 -0800 Subject: [PATCH 08/13] Update std::error::Report based on feedback --- library/std/src/error.rs | 74 +++++++++----- library/std/src/error/tests.rs | 178 +++++++++++++++++++++++---------- 2 files changed, 171 insertions(+), 81 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b45cfa3450684..10de248c3d7ec 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; use crate::char; -use crate::fmt::{self, Debug, Display}; +use crate::fmt::{self, Debug, Display, Write}; use crate::mem::transmute; use crate::num; use crate::str; @@ -63,7 +63,7 @@ pub trait Error: Debug + Display { /// /// #[derive(Debug)] /// struct SuperError { - /// side: SuperErrorSideKick, + /// source: SuperErrorSideKick, /// } /// /// impl fmt::Display for SuperError { @@ -74,7 +74,7 @@ pub trait Error: Debug + Display { /// /// impl Error for SuperError { /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// Some(&self.side) + /// Some(&self.source) /// } /// } /// @@ -90,7 +90,7 @@ pub trait Error: Debug + Display { /// impl Error for SuperErrorSideKick {} /// /// fn get_super_error() -> Result<(), SuperError> { - /// Err(SuperError { side: SuperErrorSideKick }) + /// Err(SuperError { source: SuperErrorSideKick }) /// } /// /// fn main() { @@ -836,10 +836,6 @@ impl dyn Error + Send + Sync { /// /// impl<'a> Error for SuperError<'a> {} /// -/// // Note that the error doesn't need to be `Send` or `Sync`. -/// impl<'a> !Send for SuperError<'a> {} -/// impl<'a> !Sync for SuperError<'a> {} -/// /// fn main() { /// let msg = String::from("Huzzah!"); /// let error = SuperError { side: &msg }; @@ -883,6 +879,19 @@ where self } + 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 { @@ -911,17 +920,17 @@ where for (ind, error) in cause.chain().enumerate() { writeln!(f)?; - - if multiple { - write!(f, "{: >4}: {}", ind, Indented { source: error })?; - } else { - write!(f, " {}", error)?; - } + let mut indented = Indented { + inner: f, + number: if multiple { Some(ind) } else { None }, + started: false, + }; + write!(indented, "{}", error)?; } } if self.show_backtrace { - let backtrace = error.backtrace(); + let backtrace = self.backtrace(); if let Some(backtrace) = backtrace { let backtrace = backtrace.to_string(); @@ -968,23 +977,34 @@ where } /// Wrapper type for indenting the inner source. -struct Indented { - source: D, +struct Indented<'a, D> { + inner: &'a mut D, + number: Option, + started: bool, } -impl fmt::Display for Indented +impl Write for Indented<'_, T> where - D: fmt::Display, + T: Write, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let source = self.source.to_string(); - - for (ind, line) in source.trim().split('\n').filter(|l| !l.is_empty()).enumerate() { - if ind > 0 { - write!(f, "\n {}", line)?; - } else { - write!(f, "{}", line)?; + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => write!(self.inner, "{: >5}: ", number)?, + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } } + + self.inner.write_str(line)?; } Ok(()) diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index c408915ca71a9..82ef39ae90fb6 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -36,13 +36,12 @@ fn downcasting() { } } -use crate::backtrace; -use crate::env; +use crate::backtrace::Backtrace; use crate::error::Report; #[derive(Debug)] struct SuperError { - side: SuperErrorSideKick, + source: SuperErrorSideKick, } impl fmt::Display for SuperError { @@ -53,7 +52,7 @@ impl fmt::Display for SuperError { impl Error for SuperError { fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.side) + Some(&self.source) } } @@ -70,7 +69,7 @@ impl Error for SuperErrorSideKick {} #[test] fn single_line_formatting() { - let error = SuperError { side: SuperErrorSideKick }; + let error = SuperError { source: SuperErrorSideKick }; let report = Report::new(&error); let actual = report.to_string(); let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); @@ -80,7 +79,7 @@ fn single_line_formatting() { #[test] fn multi_line_formatting() { - let error = SuperError { side: SuperErrorSideKick }; + let error = SuperError { source: SuperErrorSideKick }; let report = Report::new(&error).pretty(true); let actual = report.to_string(); let expected = @@ -108,50 +107,57 @@ fn error_with_no_sources_formats_multi_line_correctly() { } #[test] -fn error_with_backtrace_outputs_correctly() { - use backtrace::Backtrace; +fn error_with_backtrace_outputs_correctly_with_one_source() { + let trace = Backtrace::force_capture(); + let expected = format!("The source of the error - env::remove_var("RUST_BACKTRACE"); +Caused by: + Error with backtrace - #[derive(Debug)] - struct ErrorWithBacktrace<'a> { - msg: &'a str, - trace: Backtrace, - } +Stack backtrace: +{}", trace); + let error = GenericError::new("Error with backtrace"); + let mut error = GenericError::new_with_source("The source of the error", error); + error.backtrace = Some(trace); + let report = Report::new(error).pretty(true).show_backtrace(true); - impl<'a> fmt::Display for ErrorWithBacktrace<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error with backtrace: {}", self.msg) - } - } - impl<'a> Error for ErrorWithBacktrace<'a> { - fn backtrace(&self) -> Option<&Backtrace> { - Some(&self.trace) - } - } + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_two_sources() { + let trace = Backtrace::force_capture(); + let expected = format!("Error with two sources - let msg = String::from("The source of the error"); - let report = Report::new(ErrorWithBacktrace { msg: &msg, trace: Backtrace::capture() }) - .pretty(true) - .show_backtrace(true); +Caused by: + 0: The source of the error + 1: Error with backtrace - let expected = String::from( - "Error with backtrace: The source of the error\n\nStack backtrace:\ndisabled backtrace", - ); +Stack backtrace: +{}", trace); + let mut error = GenericError::new("Error with backtrace"); + error.backtrace = Some(trace); + let error = GenericError::new_with_source("The source of the error", error); + let error = GenericError::new_with_source("Error with two sources", error); + let report = Report::new(error).pretty(true).show_backtrace(true); - assert_eq!(expected, report.to_string()); + + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); } #[derive(Debug)] struct GenericError { message: D, + backtrace: Option, source: Option>, } impl GenericError { fn new(message: D) -> GenericError { - Self { message, source: None } + Self { message, backtrace: None, source: None } } fn new_with_source(message: D, source: E) -> GenericError @@ -160,7 +166,7 @@ impl GenericError { { let source: Box = Box::new(source); let source = Some(source); - GenericError { message, source } + GenericError { message, backtrace: None, source } } } @@ -180,6 +186,10 @@ where fn source(&self) -> Option<&(dyn Error + 'static)> { self.source.as_deref() } + + fn backtrace(&self) -> Option<&Backtrace> { + self.backtrace.as_ref() + } } #[test] @@ -254,24 +264,24 @@ line 5 line 6 Caused by: - 0: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 1: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 2: line 1 - line 2 - line 3 - line 4 - line 5 - line 6"#; + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"#; let actual = report.to_string(); assert_eq!(expected, actual); @@ -297,8 +307,12 @@ The message Caused by: - 0: The message - 1: The message"#; + 0: + The message + + 1: + The message + "#; let actual = report.to_string(); assert_eq!(expected, actual); @@ -326,3 +340,59 @@ Caused by: let actual = report.to_string(); assert_eq!(expected, actual); } + +#[test] +fn empty_lines_mid_message() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\n\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = r#"line 1 + +line 2 + +Caused by: + 0: line 1 + + line 2 + 1: line 1 + + line 2"#; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn only_one_source() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = r#"line 1 +line 2 + +Caused by: + line 1 + line 2"#; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} From 4420cc33d6686c9d4ae6bf490b977fc47e56d340 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 16 Dec 2021 14:06:28 -0800 Subject: [PATCH 09/13] Update report output and fix examples --- library/std/src/error.rs | 195 +++++++++++++++++++++++++++------ library/std/src/error/tests.rs | 132 ++++++++++++++-------- 2 files changed, 245 insertions(+), 82 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 10de248c3d7ec..a2b4eb2117dcc 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -808,9 +808,11 @@ impl dyn Error + Send + Sync { } } -/// An error reporter that exposes the entire error chain for printing. -/// It also exposes options for formatting the error chain, either entirely on a single line, -/// or in multi-line format with each cause in the error chain on a new line. +/// An error reporter that print's an error and its sources. +/// +/// Report also exposes configuration options for formatting the error chain, either entirely on a +/// single line, or in multi-line format with each cause in the error chain on a new line. +/// /// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the /// wrapped error be `Send`, `Sync`, or `'static`. /// @@ -818,33 +820,51 @@ impl dyn Error + Send + Sync { /// /// ```rust /// #![feature(error_reporter)] -/// #![feature(negative_impls)] -/// /// use std::error::{Error, Report}; /// use std::fmt; /// /// #[derive(Debug)] -/// struct SuperError<'a> { -/// side: &'a str, +/// struct SuperError { +/// source: SuperErrorSideKick, /// } /// -/// impl<'a> fmt::Display for SuperError<'a> { +/// impl fmt::Display for SuperError { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// write!(f, "SuperError is here: {}", self.side) +/// write!(f, "SuperError is here!") /// } /// } /// -/// impl<'a> Error for SuperError<'a> {} +/// impl Error for SuperError { +/// fn source(&self) -> Option<&(dyn Error + 'static)> { +/// Some(&self.source) +/// } +/// } /// -/// fn main() { -/// let msg = String::from("Huzzah!"); -/// let error = SuperError { side: &msg }; -/// let report = Report::new(&error).pretty(true); +/// #[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 {} /// -/// println!("{}", report); +/// fn get_super_error() -> Result<(), SuperError> { +/// Err(SuperError { source: SuperErrorSideKick }) +/// } +/// +/// fn main() { +/// match get_super_error() { +/// Err(e) => { +/// let report = Report::new(e).pretty(true); +/// println!("Error: {}", report); +/// } +/// _ => println!("No error"), +/// } /// } /// ``` - #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { /// The error being reported. @@ -865,14 +885,129 @@ where Report { error, show_backtrace: false, pretty: false } } - /// Enable pretty-printing the report. + /// Enable pretty-printing the report across multiple lines. + /// + /// # Examples + /// + /// ```rust + /// #![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 {} + /// + /// let error = SuperError { source: SuperErrorSideKick }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub fn pretty(mut self, pretty: bool) -> Self { self.pretty = pretty; self } - /// Enable showing a backtrace for the report. + /// Display backtrace if available when using pretty output format. + /// + /// # Examples + /// + /// ```rust + /// #![feature(error_reporter)] + /// #![feature(backtrace)] + /// use std::error::{Error, Report}; + /// use std::backtrace::Backtrace; + /// 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 { + /// backtrace: Backtrace, + /// } + /// + /// impl fmt::Display for SuperErrorSideKick { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "SuperErrorSideKick is here!") + /// } + /// } + /// + /// impl Error for SuperErrorSideKick { + /// fn backtrace(&self) -> Option<&Backtrace> { + /// Some(&self.backtrace) + /// } + /// } + /// + /// let source = SuperErrorSideKick { backtrace: Backtrace::force_capture() }; + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true).show_backtrace(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces something similar to the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// + /// Stack backtrace: + /// 0: rust_out::main::_doctest_main_src_error_rs_943_0 + /// 1: rust_out::main + /// 2: core::ops::function::FnOnce::call_once + /// 3: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 4: std::rt::lang_start::{{closure}} + /// 5: std::panicking::try + /// 6: std::rt::lang_start_internal + /// 7: std::rt::lang_start + /// 8: main + /// 9: __libc_start_main + /// 10: _start + /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { self.show_backtrace = show_backtrace; @@ -922,10 +1057,12 @@ where writeln!(f)?; let mut indented = Indented { inner: f, - number: if multiple { Some(ind) } else { None }, - started: false, }; - write!(indented, "{}", error)?; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } } } @@ -979,8 +1116,6 @@ where /// Wrapper type for indenting the inner source. struct Indented<'a, D> { inner: &'a mut D, - number: Option, - started: bool, } impl Write for Indented<'_, T> @@ -989,19 +1124,9 @@ where { fn write_str(&mut self, s: &str) -> fmt::Result { for (i, line) in s.split('\n').enumerate() { - if !self.started { - self.started = true; - match self.number { - Some(number) => write!(self.inner, "{: >5}: ", number)?, - None => self.inner.write_str(" ")?, - } - } else if i > 0 { + if i > 0 { self.inner.write_char('\n')?; - if self.number.is_some() { - self.inner.write_str(" ")?; - } else { - self.inner.write_str(" ")?; - } + self.inner.write_str(" ")?; } self.inner.write_str(line)?; diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 82ef39ae90fb6..0835e282c46c3 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -82,8 +82,11 @@ fn multi_line_formatting() { let error = SuperError { source: SuperErrorSideKick }; let report = Report::new(&error).pretty(true); let actual = report.to_string(); - let expected = - String::from("SuperError is here!\n\nCaused by:\n SuperErrorSideKick is here!"); + let expected = String::from("\ +SuperError is here! + +Caused by: + SuperErrorSideKick is here!"); assert_eq!(expected, actual); } @@ -109,10 +112,11 @@ fn error_with_no_sources_formats_multi_line_correctly() { #[test] fn error_with_backtrace_outputs_correctly_with_one_source() { let trace = Backtrace::force_capture(); - let expected = format!("The source of the error + let expected = format!("\ +The source of the error Caused by: - Error with backtrace + Error with backtrace Stack backtrace: {}", trace); @@ -129,11 +133,12 @@ Stack backtrace: #[test] fn error_with_backtrace_outputs_correctly_with_two_sources() { let trace = Backtrace::force_capture(); - let expected = format!("Error with two sources + let expected = format!("\ +Error with two sources Caused by: - 0: The source of the error - 1: Error with backtrace + 0: The source of the error + 1: Error with backtrace Stack backtrace: {}", trace); @@ -211,7 +216,8 @@ fn error_formats_single_line_with_rude_display_impl() { let error = GenericError::new_with_source(MyMessage, error); let error = GenericError::new_with_source(MyMessage, error); let report = Report::new(error); - let expected = r#"line 1 + let expected = "\ +line 1 line 2 line 3 line 4 @@ -231,7 +237,7 @@ line 2 line 3 line 4 line 5 -line 6"#; +line 6"; let actual = report.to_string(); assert_eq!(expected, actual); @@ -256,7 +262,7 @@ fn error_formats_multi_line_with_rude_display_impl() { let error = GenericError::new_with_source(MyMessage, error); let error = GenericError::new_with_source(MyMessage, error); let report = Report::new(error).pretty(true); - let expected = r#"line 1 + let expected = "line 1 line 2 line 3 line 4 @@ -264,24 +270,24 @@ line 5 line 6 Caused by: - 0: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 1: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 2: line 1 - line 2 - line 3 - line 4 - line 5 - line 6"#; + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"; let actual = report.to_string(); assert_eq!(expected, actual); @@ -302,19 +308,48 @@ fn errors_that_start_with_newline_formats_correctly() { let error = GenericError::new_with_source(MyMessage, error); let error = GenericError::new_with_source(MyMessage, error); let report = Report::new(error).pretty(true); - let expected = r#" + let expected = " The message Caused by: - 0: - The message - - 1: - The message - "#; + 0: + The message + + 1: + The message + "; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The message")?; + f.write_str(" goes on")?; + f.write_str(" and on.") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +The message goes on and on. + +Caused by: + 0: The message goes on and on. + 1: The message goes on and on."; let actual = report.to_string(); + println!("{}", actual); assert_eq!(expected, actual); } @@ -333,10 +368,11 @@ fn errors_with_string_interpolation_formats_correctly() { let error = GenericError::new(MyMessage(10)); let error = GenericError::new_with_source(MyMessage(20), error); let report = Report::new(error).pretty(true); - let expected = r#"Got an error code: (20). What would you like to do in response? + let expected = "\ +Got an error code: (20). What would you like to do in response? Caused by: - Got an error code: (10). What would you like to do in response?"#; + Got an error code: (10). What would you like to do in response?"; let actual = report.to_string(); assert_eq!(expected, actual); } @@ -356,17 +392,18 @@ fn empty_lines_mid_message() { let error = GenericError::new_with_source(MyMessage, error); let error = GenericError::new_with_source(MyMessage, error); let report = Report::new(error).pretty(true); - let expected = r#"line 1 + let expected = "\ +line 1 line 2 Caused by: - 0: line 1 - - line 2 - 1: line 1 - - line 2"#; + 0: line 1 + + line 2 + 1: line 1 + + line 2"; let actual = report.to_string(); assert_eq!(expected, actual); @@ -386,12 +423,13 @@ fn only_one_source() { let error = GenericError::new(MyMessage); let error = GenericError::new_with_source(MyMessage, error); let report = Report::new(error).pretty(true); - let expected = r#"line 1 + let expected = "\ +line 1 line 2 Caused by: - line 1 - line 2"#; + line 1 + line 2"; let actual = report.to_string(); assert_eq!(expected, actual); From 078b112d9452eb24cf6d5dffe8f4479cbe830d4e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 16 Dec 2021 14:22:35 -0800 Subject: [PATCH 10/13] add a panicking example --- library/std/src/error.rs | 55 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index a2b4eb2117dcc..cb74a0084c66d 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -857,14 +857,61 @@ impl dyn Error + Send + Sync { /// /// fn main() { /// match get_super_error() { -/// Err(e) => { -/// let report = Report::new(e).pretty(true); -/// println!("Error: {}", report); -/// } +/// Err(e) => println!("Error: {}", Report::new(e)), /// _ => println!("No error"), /// } /// } /// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// Report prints the same output via `Display` and `Debug`, so it works well with +/// [`unwrap`]/[`expect`]: +/// +/// ```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 }) +/// # } +/// +/// get_super_error().map_err(Report::new).unwrap(); +/// ``` +/// +/// 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 +/// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { /// The error being reported. From 9be1cc9b6133fc8341ab605d426e675746144f29 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 16 Dec 2021 15:32:31 -0800 Subject: [PATCH 11/13] more docs improvements --- library/std/src/error.rs | 242 +++++++++++++++++++++++++++++++++------ 1 file changed, 207 insertions(+), 35 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index cb74a0084c66d..07f04aa2b911b 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -869,8 +869,10 @@ impl dyn Error + Send + Sync { /// Error: SuperError is here!: SuperErrorSideKick is here! /// ``` /// +/// ## Output consistency +/// /// Report prints the same output via `Display` and `Debug`, so it works well with -/// [`unwrap`]/[`expect`]: +/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`: /// /// ```should_panic /// #![feature(error_reporter)] @@ -912,6 +914,104 @@ 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 +// /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub struct Report { /// The error being reported. @@ -977,6 +1077,68 @@ where /// Caused by: /// SuperErrorSideKick is here! /// ``` + /// + /// When there are multiple source errors the causes will be numbered in order of iteration + /// starting from the outermost error. + /// + /// ```rust + /// #![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 { + /// # source: SuperErrorSideKickSideKick, + /// # } + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKick { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKickSideKick; + /// # impl fmt::Display for SuperErrorSideKickSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKickSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKickSideKick { } + /// + /// let source = SuperErrorSideKickSideKick; + /// let source = SuperErrorSideKick { source }; + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// 0: SuperErrorSideKick is here! + /// 1: SuperErrorSideKickSideKick is here! + /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub fn pretty(mut self, pretty: bool) -> Self { self.pretty = pretty; @@ -987,38 +1149,40 @@ where /// /// # Examples /// + /// **Note**: Report will search for the first `Backtrace` it can find starting from the + /// outermost error. In this example it will display the backtrace from the second error in the + /// chain, `SuperErrorSideKick`. + /// /// ```rust /// #![feature(error_reporter)] /// #![feature(backtrace)] - /// use std::error::{Error, Report}; + /// # use std::error::Error; + /// # use std::fmt; + /// use std::error::Report; /// use std::backtrace::Backtrace; - /// 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 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 { /// backtrace: Backtrace, /// } /// - /// impl fmt::Display for SuperErrorSideKick { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "SuperErrorSideKick is here!") + /// impl SuperErrorSideKick { + /// fn new() -> SuperErrorSideKick { + /// SuperErrorSideKick { backtrace: Backtrace::force_capture() } /// } /// } /// @@ -1028,7 +1192,14 @@ where /// } /// } /// - /// let source = SuperErrorSideKick { backtrace: Backtrace::force_capture() }; + /// // The rest of the example is unchanged ... + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// + /// let source = SuperErrorSideKick::new(); /// let error = SuperError { source }; /// let report = Report::new(error).pretty(true).show_backtrace(true); /// eprintln!("Error: {:?}", report); @@ -1043,17 +1214,18 @@ where /// SuperErrorSideKick is here! /// /// Stack backtrace: - /// 0: rust_out::main::_doctest_main_src_error_rs_943_0 - /// 1: rust_out::main - /// 2: core::ops::function::FnOnce::call_once - /// 3: std::sys_common::backtrace::__rust_begin_short_backtrace - /// 4: std::rt::lang_start::{{closure}} - /// 5: std::panicking::try - /// 6: std::rt::lang_start_internal - /// 7: std::rt::lang_start - /// 8: main - /// 9: __libc_start_main - /// 10: _start + /// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new + /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 + /// 2: rust_out::main + /// 3: core::ops::function::FnOnce::call_once + /// 4: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 5: std::rt::lang_start::{{closure}} + /// 6: std::panicking::try + /// 7: std::rt::lang_start_internal + /// 8: std::rt::lang_start + /// 9: main + /// 10: __libc_start_main + /// 11: _start /// ``` #[unstable(feature = "error_reporter", issue = "90172")] pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { From 5b3902fc6550f7646c4612c7ff8f4d8712f13334 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 16 Dec 2021 16:08:30 -0800 Subject: [PATCH 12/13] attempt to make Report usable with Box dyn Error and fn main --- library/std/src/error.rs | 304 ++++++++++++++++++++++++++------------- 1 file changed, 201 insertions(+), 103 deletions(-) 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) From 72cb1bd06dfdcec7c707e46fff44b3351a6c5ea9 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 7 Jan 2022 10:10:30 -0800 Subject: [PATCH 13/13] silence tidy errors --- library/std/src/error.rs | 14 ++++-------- library/std/src/error/tests.rs | 40 +++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 5514876c5d3b8..613ec43a906e7 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1284,9 +1284,7 @@ where for (ind, error) in cause.chain().enumerate() { writeln!(f)?; - let mut indented = Indented { - inner: f, - }; + let mut indented = Indented { inner: f }; if multiple { write!(indented, "{: >4}: {}", ind, error)?; } else { @@ -1310,8 +1308,7 @@ where } } -impl Report> -{ +impl Report> { fn backtrace(&self) -> Option<&Backtrace> { // have to grab the backtrace on the first error directly since that error may not be // 'static @@ -1353,9 +1350,7 @@ impl Report> for (ind, error) in cause.chain().enumerate() { writeln!(f)?; - let mut indented = Indented { - inner: f, - }; + let mut indented = Indented { inner: f }; if multiple { write!(indented, "{: >4}: {}", ind, error)?; } else { @@ -1411,8 +1406,7 @@ where } #[unstable(feature = "error_reporter", issue = "90172")] -impl fmt::Display for Report> -{ +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) } } diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 0835e282c46c3..eae5f43ff3cfb 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -82,11 +82,13 @@ fn multi_line_formatting() { let error = SuperError { source: SuperErrorSideKick }; let report = Report::new(&error).pretty(true); let actual = report.to_string(); - let expected = String::from("\ + let expected = String::from( + "\ SuperError is here! Caused by: - SuperErrorSideKick is here!"); + SuperErrorSideKick is here!", + ); assert_eq!(expected, actual); } @@ -112,20 +114,22 @@ fn error_with_no_sources_formats_multi_line_correctly() { #[test] fn error_with_backtrace_outputs_correctly_with_one_source() { let trace = Backtrace::force_capture(); - let expected = format!("\ + let expected = format!( + "\ The source of the error Caused by: Error with backtrace Stack backtrace: -{}", trace); +{}", + trace + ); let error = GenericError::new("Error with backtrace"); let mut error = GenericError::new_with_source("The source of the error", error); error.backtrace = Some(trace); let report = Report::new(error).pretty(true).show_backtrace(true); - println!("Error: {}", report); assert_eq!(expected.trim_end(), report.to_string()); } @@ -133,7 +137,8 @@ Stack backtrace: #[test] fn error_with_backtrace_outputs_correctly_with_two_sources() { let trace = Backtrace::force_capture(); - let expected = format!("\ + let expected = format!( + "\ Error with two sources Caused by: @@ -141,14 +146,15 @@ Caused by: 1: Error with backtrace Stack backtrace: -{}", trace); +{}", + trace + ); let mut error = GenericError::new("Error with backtrace"); error.backtrace = Some(trace); let error = GenericError::new_with_source("The source of the error", error); let error = GenericError::new_with_source("Error with two sources", error); let report = Report::new(error).pretty(true).show_backtrace(true); - println!("Error: {}", report); assert_eq!(expected.trim_end(), report.to_string()); } @@ -313,11 +319,11 @@ The message Caused by: - 0: - The message - - 1: - The message + 0: \ +\n The message + \ +\n 1: \ +\n The message "; let actual = report.to_string(); @@ -399,11 +405,11 @@ line 2 Caused by: 0: line 1 - - line 2 + \ +\n line 2 1: line 1 - - line 2"; + \ +\n line 2"; let actual = report.to_string(); assert_eq!(expected, actual);