fix(handlers): source code propagation for JSON handler

A part of fix of #99, follow up of #117
This commit is contained in:
Paul Colomiets 2022-02-23 01:43:53 +02:00
parent 32520108ae
commit 410d847b0b
2 changed files with 115 additions and 11 deletions

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Write};
use crate::{protocol::Diagnostic, ReportHandler, Severity};
use crate::{protocol::Diagnostic, ReportHandler, Severity, SourceCode};
/**
[ReportHandler] that renders json output.
@ -62,6 +62,15 @@ impl JSONReportHandler {
&self,
f: &mut impl fmt::Write,
diagnostic: &(dyn Diagnostic),
) -> fmt::Result {
self._render_report(f, diagnostic, None)
}
fn _render_report(
&self,
f: &mut impl fmt::Write,
diagnostic: &(dyn Diagnostic),
parent_src: Option<&dyn SourceCode>,
) -> fmt::Result {
write!(f, r#"{{"message": "{}","#, escape(&diagnostic.to_string()))?;
if let Some(code) = diagnostic.code() {
@ -79,8 +88,9 @@ impl JSONReportHandler {
if let Some(help) = diagnostic.help() {
write!(f, r#""help": "{}","#, escape(&help.to_string()))?;
}
if diagnostic.source_code().is_some() {
self.render_snippets(f, diagnostic)?;
let src = diagnostic.source_code().or(parent_src);
if let Some(src) = src {
self.render_snippets(f, diagnostic, src)?;
}
if let Some(labels) = diagnostic.labels() {
write!(f, r#""labels": ["#)?;
@ -114,7 +124,7 @@ impl JSONReportHandler {
} else {
add_comma = true;
}
self.render_report(f, related)?;
self._render_report(f, related, src)?;
}
write!(f, "]")?;
} else {
@ -127,14 +137,13 @@ impl JSONReportHandler {
&self,
f: &mut impl fmt::Write,
diagnostic: &(dyn Diagnostic),
source: &dyn SourceCode,
) -> fmt::Result {
if let Some(source) = diagnostic.source_code() {
if let Some(mut labels) = diagnostic.labels() {
if let Some(label) = labels.next() {
if let Ok(span_content) = source.read_span(label.inner(), 0, 0) {
let filename = span_content.name().unwrap_or_default();
return write!(f, r#""filename": "{}","#, escape(filename));
}
if let Some(mut labels) = diagnostic.labels() {
if let Some(label) = labels.next() {
if let Ok(span_content) = source.read_span(label.inner(), 0, 0) {
let filename = span_content.name().unwrap_or_default();
return write!(f, r#""filename": "{}","#, escape(filename));
}
}
}

View File

@ -809,4 +809,99 @@ mod json_report_handler {
assert_eq!(expected, out);
Ok(())
}
#[test]
fn related_source_code_propagation() -> Result<(), MietteError> {
#[derive(Debug, Diagnostic, Error)]
#[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad {
#[source_code]
src: NamedSource,
#[label("this bit here")]
highlight: SourceSpan,
#[related]
related: Vec<InnerError>,
}
#[derive(Debug, Diagnostic, Error)]
#[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct InnerError {
#[label("this bit here")]
highlight: SourceSpan,
}
let src = "source\n text\n here".to_string();
let err = MyBad {
src: NamedSource::new("bad_file.rs", src.clone()),
highlight: (9, 4).into(),
related: vec![
InnerError {
highlight: (0, 6).into(),
},
InnerError {
highlight: (0, 6).into(),
},
],
};
let out = fmt_report(err.into());
println!("Error: {}", out);
let expected: String = r#"
{
"message": "oops!",
"code": "oops::my::bad",
"severity": "error",
"help": "try doing it better next time?",
"filename": "bad_file.rs",
"labels": [
{
"label": "this bit here",
"span": {
"offset": 9,
"length": 4
}
}
],
"related": [{
"message": "oops!",
"code": "oops::my::bad",
"severity": "error",
"help": "try doing it better next time?",
"filename": "bad_file.rs",
"labels": [
{
"label": "this bit here",
"span": {
"offset": 0,
"length": 6
}
}
],
"related": []
},{
"message": "oops!",
"code": "oops::my::bad",
"severity": "error",
"help": "try doing it better next time?",
"filename": "bad_file.rs",
"labels": [
{
"label": "this bit here",
"span": {
"offset": 0,
"length": 6
}
}
],
"related": []
}]
}"#
.lines()
.into_iter()
.map(|s| s.trim_matches(|c| c == ' ' || c == '\n'))
.collect();
assert_eq!(expected, out);
Ok(())
}
}