Skip to content

Commit e045c79

Browse files
authoredJan 13, 2022
Rollup merge of #91938 - yaahc:error-reporter, r=m-ou-se
Add `std::error::Report` type This is a continuation of #90174, split into a separate PR since I cannot push to ```````@seanchen1991``````` 's fork
·
1.88.01.60.0
2 parents 3e7bc08 + 72cb1bd commit e045c79

File tree

2 files changed

+1048
-4
lines changed

2 files changed

+1048
-4
lines changed
 

‎library/std/src/error.rs

Lines changed: 643 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::backtrace::Backtrace;
2525
use crate::borrow::Cow;
2626
use crate::cell;
2727
use crate::char;
28-
use crate::fmt::{self, Debug, Display};
28+
use crate::fmt::{self, Debug, Display, Write};
2929
use crate::mem::transmute;
3030
use crate::num;
3131
use crate::str;
@@ -63,7 +63,7 @@ pub trait Error: Debug + Display {
6363
///
6464
/// #[derive(Debug)]
6565
/// struct SuperError {
66-
/// side: SuperErrorSideKick,
66+
/// source: SuperErrorSideKick,
6767
/// }
6868
///
6969
/// impl fmt::Display for SuperError {
@@ -74,7 +74,7 @@ pub trait Error: Debug + Display {
7474
///
7575
/// impl Error for SuperError {
7676
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
77-
/// Some(&self.side)
77+
/// Some(&self.source)
7878
/// }
7979
/// }
8080
///
@@ -90,7 +90,7 @@ pub trait Error: Debug + Display {
9090
/// impl Error for SuperErrorSideKick {}
9191
///
9292
/// fn get_super_error() -> Result<(), SuperError> {
93-
/// Err(SuperError { side: SuperErrorSideKick })
93+
/// Err(SuperError { source: SuperErrorSideKick })
9494
/// }
9595
///
9696
/// fn main() {
@@ -810,3 +810,642 @@ impl dyn Error + Send + Sync {
810810
})
811811
}
812812
}
813+
814+
/// An error reporter that print's an error and its sources.
815+
///
816+
/// Report also exposes configuration options for formatting the error chain, either entirely on a
817+
/// single line, or in multi-line format with each cause in the error chain on a new line.
818+
///
819+
/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the
820+
/// wrapped error be `Send`, `Sync`, or `'static`.
821+
///
822+
/// # Examples
823+
///
824+
/// ```rust
825+
/// #![feature(error_reporter)]
826+
/// use std::error::{Error, Report};
827+
/// use std::fmt;
828+
///
829+
/// #[derive(Debug)]
830+
/// struct SuperError {
831+
/// source: SuperErrorSideKick,
832+
/// }
833+
///
834+
/// impl fmt::Display for SuperError {
835+
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836+
/// write!(f, "SuperError is here!")
837+
/// }
838+
/// }
839+
///
840+
/// impl Error for SuperError {
841+
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
842+
/// Some(&self.source)
843+
/// }
844+
/// }
845+
///
846+
/// #[derive(Debug)]
847+
/// struct SuperErrorSideKick;
848+
///
849+
/// impl fmt::Display for SuperErrorSideKick {
850+
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
851+
/// write!(f, "SuperErrorSideKick is here!")
852+
/// }
853+
/// }
854+
///
855+
/// impl Error for SuperErrorSideKick {}
856+
///
857+
/// fn get_super_error() -> Result<(), SuperError> {
858+
/// Err(SuperError { source: SuperErrorSideKick })
859+
/// }
860+
///
861+
/// fn main() {
862+
/// match get_super_error() {
863+
/// Err(e) => println!("Error: {}", Report::new(e)),
864+
/// _ => println!("No error"),
865+
/// }
866+
/// }
867+
/// ```
868+
///
869+
/// This example produces the following output:
870+
///
871+
/// ```console
872+
/// Error: SuperError is here!: SuperErrorSideKick is here!
873+
/// ```
874+
///
875+
/// ## Output consistency
876+
///
877+
/// Report prints the same output via `Display` and `Debug`, so it works well with
878+
/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`:
879+
///
880+
/// ```should_panic
881+
/// #![feature(error_reporter)]
882+
/// use std::error::Report;
883+
/// # use std::error::Error;
884+
/// # use std::fmt;
885+
/// # #[derive(Debug)]
886+
/// # struct SuperError {
887+
/// # source: SuperErrorSideKick,
888+
/// # }
889+
/// # impl fmt::Display for SuperError {
890+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
891+
/// # write!(f, "SuperError is here!")
892+
/// # }
893+
/// # }
894+
/// # impl Error for SuperError {
895+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
896+
/// # Some(&self.source)
897+
/// # }
898+
/// # }
899+
/// # #[derive(Debug)]
900+
/// # struct SuperErrorSideKick;
901+
/// # impl fmt::Display for SuperErrorSideKick {
902+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
903+
/// # write!(f, "SuperErrorSideKick is here!")
904+
/// # }
905+
/// # }
906+
/// # impl Error for SuperErrorSideKick {}
907+
/// # fn get_super_error() -> Result<(), SuperError> {
908+
/// # Err(SuperError { source: SuperErrorSideKick })
909+
/// # }
910+
///
911+
/// get_super_error().map_err(Report::new).unwrap();
912+
/// ```
913+
///
914+
/// This example produces the following output:
915+
///
916+
/// ```console
917+
/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40
918+
/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
919+
/// ```
920+
///
921+
/// ## Return from `main`
922+
///
923+
/// `Report` also implements `From` for all types that implement [`Error`], this when combined with
924+
/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
925+
/// from `main`.
926+
///
927+
/// ```should_panic
928+
/// #![feature(error_reporter)]
929+
/// use std::error::Report;
930+
/// # use std::error::Error;
931+
/// # use std::fmt;
932+
/// # #[derive(Debug)]
933+
/// # struct SuperError {
934+
/// # source: SuperErrorSideKick,
935+
/// # }
936+
/// # impl fmt::Display for SuperError {
937+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938+
/// # write!(f, "SuperError is here!")
939+
/// # }
940+
/// # }
941+
/// # impl Error for SuperError {
942+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
943+
/// # Some(&self.source)
944+
/// # }
945+
/// # }
946+
/// # #[derive(Debug)]
947+
/// # struct SuperErrorSideKick;
948+
/// # impl fmt::Display for SuperErrorSideKick {
949+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
950+
/// # write!(f, "SuperErrorSideKick is here!")
951+
/// # }
952+
/// # }
953+
/// # impl Error for SuperErrorSideKick {}
954+
/// # fn get_super_error() -> Result<(), SuperError> {
955+
/// # Err(SuperError { source: SuperErrorSideKick })
956+
/// # }
957+
///
958+
/// fn main() -> Result<(), Report> {
959+
/// get_super_error()?;
960+
/// Ok(())
961+
/// }
962+
/// ```
963+
///
964+
/// This example produces the following output:
965+
///
966+
/// ```console
967+
/// Error: SuperError is here!: SuperErrorSideKick is here!
968+
/// ```
969+
///
970+
/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line
971+
/// output format, if you want to make sure your `Report`s are pretty printed and include backtrace
972+
/// you will need to manually convert and enable those flags.
973+
///
974+
/// ```should_panic
975+
/// #![feature(error_reporter)]
976+
/// use std::error::Report;
977+
/// # use std::error::Error;
978+
/// # use std::fmt;
979+
/// # #[derive(Debug)]
980+
/// # struct SuperError {
981+
/// # source: SuperErrorSideKick,
982+
/// # }
983+
/// # impl fmt::Display for SuperError {
984+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
985+
/// # write!(f, "SuperError is here!")
986+
/// # }
987+
/// # }
988+
/// # impl Error for SuperError {
989+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
990+
/// # Some(&self.source)
991+
/// # }
992+
/// # }
993+
/// # #[derive(Debug)]
994+
/// # struct SuperErrorSideKick;
995+
/// # impl fmt::Display for SuperErrorSideKick {
996+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
997+
/// # write!(f, "SuperErrorSideKick is here!")
998+
/// # }
999+
/// # }
1000+
/// # impl Error for SuperErrorSideKick {}
1001+
/// # fn get_super_error() -> Result<(), SuperError> {
1002+
/// # Err(SuperError { source: SuperErrorSideKick })
1003+
/// # }
1004+
///
1005+
/// fn main() -> Result<(), Report> {
1006+
/// get_super_error()
1007+
/// .map_err(Report::from)
1008+
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
1009+
/// Ok(())
1010+
/// }
1011+
/// ```
1012+
///
1013+
/// This example produces the following output:
1014+
///
1015+
/// ```console
1016+
/// Error: SuperError is here!
1017+
///
1018+
/// Caused by:
1019+
/// SuperErrorSideKick is here!
1020+
/// ```
1021+
#[unstable(feature = "error_reporter", issue = "90172")]
1022+
pub struct Report<E = Box<dyn Error>> {
1023+
/// The error being reported.
1024+
error: E,
1025+
/// Whether a backtrace should be included as part of the report.
1026+
show_backtrace: bool,
1027+
/// Whether the report should be pretty-printed.
1028+
pretty: bool,
1029+
}
1030+
1031+
impl<E> Report<E>
1032+
where
1033+
Report<E>: From<E>,
1034+
{
1035+
/// Create a new `Report` from an input error.
1036+
#[unstable(feature = "error_reporter", issue = "90172")]
1037+
pub fn new(error: E) -> Report<E> {
1038+
Self::from(error)
1039+
}
1040+
}
1041+
1042+
impl<E> Report<E> {
1043+
/// Enable pretty-printing the report across multiple lines.
1044+
///
1045+
/// # Examples
1046+
///
1047+
/// ```rust
1048+
/// #![feature(error_reporter)]
1049+
/// use std::error::Report;
1050+
/// # use std::error::Error;
1051+
/// # use std::fmt;
1052+
/// # #[derive(Debug)]
1053+
/// # struct SuperError {
1054+
/// # source: SuperErrorSideKick,
1055+
/// # }
1056+
/// # impl fmt::Display for SuperError {
1057+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1058+
/// # write!(f, "SuperError is here!")
1059+
/// # }
1060+
/// # }
1061+
/// # impl Error for SuperError {
1062+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
1063+
/// # Some(&self.source)
1064+
/// # }
1065+
/// # }
1066+
/// # #[derive(Debug)]
1067+
/// # struct SuperErrorSideKick;
1068+
/// # impl fmt::Display for SuperErrorSideKick {
1069+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1070+
/// # write!(f, "SuperErrorSideKick is here!")
1071+
/// # }
1072+
/// # }
1073+
/// # impl Error for SuperErrorSideKick {}
1074+
///
1075+
/// let error = SuperError { source: SuperErrorSideKick };
1076+
/// let report = Report::new(error).pretty(true);
1077+
/// eprintln!("Error: {:?}", report);
1078+
/// ```
1079+
///
1080+
/// This example produces the following output:
1081+
///
1082+
/// ```console
1083+
/// Error: SuperError is here!
1084+
///
1085+
/// Caused by:
1086+
/// SuperErrorSideKick is here!
1087+
/// ```
1088+
///
1089+
/// When there are multiple source errors the causes will be numbered in order of iteration
1090+
/// starting from the outermost error.
1091+
///
1092+
/// ```rust
1093+
/// #![feature(error_reporter)]
1094+
/// use std::error::Report;
1095+
/// # use std::error::Error;
1096+
/// # use std::fmt;
1097+
/// # #[derive(Debug)]
1098+
/// # struct SuperError {
1099+
/// # source: SuperErrorSideKick,
1100+
/// # }
1101+
/// # impl fmt::Display for SuperError {
1102+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1103+
/// # write!(f, "SuperError is here!")
1104+
/// # }
1105+
/// # }
1106+
/// # impl Error for SuperError {
1107+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
1108+
/// # Some(&self.source)
1109+
/// # }
1110+
/// # }
1111+
/// # #[derive(Debug)]
1112+
/// # struct SuperErrorSideKick {
1113+
/// # source: SuperErrorSideKickSideKick,
1114+
/// # }
1115+
/// # impl fmt::Display for SuperErrorSideKick {
1116+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1117+
/// # write!(f, "SuperErrorSideKick is here!")
1118+
/// # }
1119+
/// # }
1120+
/// # impl Error for SuperErrorSideKick {
1121+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
1122+
/// # Some(&self.source)
1123+
/// # }
1124+
/// # }
1125+
/// # #[derive(Debug)]
1126+
/// # struct SuperErrorSideKickSideKick;
1127+
/// # impl fmt::Display for SuperErrorSideKickSideKick {
1128+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1129+
/// # write!(f, "SuperErrorSideKickSideKick is here!")
1130+
/// # }
1131+
/// # }
1132+
/// # impl Error for SuperErrorSideKickSideKick { }
1133+
///
1134+
/// let source = SuperErrorSideKickSideKick;
1135+
/// let source = SuperErrorSideKick { source };
1136+
/// let error = SuperError { source };
1137+
/// let report = Report::new(error).pretty(true);
1138+
/// eprintln!("Error: {:?}", report);
1139+
/// ```
1140+
///
1141+
/// This example produces the following output:
1142+
///
1143+
/// ```console
1144+
/// Error: SuperError is here!
1145+
///
1146+
/// Caused by:
1147+
/// 0: SuperErrorSideKick is here!
1148+
/// 1: SuperErrorSideKickSideKick is here!
1149+
/// ```
1150+
#[unstable(feature = "error_reporter", issue = "90172")]
1151+
pub fn pretty(mut self, pretty: bool) -> Self {
1152+
self.pretty = pretty;
1153+
self
1154+
}
1155+
1156+
/// Display backtrace if available when using pretty output format.
1157+
///
1158+
/// # Examples
1159+
///
1160+
/// **Note**: Report will search for the first `Backtrace` it can find starting from the
1161+
/// outermost error. In this example it will display the backtrace from the second error in the
1162+
/// chain, `SuperErrorSideKick`.
1163+
///
1164+
/// ```rust
1165+
/// #![feature(error_reporter)]
1166+
/// #![feature(backtrace)]
1167+
/// # use std::error::Error;
1168+
/// # use std::fmt;
1169+
/// use std::error::Report;
1170+
/// use std::backtrace::Backtrace;
1171+
///
1172+
/// # #[derive(Debug)]
1173+
/// # struct SuperError {
1174+
/// # source: SuperErrorSideKick,
1175+
/// # }
1176+
/// # impl fmt::Display for SuperError {
1177+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1178+
/// # write!(f, "SuperError is here!")
1179+
/// # }
1180+
/// # }
1181+
/// # impl Error for SuperError {
1182+
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
1183+
/// # Some(&self.source)
1184+
/// # }
1185+
/// # }
1186+
/// #[derive(Debug)]
1187+
/// struct SuperErrorSideKick {
1188+
/// backtrace: Backtrace,
1189+
/// }
1190+
///
1191+
/// impl SuperErrorSideKick {
1192+
/// fn new() -> SuperErrorSideKick {
1193+
/// SuperErrorSideKick { backtrace: Backtrace::force_capture() }
1194+
/// }
1195+
/// }
1196+
///
1197+
/// impl Error for SuperErrorSideKick {
1198+
/// fn backtrace(&self) -> Option<&Backtrace> {
1199+
/// Some(&self.backtrace)
1200+
/// }
1201+
/// }
1202+
///
1203+
/// // The rest of the example is unchanged ...
1204+
/// # impl fmt::Display for SuperErrorSideKick {
1205+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1206+
/// # write!(f, "SuperErrorSideKick is here!")
1207+
/// # }
1208+
/// # }
1209+
///
1210+
/// let source = SuperErrorSideKick::new();
1211+
/// let error = SuperError { source };
1212+
/// let report = Report::new(error).pretty(true).show_backtrace(true);
1213+
/// eprintln!("Error: {:?}", report);
1214+
/// ```
1215+
///
1216+
/// This example produces something similar to the following output:
1217+
///
1218+
/// ```console
1219+
/// Error: SuperError is here!
1220+
///
1221+
/// Caused by:
1222+
/// SuperErrorSideKick is here!
1223+
///
1224+
/// Stack backtrace:
1225+
/// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
1226+
/// 1: rust_out::main::_doctest_main_src_error_rs_1158_0
1227+
/// 2: rust_out::main
1228+
/// 3: core::ops::function::FnOnce::call_once
1229+
/// 4: std::sys_common::backtrace::__rust_begin_short_backtrace
1230+
/// 5: std::rt::lang_start::{{closure}}
1231+
/// 6: std::panicking::try
1232+
/// 7: std::rt::lang_start_internal
1233+
/// 8: std::rt::lang_start
1234+
/// 9: main
1235+
/// 10: __libc_start_main
1236+
/// 11: _start
1237+
/// ```
1238+
#[unstable(feature = "error_reporter", issue = "90172")]
1239+
pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
1240+
self.show_backtrace = show_backtrace;
1241+
self
1242+
}
1243+
}
1244+
1245+
impl<E> Report<E>
1246+
where
1247+
E: Error,
1248+
{
1249+
fn backtrace(&self) -> Option<&Backtrace> {
1250+
// have to grab the backtrace on the first error directly since that error may not be
1251+
// 'static
1252+
let backtrace = self.error.backtrace();
1253+
let backtrace = backtrace.or_else(|| {
1254+
self.error
1255+
.source()
1256+
.map(|source| source.chain().find_map(|source| source.backtrace()))
1257+
.flatten()
1258+
});
1259+
backtrace
1260+
}
1261+
1262+
/// Format the report as a single line.
1263+
#[unstable(feature = "error_reporter", issue = "90172")]
1264+
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1265+
write!(f, "{}", self.error)?;
1266+
1267+
let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
1268+
1269+
for cause in sources {
1270+
write!(f, ": {}", cause)?;
1271+
}
1272+
1273+
Ok(())
1274+
}
1275+
1276+
/// Format the report as multiple lines, with each error cause on its own line.
1277+
#[unstable(feature = "error_reporter", issue = "90172")]
1278+
fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279+
let error = &self.error;
1280+
1281+
write!(f, "{}", error)?;
1282+
1283+
if let Some(cause) = error.source() {
1284+
write!(f, "\n\nCaused by:")?;
1285+
1286+
let multiple = cause.source().is_some();
1287+
1288+
for (ind, error) in cause.chain().enumerate() {
1289+
writeln!(f)?;
1290+
let mut indented = Indented { inner: f };
1291+
if multiple {
1292+
write!(indented, "{: >4}: {}", ind, error)?;
1293+
} else {
1294+
write!(indented, " {}", error)?;
1295+
}
1296+
}
1297+
}
1298+
1299+
if self.show_backtrace {
1300+
let backtrace = self.backtrace();
1301+
1302+
if let Some(backtrace) = backtrace {
1303+
let backtrace = backtrace.to_string();
1304+
1305+
f.write_str("\n\nStack backtrace:\n")?;
1306+
f.write_str(backtrace.trim_end())?;
1307+
}
1308+
}
1309+
1310+
Ok(())
1311+
}
1312+
}
1313+
1314+
impl Report<Box<dyn Error>> {
1315+
fn backtrace(&self) -> Option<&Backtrace> {
1316+
// have to grab the backtrace on the first error directly since that error may not be
1317+
// 'static
1318+
let backtrace = self.error.backtrace();
1319+
let backtrace = backtrace.or_else(|| {
1320+
self.error
1321+
.source()
1322+
.map(|source| source.chain().find_map(|source| source.backtrace()))
1323+
.flatten()
1324+
});
1325+
backtrace
1326+
}
1327+
1328+
/// Format the report as a single line.
1329+
#[unstable(feature = "error_reporter", issue = "90172")]
1330+
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1331+
write!(f, "{}", self.error)?;
1332+
1333+
let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
1334+
1335+
for cause in sources {
1336+
write!(f, ": {}", cause)?;
1337+
}
1338+
1339+
Ok(())
1340+
}
1341+
1342+
/// Format the report as multiple lines, with each error cause on its own line.
1343+
#[unstable(feature = "error_reporter", issue = "90172")]
1344+
fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1345+
let error = &self.error;
1346+
1347+
write!(f, "{}", error)?;
1348+
1349+
if let Some(cause) = error.source() {
1350+
write!(f, "\n\nCaused by:")?;
1351+
1352+
let multiple = cause.source().is_some();
1353+
1354+
for (ind, error) in cause.chain().enumerate() {
1355+
writeln!(f)?;
1356+
let mut indented = Indented { inner: f };
1357+
if multiple {
1358+
write!(indented, "{: >4}: {}", ind, error)?;
1359+
} else {
1360+
write!(indented, " {}", error)?;
1361+
}
1362+
}
1363+
}
1364+
1365+
if self.show_backtrace {
1366+
let backtrace = self.backtrace();
1367+
1368+
if let Some(backtrace) = backtrace {
1369+
let backtrace = backtrace.to_string();
1370+
1371+
f.write_str("\n\nStack backtrace:\n")?;
1372+
f.write_str(backtrace.trim_end())?;
1373+
}
1374+
}
1375+
1376+
Ok(())
1377+
}
1378+
}
1379+
1380+
#[unstable(feature = "error_reporter", issue = "90172")]
1381+
impl<E> From<E> for Report<E>
1382+
where
1383+
E: Error,
1384+
{
1385+
fn from(error: E) -> Self {
1386+
Report { error, show_backtrace: false, pretty: false }
1387+
}
1388+
}
1389+
1390+
#[unstable(feature = "error_reporter", issue = "90172")]
1391+
impl<'a, E> From<E> for Report<Box<dyn Error + 'a>>
1392+
where
1393+
E: Error + 'a,
1394+
{
1395+
fn from(error: E) -> Self {
1396+
let error = box error;
1397+
Report { error, show_backtrace: false, pretty: false }
1398+
}
1399+
}
1400+
1401+
#[unstable(feature = "error_reporter", issue = "90172")]
1402+
impl<E> fmt::Display for Report<E>
1403+
where
1404+
E: Error,
1405+
{
1406+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1407+
if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
1408+
}
1409+
}
1410+
1411+
#[unstable(feature = "error_reporter", issue = "90172")]
1412+
impl fmt::Display for Report<Box<dyn Error>> {
1413+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1414+
if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
1415+
}
1416+
}
1417+
1418+
// This type intentionally outputs the same format for `Display` and `Debug`for
1419+
// situations where you unwrap a `Report` or return it from main.
1420+
#[unstable(feature = "error_reporter", issue = "90172")]
1421+
impl<E> fmt::Debug for Report<E>
1422+
where
1423+
Report<E>: fmt::Display,
1424+
{
1425+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1426+
fmt::Display::fmt(self, f)
1427+
}
1428+
}
1429+
1430+
/// Wrapper type for indenting the inner source.
1431+
struct Indented<'a, D> {
1432+
inner: &'a mut D,
1433+
}
1434+
1435+
impl<T> Write for Indented<'_, T>
1436+
where
1437+
T: Write,
1438+
{
1439+
fn write_str(&mut self, s: &str) -> fmt::Result {
1440+
for (i, line) in s.split('\n').enumerate() {
1441+
if i > 0 {
1442+
self.inner.write_char('\n')?;
1443+
self.inner.write_str(" ")?;
1444+
}
1445+
1446+
self.inner.write_str(line)?;
1447+
}
1448+
1449+
Ok(())
1450+
}
1451+
}

‎library/std/src/error/tests.rs

Lines changed: 405 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,408 @@ fn downcasting() {
3535
Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A),
3636
}
3737
}
38+
39+
use crate::backtrace::Backtrace;
40+
use crate::error::Report;
41+
42+
#[derive(Debug)]
43+
struct SuperError {
44+
source: SuperErrorSideKick,
45+
}
46+
47+
impl fmt::Display for SuperError {
48+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49+
write!(f, "SuperError is here!")
50+
}
51+
}
52+
53+
impl Error for SuperError {
54+
fn source(&self) -> Option<&(dyn Error + 'static)> {
55+
Some(&self.source)
56+
}
57+
}
58+
59+
#[derive(Debug)]
60+
struct SuperErrorSideKick;
61+
62+
impl fmt::Display for SuperErrorSideKick {
63+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64+
write!(f, "SuperErrorSideKick is here!")
65+
}
66+
}
67+
68+
impl Error for SuperErrorSideKick {}
69+
70+
#[test]
71+
fn single_line_formatting() {
72+
let error = SuperError { source: SuperErrorSideKick };
73+
let report = Report::new(&error);
74+
let actual = report.to_string();
75+
let expected = String::from("SuperError is here!: SuperErrorSideKick is here!");
76+
77+
assert_eq!(expected, actual);
78+
}
79+
80+
#[test]
81+
fn multi_line_formatting() {
82+
let error = SuperError { source: SuperErrorSideKick };
83+
let report = Report::new(&error).pretty(true);
84+
let actual = report.to_string();
85+
let expected = String::from(
86+
"\
87+
SuperError is here!
88+
89+
Caused by:
90+
SuperErrorSideKick is here!",
91+
);
92+
93+
assert_eq!(expected, actual);
94+
}
95+
96+
#[test]
97+
fn error_with_no_sources_formats_single_line_correctly() {
98+
let report = Report::new(SuperErrorSideKick);
99+
let actual = report.to_string();
100+
let expected = String::from("SuperErrorSideKick is here!");
101+
102+
assert_eq!(expected, actual);
103+
}
104+
105+
#[test]
106+
fn error_with_no_sources_formats_multi_line_correctly() {
107+
let report = Report::new(SuperErrorSideKick).pretty(true);
108+
let actual = report.to_string();
109+
let expected = String::from("SuperErrorSideKick is here!");
110+
111+
assert_eq!(expected, actual);
112+
}
113+
114+
#[test]
115+
fn error_with_backtrace_outputs_correctly_with_one_source() {
116+
let trace = Backtrace::force_capture();
117+
let expected = format!(
118+
"\
119+
The source of the error
120+
121+
Caused by:
122+
Error with backtrace
123+
124+
Stack backtrace:
125+
{}",
126+
trace
127+
);
128+
let error = GenericError::new("Error with backtrace");
129+
let mut error = GenericError::new_with_source("The source of the error", error);
130+
error.backtrace = Some(trace);
131+
let report = Report::new(error).pretty(true).show_backtrace(true);
132+
133+
println!("Error: {}", report);
134+
assert_eq!(expected.trim_end(), report.to_string());
135+
}
136+
137+
#[test]
138+
fn error_with_backtrace_outputs_correctly_with_two_sources() {
139+
let trace = Backtrace::force_capture();
140+
let expected = format!(
141+
"\
142+
Error with two sources
143+
144+
Caused by:
145+
0: The source of the error
146+
1: Error with backtrace
147+
148+
Stack backtrace:
149+
{}",
150+
trace
151+
);
152+
let mut error = GenericError::new("Error with backtrace");
153+
error.backtrace = Some(trace);
154+
let error = GenericError::new_with_source("The source of the error", error);
155+
let error = GenericError::new_with_source("Error with two sources", error);
156+
let report = Report::new(error).pretty(true).show_backtrace(true);
157+
158+
println!("Error: {}", report);
159+
assert_eq!(expected.trim_end(), report.to_string());
160+
}
161+
162+
#[derive(Debug)]
163+
struct GenericError<D> {
164+
message: D,
165+
backtrace: Option<Backtrace>,
166+
source: Option<Box<dyn Error + 'static>>,
167+
}
168+
169+
impl<D> GenericError<D> {
170+
fn new(message: D) -> GenericError<D> {
171+
Self { message, backtrace: None, source: None }
172+
}
173+
174+
fn new_with_source<E>(message: D, source: E) -> GenericError<D>
175+
where
176+
E: Error + 'static,
177+
{
178+
let source: Box<dyn Error + 'static> = Box::new(source);
179+
let source = Some(source);
180+
GenericError { message, backtrace: None, source }
181+
}
182+
}
183+
184+
impl<D> fmt::Display for GenericError<D>
185+
where
186+
D: fmt::Display,
187+
{
188+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189+
fmt::Display::fmt(&self.message, f)
190+
}
191+
}
192+
193+
impl<D> Error for GenericError<D>
194+
where
195+
D: fmt::Debug + fmt::Display,
196+
{
197+
fn source(&self) -> Option<&(dyn Error + 'static)> {
198+
self.source.as_deref()
199+
}
200+
201+
fn backtrace(&self) -> Option<&Backtrace> {
202+
self.backtrace.as_ref()
203+
}
204+
}
205+
206+
#[test]
207+
fn error_formats_single_line_with_rude_display_impl() {
208+
#[derive(Debug)]
209+
struct MyMessage;
210+
211+
impl fmt::Display for MyMessage {
212+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213+
f.write_str("line 1\nline 2")?;
214+
f.write_str("\nline 3\nline 4\n")?;
215+
f.write_str("line 5\nline 6")?;
216+
Ok(())
217+
}
218+
}
219+
220+
let error = GenericError::new(MyMessage);
221+
let error = GenericError::new_with_source(MyMessage, error);
222+
let error = GenericError::new_with_source(MyMessage, error);
223+
let error = GenericError::new_with_source(MyMessage, error);
224+
let report = Report::new(error);
225+
let expected = "\
226+
line 1
227+
line 2
228+
line 3
229+
line 4
230+
line 5
231+
line 6: line 1
232+
line 2
233+
line 3
234+
line 4
235+
line 5
236+
line 6: line 1
237+
line 2
238+
line 3
239+
line 4
240+
line 5
241+
line 6: line 1
242+
line 2
243+
line 3
244+
line 4
245+
line 5
246+
line 6";
247+
248+
let actual = report.to_string();
249+
assert_eq!(expected, actual);
250+
}
251+
252+
#[test]
253+
fn error_formats_multi_line_with_rude_display_impl() {
254+
#[derive(Debug)]
255+
struct MyMessage;
256+
257+
impl fmt::Display for MyMessage {
258+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259+
f.write_str("line 1\nline 2")?;
260+
f.write_str("\nline 3\nline 4\n")?;
261+
f.write_str("line 5\nline 6")?;
262+
Ok(())
263+
}
264+
}
265+
266+
let error = GenericError::new(MyMessage);
267+
let error = GenericError::new_with_source(MyMessage, error);
268+
let error = GenericError::new_with_source(MyMessage, error);
269+
let error = GenericError::new_with_source(MyMessage, error);
270+
let report = Report::new(error).pretty(true);
271+
let expected = "line 1
272+
line 2
273+
line 3
274+
line 4
275+
line 5
276+
line 6
277+
278+
Caused by:
279+
0: line 1
280+
line 2
281+
line 3
282+
line 4
283+
line 5
284+
line 6
285+
1: line 1
286+
line 2
287+
line 3
288+
line 4
289+
line 5
290+
line 6
291+
2: line 1
292+
line 2
293+
line 3
294+
line 4
295+
line 5
296+
line 6";
297+
298+
let actual = report.to_string();
299+
assert_eq!(expected, actual);
300+
}
301+
302+
#[test]
303+
fn errors_that_start_with_newline_formats_correctly() {
304+
#[derive(Debug)]
305+
struct MyMessage;
306+
307+
impl fmt::Display for MyMessage {
308+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309+
f.write_str("\nThe message\n")
310+
}
311+
}
312+
313+
let error = GenericError::new(MyMessage);
314+
let error = GenericError::new_with_source(MyMessage, error);
315+
let error = GenericError::new_with_source(MyMessage, error);
316+
let report = Report::new(error).pretty(true);
317+
let expected = "
318+
The message
319+
320+
321+
Caused by:
322+
0: \
323+
\n The message
324+
\
325+
\n 1: \
326+
\n The message
327+
";
328+
329+
let actual = report.to_string();
330+
assert_eq!(expected, actual);
331+
}
332+
333+
#[test]
334+
fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() {
335+
#[derive(Debug)]
336+
struct MyMessage;
337+
338+
impl fmt::Display for MyMessage {
339+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340+
f.write_str("The message")?;
341+
f.write_str(" goes on")?;
342+
f.write_str(" and on.")
343+
}
344+
}
345+
346+
let error = GenericError::new(MyMessage);
347+
let error = GenericError::new_with_source(MyMessage, error);
348+
let error = GenericError::new_with_source(MyMessage, error);
349+
let report = Report::new(error).pretty(true);
350+
let expected = "\
351+
The message goes on and on.
352+
353+
Caused by:
354+
0: The message goes on and on.
355+
1: The message goes on and on.";
356+
357+
let actual = report.to_string();
358+
println!("{}", actual);
359+
assert_eq!(expected, actual);
360+
}
361+
362+
#[test]
363+
fn errors_with_string_interpolation_formats_correctly() {
364+
#[derive(Debug)]
365+
struct MyMessage(usize);
366+
367+
impl fmt::Display for MyMessage {
368+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369+
write!(f, "Got an error code: ({}). ", self.0)?;
370+
write!(f, "What would you like to do in response?")
371+
}
372+
}
373+
374+
let error = GenericError::new(MyMessage(10));
375+
let error = GenericError::new_with_source(MyMessage(20), error);
376+
let report = Report::new(error).pretty(true);
377+
let expected = "\
378+
Got an error code: (20). What would you like to do in response?
379+
380+
Caused by:
381+
Got an error code: (10). What would you like to do in response?";
382+
let actual = report.to_string();
383+
assert_eq!(expected, actual);
384+
}
385+
386+
#[test]
387+
fn empty_lines_mid_message() {
388+
#[derive(Debug)]
389+
struct MyMessage;
390+
391+
impl fmt::Display for MyMessage {
392+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393+
f.write_str("line 1\n\nline 2")
394+
}
395+
}
396+
397+
let error = GenericError::new(MyMessage);
398+
let error = GenericError::new_with_source(MyMessage, error);
399+
let error = GenericError::new_with_source(MyMessage, error);
400+
let report = Report::new(error).pretty(true);
401+
let expected = "\
402+
line 1
403+
404+
line 2
405+
406+
Caused by:
407+
0: line 1
408+
\
409+
\n line 2
410+
1: line 1
411+
\
412+
\n line 2";
413+
414+
let actual = report.to_string();
415+
assert_eq!(expected, actual);
416+
}
417+
418+
#[test]
419+
fn only_one_source() {
420+
#[derive(Debug)]
421+
struct MyMessage;
422+
423+
impl fmt::Display for MyMessage {
424+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425+
f.write_str("line 1\nline 2")
426+
}
427+
}
428+
429+
let error = GenericError::new(MyMessage);
430+
let error = GenericError::new_with_source(MyMessage, error);
431+
let report = Report::new(error).pretty(true);
432+
let expected = "\
433+
line 1
434+
line 2
435+
436+
Caused by:
437+
line 1
438+
line 2";
439+
440+
let actual = report.to_string();
441+
assert_eq!(expected, actual);
442+
}

0 commit comments

Comments
 (0)
Please sign in to comment.