1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
//! Reporting errors, warnings, and other information to the user.
use crate::parse::FileId;
use std::{convert::TryInto, ops::Range};
/// A span of a source file.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
start: u32,
end: u32,
}
/// A diagnostic level
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Level {
/// An unrecoverable error
Error,
/// A warning: something the user may want to address, but which is non-fatal
Warning,
/// Info. unused?
Info,
}
/// A message, associated with a location in a file.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Message {
pub text: String,
pub file: FileId,
pub span: Span,
}
/// A diagnostic, including a message and additional annotations
//TODO: would this be more useful with additional annotations or a help field?
//some fancy error reporting crates have these.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
/// The main message for this diagnostic
pub message: Message,
/// The diagnostic level
pub level: Level,
}
impl Span {
/// Convert this span to a `Range<usize>`
pub fn range(&self) -> Range<usize> {
self.start as usize..self.end as usize
}
}
impl Diagnostic {
/// Create a new diagnostic
pub fn new(
level: Level,
file: FileId,
range: Range<usize>,
message: impl Into<String>,
) -> Self {
Diagnostic {
message: Message {
text: message.into(),
span: Span {
start: range.start.try_into().unwrap(),
end: range.end.try_into().unwrap(),
},
file,
},
level,
}
}
/// Create a new error, at the provided location
pub fn error(file: FileId, span: Range<usize>, message: impl Into<String>) -> Self {
Diagnostic::new(Level::Error, file, span, message)
}
/// Create a new warning, at the provided location
pub fn warning(file: FileId, span: Range<usize>, message: impl Into<String>) -> Self {
Diagnostic::new(Level::Warning, file, span, message)
}
/// The diagnostic's message text
pub fn text(&self) -> &str {
&self.message.text
}
/// The location of the main span, as a `Range<usize>`
pub fn span(&self) -> Range<usize> {
self.message.span.range()
}
/// `true` if this diagnostic is an error
pub fn is_error(&self) -> bool {
matches!(self.level, Level::Error)
}
}