tamer: diagnose::report: Error tracking
This extracts error tracking into the Reporter itself, which is already shared between lowering operations. This can then be used to display the number of errors. A new formatter (in tamer::fmt) will be added to handle the singular/plural conversion in place of "error(s)" in the future; I have more important things to work on right now. DEV-13158main
parent
f049da4496
commit
2ccdaf80fe
|
@ -111,16 +111,12 @@ fn compile<R: Reporter>(
|
|||
let escaper = DefaultEscaper::default();
|
||||
|
||||
let ebuf = RefCell::new(String::new());
|
||||
let has_err = RefCell::new(false);
|
||||
|
||||
fn report_err<E: Diagnostic, R: Reporter>(
|
||||
e: &E,
|
||||
reporter: &R,
|
||||
has_err: &RefCell<bool>,
|
||||
ebuf: &mut String,
|
||||
) -> Result<(), TamecError> {
|
||||
*has_err.borrow_mut() = true;
|
||||
|
||||
// See below note about buffering.
|
||||
ebuf.clear();
|
||||
writeln!(ebuf, "{}", reporter.render(e))?;
|
||||
|
@ -155,7 +151,6 @@ fn compile<R: Reporter>(
|
|||
report_err(
|
||||
&e,
|
||||
reporter,
|
||||
&has_err,
|
||||
&mut ebuf.borrow_mut(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -169,7 +164,6 @@ fn compile<R: Reporter>(
|
|||
report_err(
|
||||
&e,
|
||||
reporter,
|
||||
&has_err,
|
||||
&mut ebuf.borrow_mut(),
|
||||
)?;
|
||||
x
|
||||
|
@ -180,15 +174,17 @@ fn compile<R: Reporter>(
|
|||
},
|
||||
)?;
|
||||
|
||||
// TODO: Proper error summary,
|
||||
// and roll into rest of lowering pipeline.
|
||||
match has_err.into_inner() {
|
||||
false => Ok(()),
|
||||
true => {
|
||||
println!("fatal: failed to compile `{}`", dest_path);
|
||||
std::process::exit(1);
|
||||
}
|
||||
// TODO: Proper error summary and exit in `main`.
|
||||
if reporter.has_errors() {
|
||||
println!(
|
||||
"fatal: failed to compile `{}` due to previous {} error(s)",
|
||||
dest_path,
|
||||
reporter.error_count(),
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entrypoint for the compiler
|
||||
|
|
|
@ -113,8 +113,13 @@ impl Diagnostic for Infallible {
|
|||
/// Levels are used both for entire reports and for styling of individual
|
||||
/// [`AnnotatedSpan`]s.
|
||||
///
|
||||
/// Lower levels are more severe
|
||||
/// (e.g. level 1 is the worst).
|
||||
/// Higher severity levels are represented by lower integer values
|
||||
/// (e.g. level 1 is the worst),
|
||||
/// like DEFCON levels.
|
||||
/// The rationale here is that,
|
||||
/// provided that you remember that these are 1-indexed,
|
||||
/// you do not need to know how many levels exist to know how severe it
|
||||
/// is.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum Level {
|
||||
|
@ -145,6 +150,13 @@ pub enum Level {
|
|||
Help,
|
||||
}
|
||||
|
||||
impl Level {
|
||||
/// Whether this error level represents an error.
|
||||
pub fn is_error(self) -> bool {
|
||||
self <= Self::Error
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Level {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
|
@ -74,6 +74,13 @@ pub trait Reporter {
|
|||
/// and that we're not inadvertently suppressing the actual
|
||||
/// diagnostic messages that were requested.
|
||||
fn render<'d, D: Diagnostic>(&self, diagnostic: &'d D) -> Report<'d, D>;
|
||||
|
||||
/// Whether any reports have been rendered with an error level or higher.
|
||||
fn has_errors(&self) -> bool;
|
||||
|
||||
/// Number of reports with an error level or higher that have been
|
||||
/// rendered.
|
||||
fn error_count(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Render diagnostic report in a highly visual way.
|
||||
|
@ -96,12 +103,16 @@ pub struct VisualReporter<R: SpanResolver> {
|
|||
/// shared resolver caching is far more important than saving on a
|
||||
/// cell lock check.
|
||||
resolver: RefCell<R>,
|
||||
|
||||
/// Number of reports with a severity level of error or higher.
|
||||
err_n: RefCell<usize>,
|
||||
}
|
||||
|
||||
impl<R: SpanResolver> VisualReporter<R> {
|
||||
pub fn new(resolver: R) -> Self {
|
||||
Self {
|
||||
resolver: RefCell::new(resolver),
|
||||
err_n: RefCell::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +131,22 @@ impl<R: SpanResolver> Reporter for VisualReporter<R> {
|
|||
// which is more aesthetically pleasing.
|
||||
report.normalize_gutters();
|
||||
|
||||
if report.level.is_error() {
|
||||
// Not worried about overflow panic
|
||||
// (you have bigger problems if there are that many errors).
|
||||
*self.err_n.borrow_mut() += 1;
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> bool {
|
||||
self.error_count() > 0
|
||||
}
|
||||
|
||||
fn error_count(&self) -> usize {
|
||||
*self.err_n.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
/// Request a diagnostic description and immediately resolve the provided
|
||||
|
|
|
@ -114,33 +114,37 @@ const FILE_MANY_LINES: &[u8] = b"\
|
|||
\n90\n91\n92\n93\n94\n95\n96\n97\n98\n99\
|
||||
\n100";
|
||||
|
||||
fn new_sut() -> impl Reporter {
|
||||
let mut resolver = HashMap::<Context, BufSpanResolver<_>>::new();
|
||||
|
||||
let ctx_foo_bar = Context::from("foo/bar");
|
||||
let ctx_bar_baz = Context::from("bar/baz");
|
||||
let ctx_inv_utf = Context::from("invalid/utf8");
|
||||
let ctx_mny_lns = Context::from("many/lines");
|
||||
|
||||
resolver.insert(
|
||||
ctx_foo_bar,
|
||||
BufSpanResolver::new(Cursor::new(FILE_FOO_BAR), ctx_foo_bar),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_bar_baz,
|
||||
BufSpanResolver::new(Cursor::new(FILE_BAR_BAZ), ctx_bar_baz),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_inv_utf,
|
||||
BufSpanResolver::new(Cursor::new(FILE_INVALID_UTF8), ctx_inv_utf),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_mny_lns,
|
||||
BufSpanResolver::new(Cursor::new(FILE_MANY_LINES), ctx_mny_lns),
|
||||
);
|
||||
|
||||
VisualReporter::new(resolver)
|
||||
}
|
||||
|
||||
macro_rules! assert_report {
|
||||
($msg:expr, $aspans:expr, $expected:expr) => {
|
||||
let mut resolver = HashMap::<Context, BufSpanResolver<_>>::new();
|
||||
|
||||
let ctx_foo_bar = Context::from("foo/bar");
|
||||
let ctx_bar_baz = Context::from("bar/baz");
|
||||
let ctx_inv_utf = Context::from("invalid/utf8");
|
||||
let ctx_mny_lns = Context::from("many/lines");
|
||||
|
||||
resolver.insert(
|
||||
ctx_foo_bar,
|
||||
BufSpanResolver::new(Cursor::new(FILE_FOO_BAR), ctx_foo_bar),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_bar_baz,
|
||||
BufSpanResolver::new(Cursor::new(FILE_BAR_BAZ), ctx_bar_baz),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_inv_utf,
|
||||
BufSpanResolver::new(Cursor::new(FILE_INVALID_UTF8), ctx_inv_utf),
|
||||
);
|
||||
resolver.insert(
|
||||
ctx_mny_lns,
|
||||
BufSpanResolver::new(Cursor::new(FILE_MANY_LINES), ctx_mny_lns),
|
||||
);
|
||||
|
||||
let sut = VisualReporter::new(resolver);
|
||||
let sut = new_sut();
|
||||
|
||||
assert_eq!(
|
||||
sut.render(&StubError($msg.into(), $aspans)).to_string(),
|
||||
|
@ -589,3 +593,39 @@ error: wide gutter
|
|||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visual_reporter_tracks_errors() {
|
||||
let sut = new_sut();
|
||||
let ctx = Context::from("error/tracking");
|
||||
|
||||
fn feed_aspan(sut: &impl Reporter, aspan: AnnotatedSpan<'static>) {
|
||||
// We do not care about the report value;
|
||||
// we're only interested in how it tracks errors for this test.
|
||||
let _ = sut.render(&StubError("ignored".into(), vec![aspan]));
|
||||
}
|
||||
|
||||
// We should start with no errors.
|
||||
assert_eq!(sut.error_count(), 0);
|
||||
assert!(!sut.has_errors());
|
||||
|
||||
// Help must not increment.
|
||||
feed_aspan(&sut, ctx.span(0, 1).help("no increment"));
|
||||
assert_eq!(sut.error_count(), 0);
|
||||
assert!(!sut.has_errors());
|
||||
|
||||
// Note must not increment.
|
||||
feed_aspan(&sut, ctx.span(0, 1).note("no increment"));
|
||||
assert_eq!(sut.error_count(), 0);
|
||||
assert!(!sut.has_errors());
|
||||
|
||||
// Error must increment.
|
||||
feed_aspan(&sut, ctx.span(0, 1).error("increment"));
|
||||
assert_eq!(sut.error_count(), 1);
|
||||
assert!(sut.has_errors());
|
||||
|
||||
// Internal error must increment.
|
||||
feed_aspan(&sut, ctx.span(0, 1).error("increment"));
|
||||
assert_eq!(sut.error_count(), 2);
|
||||
assert!(sut.has_errors());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue