From c0f14cb9301eacb51fc660f1d461cbc783f4aaa7 Mon Sep 17 00:00:00 2001 From: Sean Chen Date: Wed, 27 Oct 2021 13:03:53 -0500 Subject: [PATCH] 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 52f3ebbdee4..d8859cf1e55 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(); + let backtrace = backtrace.to_string(); - write!(f, "\n\n")?; - writeln!(f, "Stack backtrace:")?; - - 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, +/// Wrapper type for indenting the inner source. +struct Indented { + source: D, } -/// 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> +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() { + 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 { - self.inner.write_char('\n')?; - self.needs_indent = true; + write!(f, "\n {}", line)?; + } else { + write!(f, "{}", line)?; } - - 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, " ") - } - } - } - } -} diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 66d6924f34d..c408915ca71 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); +}