use crate::{Diagnostic, Severity};
pub const DEFAULT_ERROR_LIMIT: usize = 20;
#[derive(Debug)]
pub struct Errors {
diagnostics: Vec<Diagnostic>,
errors: usize,
limit: usize,
stopped: bool,
}
impl Default for Errors {
fn default() -> Self {
Errors::new(DEFAULT_ERROR_LIMIT)
}
}
impl Errors {
#[must_use]
pub fn new(limit: usize) -> Self {
Errors { diagnostics: Vec::new(), errors: 0, limit, stopped: false }
}
pub fn push(&mut self, diagnostic: Diagnostic) {
if self.stopped {
return;
}
let fatal = diagnostic.severity.is_fatal();
let span = diagnostic.span;
self.diagnostics.push(diagnostic);
if fatal {
self.errors += 1;
if self.limit != 0 && self.errors >= self.limit {
self.diagnostics.push(Diagnostic::new(
Severity::Note,
"too many errors emitted, stopping now",
span,
));
self.stopped = true;
}
}
}
pub fn push_unless(&mut self, suppressed: bool, diagnostic: Diagnostic) {
if !suppressed {
self.push(diagnostic);
}
}
#[inline]
#[must_use]
pub fn stopped(&self) -> bool {
self.stopped
}
#[inline]
#[must_use]
pub fn errors(&self) -> usize {
self.errors
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.diagnostics.is_empty()
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.diagnostics.len()
}
#[inline]
#[must_use]
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
#[must_use]
pub fn finish(self) -> Vec<Diagnostic> {
self.diagnostics
}
}
#[cfg(test)]
mod tests {
use crate::Span;
use super::*;
#[test]
fn the_limit_stops_the_run_and_says_so() {
let mut errors = Errors::new(3);
for _ in 0..10 {
errors.push(Diagnostic::error("no", Span::empty_at(0)));
}
assert!(errors.stopped());
assert_eq!(errors.errors(), 3);
let diagnostics = errors.finish();
assert_eq!(diagnostics.len(), 4);
assert_eq!(diagnostics[3].severity, Severity::Note);
assert_eq!(diagnostics[3].message, "too many errors emitted, stopping now");
}
#[test]
fn a_limit_of_zero_never_stops() {
let mut errors = Errors::new(0);
for _ in 0..64 {
errors.push(Diagnostic::error("no", Span::empty_at(0)));
}
assert!(!errors.stopped());
assert_eq!(errors.len(), 64);
}
#[test]
fn warnings_do_not_count_against_the_limit() {
let mut errors = Errors::default();
assert!(errors.is_empty());
for _ in 0..64 {
errors.push(Diagnostic::warning("hmm", Span::empty_at(0)));
}
assert_eq!(errors.errors(), 0);
assert!(!errors.stopped());
}
#[test]
fn a_message_about_a_poisoned_node_is_held_back() {
let mut errors = Errors::default();
let at = Span::empty_at(0);
errors.push_unless(true, Diagnostic::error("about the broken one", at));
assert!(errors.is_empty());
errors.push_unless(false, Diagnostic::error("about the good one", at));
assert_eq!(errors.len(), 1);
}
}