Update std::error::Report based on feedback
This commit is contained in:
parent
32bcb8113f
commit
1386a15529
2 changed files with 171 additions and 81 deletions
|
@ -25,7 +25,7 @@ use crate::backtrace::Backtrace;
|
||||||
use crate::borrow::Cow;
|
use crate::borrow::Cow;
|
||||||
use crate::cell;
|
use crate::cell;
|
||||||
use crate::char;
|
use crate::char;
|
||||||
use crate::fmt::{self, Debug, Display};
|
use crate::fmt::{self, Debug, Display, Write};
|
||||||
use crate::mem::transmute;
|
use crate::mem::transmute;
|
||||||
use crate::num;
|
use crate::num;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
|
@ -63,7 +63,7 @@ pub trait Error: Debug + Display {
|
||||||
///
|
///
|
||||||
/// #[derive(Debug)]
|
/// #[derive(Debug)]
|
||||||
/// struct SuperError {
|
/// struct SuperError {
|
||||||
/// side: SuperErrorSideKick,
|
/// source: SuperErrorSideKick,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl fmt::Display for SuperError {
|
/// impl fmt::Display for SuperError {
|
||||||
|
@ -74,7 +74,7 @@ pub trait Error: Debug + Display {
|
||||||
///
|
///
|
||||||
/// impl Error for SuperError {
|
/// impl Error for SuperError {
|
||||||
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
|
/// 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 {}
|
/// impl Error for SuperErrorSideKick {}
|
||||||
///
|
///
|
||||||
/// fn get_super_error() -> Result<(), SuperError> {
|
/// fn get_super_error() -> Result<(), SuperError> {
|
||||||
/// Err(SuperError { side: SuperErrorSideKick })
|
/// Err(SuperError { source: SuperErrorSideKick })
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
|
@ -836,10 +836,6 @@ impl dyn Error + Send + Sync {
|
||||||
///
|
///
|
||||||
/// impl<'a> Error for SuperError<'a> {}
|
/// 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() {
|
/// fn main() {
|
||||||
/// let msg = String::from("Huzzah!");
|
/// let msg = String::from("Huzzah!");
|
||||||
/// let error = SuperError { side: &msg };
|
/// let error = SuperError { side: &msg };
|
||||||
|
@ -883,6 +879,19 @@ where
|
||||||
self
|
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.
|
/// Format the report as a single line.
|
||||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||||
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
@ -911,17 +920,17 @@ where
|
||||||
|
|
||||||
for (ind, error) in cause.chain().enumerate() {
|
for (ind, error) in cause.chain().enumerate() {
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
|
let mut indented = Indented {
|
||||||
if multiple {
|
inner: f,
|
||||||
write!(f, "{: >4}: {}", ind, Indented { source: error })?;
|
number: if multiple { Some(ind) } else { None },
|
||||||
} else {
|
started: false,
|
||||||
write!(f, " {}", error)?;
|
};
|
||||||
}
|
write!(indented, "{}", error)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.show_backtrace {
|
if self.show_backtrace {
|
||||||
let backtrace = error.backtrace();
|
let backtrace = self.backtrace();
|
||||||
|
|
||||||
if let Some(backtrace) = backtrace {
|
if let Some(backtrace) = backtrace {
|
||||||
let backtrace = backtrace.to_string();
|
let backtrace = backtrace.to_string();
|
||||||
|
@ -968,23 +977,34 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper type for indenting the inner source.
|
/// Wrapper type for indenting the inner source.
|
||||||
struct Indented<D> {
|
struct Indented<'a, D> {
|
||||||
source: D,
|
inner: &'a mut D,
|
||||||
|
number: Option<usize>,
|
||||||
|
started: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> fmt::Display for Indented<D>
|
impl<T> Write for Indented<'_, T>
|
||||||
where
|
where
|
||||||
D: fmt::Display,
|
T: Write,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
let source = self.source.to_string();
|
for (i, line) in s.split('\n').enumerate() {
|
||||||
|
if !self.started {
|
||||||
for (ind, line) in source.trim().split('\n').filter(|l| !l.is_empty()).enumerate() {
|
self.started = true;
|
||||||
if ind > 0 {
|
match self.number {
|
||||||
write!(f, "\n {}", line)?;
|
Some(number) => write!(self.inner, "{: >5}: ", number)?,
|
||||||
} else {
|
None => self.inner.write_str(" ")?,
|
||||||
write!(f, "{}", line)?;
|
|
||||||
}
|
}
|
||||||
|
} 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(())
|
Ok(())
|
||||||
|
|
|
@ -36,13 +36,12 @@ fn downcasting() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::backtrace;
|
use crate::backtrace::Backtrace;
|
||||||
use crate::env;
|
|
||||||
use crate::error::Report;
|
use crate::error::Report;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SuperError {
|
struct SuperError {
|
||||||
side: SuperErrorSideKick,
|
source: SuperErrorSideKick,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SuperError {
|
impl fmt::Display for SuperError {
|
||||||
|
@ -53,7 +52,7 @@ impl fmt::Display for SuperError {
|
||||||
|
|
||||||
impl Error for SuperError {
|
impl Error for SuperError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
Some(&self.side)
|
Some(&self.source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ impl Error for SuperErrorSideKick {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_line_formatting() {
|
fn single_line_formatting() {
|
||||||
let error = SuperError { side: SuperErrorSideKick };
|
let error = SuperError { source: SuperErrorSideKick };
|
||||||
let report = Report::new(&error);
|
let report = Report::new(&error);
|
||||||
let actual = report.to_string();
|
let actual = report.to_string();
|
||||||
let expected = String::from("SuperError is here!: SuperErrorSideKick is here!");
|
let expected = String::from("SuperError is here!: SuperErrorSideKick is here!");
|
||||||
|
@ -80,7 +79,7 @@ fn single_line_formatting() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_line_formatting() {
|
fn multi_line_formatting() {
|
||||||
let error = SuperError { side: SuperErrorSideKick };
|
let error = SuperError { source: SuperErrorSideKick };
|
||||||
let report = Report::new(&error).pretty(true);
|
let report = Report::new(&error).pretty(true);
|
||||||
let actual = report.to_string();
|
let actual = report.to_string();
|
||||||
let expected =
|
let expected =
|
||||||
|
@ -108,50 +107,57 @@ fn error_with_no_sources_formats_multi_line_correctly() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_with_backtrace_outputs_correctly() {
|
fn error_with_backtrace_outputs_correctly_with_one_source() {
|
||||||
use backtrace::Backtrace;
|
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)]
|
Stack backtrace:
|
||||||
struct ErrorWithBacktrace<'a> {
|
{}", trace);
|
||||||
msg: &'a str,
|
let error = GenericError::new("Error with backtrace");
|
||||||
trace: 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> {
|
println!("Error: {}", report);
|
||||||
fn backtrace(&self) -> Option<&Backtrace> {
|
assert_eq!(expected.trim_end(), report.to_string());
|
||||||
Some(&self.trace)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = String::from("The source of the error");
|
#[test]
|
||||||
let report = Report::new(ErrorWithBacktrace { msg: &msg, trace: Backtrace::capture() })
|
fn error_with_backtrace_outputs_correctly_with_two_sources() {
|
||||||
.pretty(true)
|
let trace = Backtrace::force_capture();
|
||||||
.show_backtrace(true);
|
let expected = format!("Error with two sources
|
||||||
|
|
||||||
let expected = String::from(
|
Caused by:
|
||||||
"Error with backtrace: The source of the error\n\nStack backtrace:\ndisabled backtrace",
|
0: The source of the error
|
||||||
);
|
1: Error with backtrace
|
||||||
|
|
||||||
assert_eq!(expected, report.to_string());
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
println!("Error: {}", report);
|
||||||
|
assert_eq!(expected.trim_end(), report.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct GenericError<D> {
|
struct GenericError<D> {
|
||||||
message: D,
|
message: D,
|
||||||
|
backtrace: Option<Backtrace>,
|
||||||
source: Option<Box<dyn Error + 'static>>,
|
source: Option<Box<dyn Error + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> GenericError<D> {
|
impl<D> GenericError<D> {
|
||||||
fn new(message: D) -> GenericError<D> {
|
fn new(message: D) -> GenericError<D> {
|
||||||
Self { message, source: None }
|
Self { message, backtrace: None, source: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_source<E>(message: D, source: E) -> GenericError<D>
|
fn new_with_source<E>(message: D, source: E) -> GenericError<D>
|
||||||
|
@ -160,7 +166,7 @@ impl<D> GenericError<D> {
|
||||||
{
|
{
|
||||||
let source: Box<dyn Error + 'static> = Box::new(source);
|
let source: Box<dyn Error + 'static> = Box::new(source);
|
||||||
let source = Some(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)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
self.source.as_deref()
|
self.source.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn backtrace(&self) -> Option<&Backtrace> {
|
||||||
|
self.backtrace.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -297,8 +307,12 @@ The message
|
||||||
|
|
||||||
|
|
||||||
Caused by:
|
Caused by:
|
||||||
0: The message
|
0:
|
||||||
1: The message"#;
|
The message
|
||||||
|
|
||||||
|
1:
|
||||||
|
The message
|
||||||
|
"#;
|
||||||
|
|
||||||
let actual = report.to_string();
|
let actual = report.to_string();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
|
@ -326,3 +340,59 @@ Caused by:
|
||||||
let actual = report.to_string();
|
let actual = report.to_string();
|
||||||
assert_eq!(expected, actual);
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue