diff options
| -rw-r--r-- | library/std/src/error.rs | 195 | ||||
| -rw-r--r-- | 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 10de248c3d7..a2b4eb2117d 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<E> { /// 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<usize>, - started: bool, } impl<T> 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 82ef39ae90f..0835e282c46 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); |
