Skip to content

Commit aefe323

Browse files
authoredMay 18, 2023
feat(nested): Render inner diagnostics (#170)
1 parent 46adb3b commit aefe323

File tree

3 files changed

+155
-10
lines changed

3 files changed

+155
-10
lines changed
 

‎src/handlers/graphical.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt::{self, Write};
33
use owo_colors::{OwoColorize, Style};
44
use unicode_width::UnicodeWidthChar;
55

6-
use crate::diagnostic_chain::DiagnosticChain;
6+
use crate::diagnostic_chain::{DiagnosticChain, ErrorKind};
77
use crate::handlers::theme::*;
88
use crate::protocol::{Diagnostic, Severity};
99
use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents};
@@ -151,7 +151,6 @@ impl GraphicalReportHandler {
151151
diagnostic: &(dyn Diagnostic),
152152
) -> fmt::Result {
153153
self.render_header(f, diagnostic)?;
154-
writeln!(f)?;
155154
self.render_causes(f, diagnostic)?;
156155
let src = diagnostic.source_code();
157156
self.render_snippets(f, diagnostic, src)?;
@@ -190,13 +189,15 @@ impl GraphicalReportHandler {
190189
);
191190
write!(header, "{}", link)?;
192191
writeln!(f, "{}", header)?;
192+
writeln!(f)?;
193193
} else if let Some(code) = diagnostic.code() {
194194
write!(header, "{}", code.style(severity_style),)?;
195195
if self.links == LinkStyle::Text && diagnostic.url().is_some() {
196196
let url = diagnostic.url().unwrap(); // safe
197197
write!(header, " ({})", url.style(self.theme.styles.link))?;
198198
}
199199
writeln!(f, "{}", header)?;
200+
writeln!(f)?;
200201
}
201202
Ok(())
202203
}
@@ -253,7 +254,22 @@ impl GraphicalReportHandler {
253254
let opts = textwrap::Options::new(width)
254255
.initial_indent(&initial_indent)
255256
.subsequent_indent(&rest_indent);
256-
writeln!(f, "{}", textwrap::fill(&error.to_string(), opts))?;
257+
match error {
258+
ErrorKind::Diagnostic(diag) => {
259+
let mut inner = String::new();
260+
261+
// Don't print footer for inner errors
262+
let mut inner_renderer = self.clone();
263+
inner_renderer.footer = None;
264+
inner_renderer.with_cause_chain = false;
265+
inner_renderer.render_report(&mut inner, diag)?;
266+
267+
writeln!(f, "{}", textwrap::fill(&inner, opts))?;
268+
}
269+
ErrorKind::StdError(err) => {
270+
writeln!(f, "{}", textwrap::fill(&err.to_string(), opts))?;
271+
}
272+
}
257273
}
258274
}
259275

@@ -287,7 +303,6 @@ impl GraphicalReportHandler {
287303
Some(Severity::Advice) => write!(f, "Advice: ")?,
288304
};
289305
self.render_header(f, rel)?;
290-
writeln!(f)?;
291306
self.render_causes(f, rel)?;
292307
let src = rel.source_code().or(parent_src);
293308
self.render_snippets(f, rel, src)?;

‎tests/graphical.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ fn single_line_highlight_span_full_line() {
8585
let out = fmt_report(err.into());
8686
println!("Error: {}", out);
8787

88-
let expected = r#"
89-
× oops!
88+
let expected = r#" × oops!
9089
╭─[issue:1:1]
9190
1 │ source
9291
2 │ text
@@ -1201,8 +1200,7 @@ fn zero_length_eol_span() {
12011200
let out = fmt_report(err.into());
12021201
println!("Error: {}", out);
12031202

1204-
let expected = r#"
1205-
× oops!
1203+
let expected = r#" × oops!
12061204
╭─[issue:1:1]
12071205
1 │ this is the first line
12081206
2 │ this is the second line

‎tests/test_diagnostic_source_macro.rs

+134-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
use miette::Diagnostic;
22

3+
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
4+
#[error("A complex error happened")]
5+
struct SourceError {
6+
#[source_code]
7+
code: String,
8+
#[help]
9+
help: String,
10+
#[label("here")]
11+
label: (usize, usize),
12+
}
13+
314
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
415
#[error("AnErr")]
516
struct AnErr;
@@ -8,7 +19,7 @@ struct AnErr;
819
#[error("TestError")]
920
struct TestStructError {
1021
#[diagnostic_source]
11-
asdf_inner_foo: AnErr,
22+
asdf_inner_foo: SourceError,
1223
}
1324

1425
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
@@ -37,7 +48,11 @@ struct TestArcedError(#[diagnostic_source] std::sync::Arc<dyn Diagnostic>);
3748
#[test]
3849
fn test_diagnostic_source() {
3950
let error = TestStructError {
40-
asdf_inner_foo: AnErr,
51+
asdf_inner_foo: SourceError {
52+
code: String::new(),
53+
help: String::new(),
54+
label: (0, 0),
55+
},
4156
};
4257
assert!(error.diagnostic_source().is_some());
4358

@@ -59,3 +74,120 @@ fn test_diagnostic_source() {
5974
let error = TestArcedError(std::sync::Arc::new(AnErr));
6075
assert!(error.diagnostic_source().is_some());
6176
}
77+
78+
#[test]
79+
fn test_diagnostic_source_pass_extra_info() {
80+
let diag = TestBoxedError(Box::new(SourceError {
81+
code: String::from("Hello\nWorld!"),
82+
help: format!("Have you tried turning it on and off again?"),
83+
label: (1, 4),
84+
}));
85+
let mut out = String::new();
86+
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor())
87+
.with_width(80)
88+
.with_footer("this is a footer".into())
89+
.render_report(&mut out, &diag)
90+
.unwrap();
91+
println!("Error: {}", out);
92+
let expected = r#" × TestError
93+
╰─▶ × A complex error happened
94+
╭─[1:1]
95+
1 │ Hello
96+
· ──┬─
97+
· ╰── here
98+
2 │ World!
99+
╰────
100+
help: Have you tried turning it on and off again?
101+
102+
103+
this is a footer
104+
"#
105+
.to_string();
106+
assert_eq!(expected, out);
107+
}
108+
109+
#[test]
110+
fn test_diagnostic_source_is_output() {
111+
let diag = TestStructError {
112+
asdf_inner_foo: SourceError {
113+
code: String::from("right here"),
114+
help: String::from("That's where the error is!"),
115+
label: (6, 4),
116+
},
117+
};
118+
let mut out = String::new();
119+
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor())
120+
.with_width(80)
121+
.render_report(&mut out, &diag)
122+
.unwrap();
123+
println!("{}", out);
124+
125+
let expected = r#" × TestError
126+
╰─▶ × A complex error happened
127+
╭────
128+
1 │ right here
129+
· ──┬─
130+
· ╰── here
131+
╰────
132+
help: That's where the error is!
133+
134+
"#;
135+
136+
assert_eq!(expected, out);
137+
}
138+
139+
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
140+
#[error("A nested error happened")]
141+
struct NestedError {
142+
#[source_code]
143+
code: String,
144+
#[label("here")]
145+
label: (usize, usize),
146+
#[diagnostic_source]
147+
the_other_err: Box<dyn Diagnostic>,
148+
}
149+
150+
#[test]
151+
fn test_nested_diagnostic_source_is_output() {
152+
let inner_error = TestStructError {
153+
asdf_inner_foo: SourceError {
154+
code: String::from("This is another error"),
155+
help: String::from("You should fix this"),
156+
label: (3, 4),
157+
},
158+
};
159+
let diag = NestedError {
160+
code: String::from("right here"),
161+
label: (6, 4),
162+
the_other_err: Box::new(inner_error),
163+
};
164+
let mut out = String::new();
165+
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor())
166+
.with_width(80)
167+
.with_footer("Yooo, a footer".to_string())
168+
.render_report(&mut out, &diag)
169+
.unwrap();
170+
println!("{}", out);
171+
172+
let expected = r#" × A nested error happened
173+
├─▶ × TestError
174+
175+
╰─▶ × A complex error happened
176+
╭────
177+
1 │ This is another error
178+
· ──┬─
179+
· ╰── here
180+
╰────
181+
help: You should fix this
182+
183+
╭────
184+
1 │ right here
185+
· ──┬─
186+
· ╰── here
187+
╰────
188+
189+
Yooo, a footer
190+
"#;
191+
192+
assert_eq!(expected, out);
193+
}

0 commit comments

Comments
 (0)