core: Add Diagnostic class
This commit is contained in:
parent
593555228c
commit
1c071a294c
|
@ -1,7 +1,258 @@
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::hash_set::{IntoIter, Iter};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use self::Diagnostic::*;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use nac3parser::ast::Location;
|
||||||
|
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod symbol_resolver;
|
pub mod symbol_resolver;
|
||||||
pub mod toplevel;
|
pub mod toplevel;
|
||||||
pub mod typecheck;
|
pub mod typecheck;
|
||||||
|
|
||||||
|
#[derive(Clone, Eq)]
|
||||||
|
pub enum Diagnostic {
|
||||||
|
/// A diagnostic warning.
|
||||||
|
DiagWarn(String, Location),
|
||||||
|
|
||||||
|
/// A diagnostic error.
|
||||||
|
DiagErr(String, Location),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic {
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
|
||||||
|
let diag_type = match self {
|
||||||
|
DiagErr(_, _) => "error",
|
||||||
|
DiagWarn(_, _) => "warning",
|
||||||
|
};
|
||||||
|
let msg = self.message();
|
||||||
|
let loc = self.location();
|
||||||
|
|
||||||
|
format!("{loc}: {diag_type}: {msg}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the message of this [Diagnostic].
|
||||||
|
fn message(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
DiagErr(msg, _) => msg,
|
||||||
|
DiagWarn(msg, _) => msg,
|
||||||
|
}.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the location of where this [Diagnostic] is created from.
|
||||||
|
fn location(&self) -> &Location {
|
||||||
|
match self {
|
||||||
|
DiagErr(_, loc) => loc,
|
||||||
|
DiagWarn(_, loc) => loc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Diagnostic {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let loc_cmp = self.location().cmp(other.location());
|
||||||
|
if loc_cmp != Ordering::Equal {
|
||||||
|
return loc_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
let diag_type_cmp = match self {
|
||||||
|
DiagWarn(_, _) => match other {
|
||||||
|
DiagWarn(_, _) => Ordering::Equal,
|
||||||
|
DiagErr(_, _) => Ordering::Less,
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagErr(_, _) => match other {
|
||||||
|
DiagWarn(_, _) => Ordering::Greater,
|
||||||
|
DiagErr(_, _) => Ordering::Equal,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if diag_type_cmp != Ordering::Equal {
|
||||||
|
return diag_type_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
self.message().cmp(other.message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Diagnostic {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Diagnostic {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
DiagErr(msg, loc) => {
|
||||||
|
if let DiagErr(other_msg, other_loc) = other {
|
||||||
|
msg == other_msg && loc == other_loc
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagWarn(msg, loc) => {
|
||||||
|
if let DiagWarn(other_msg, other_loc) = other {
|
||||||
|
msg == other_msg && loc == other_loc
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Diagnostic {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Diagnostic {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.to_string().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn contains_errors(diagnostics: &HashSet<Diagnostic>) -> bool {
|
||||||
|
diagnostics.iter().any(|d| match d {
|
||||||
|
DiagErr(_, _) => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for [Result] encapsulating a success value of type [T], and any diagnostics emitted
|
||||||
|
/// during the generation of the result.
|
||||||
|
///
|
||||||
|
/// This type should be used where a result cannot be meaningfully generated when a subset of the
|
||||||
|
/// possible diagnostics is emitted.
|
||||||
|
pub type DiagnosticsResult<T> = Result<(T, DiagnosticEngine), DiagnosticEngine>;
|
||||||
|
|
||||||
|
/// Type representing a result of type `T` with any diagnostics emitted during the generation of the
|
||||||
|
/// result.
|
||||||
|
///
|
||||||
|
/// This type should be used where, even when diagnostics are generated, a meaningful result can
|
||||||
|
/// still be generated.
|
||||||
|
pub struct DiagnosticsPartialResult<T> {
|
||||||
|
value: T,
|
||||||
|
engine: DiagnosticEngine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> Into<DiagnosticsResult<T>> for DiagnosticsPartialResult<T> {
|
||||||
|
fn into(self) -> DiagnosticsResult<T> {
|
||||||
|
if self.engine.has_errors() {
|
||||||
|
Err(self.engine)
|
||||||
|
} else {
|
||||||
|
Ok((self.value, self.engine))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple abstraction over a set of collected diagnostics.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct DiagnosticEngine {
|
||||||
|
diagnostics: HashSet<Diagnostic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticEngine {
|
||||||
|
pub fn new() -> DiagnosticEngine {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(s: HashSet<Diagnostic>) -> DiagnosticEngine {
|
||||||
|
DiagnosticEngine {
|
||||||
|
diagnostics: s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn single(diag: Diagnostic) -> DiagnosticEngine {
|
||||||
|
Self::from(HashSet::from([diag]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.diagnostics.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_errors(&self) -> bool {
|
||||||
|
self.diagnostics.iter().any(|d| match d {
|
||||||
|
DiagErr(_, _) => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, diag: Diagnostic) {
|
||||||
|
self.diagnostics.insert(diag);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend<I: IntoIterator<Item = Diagnostic>>(&mut self, diags: I) {
|
||||||
|
self.diagnostics.extend(diags);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_with(&mut self, other: DiagnosticEngine) {
|
||||||
|
self.extend(other.diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume_partial_result<T>(&mut self, result: DiagnosticsPartialResult<T>) -> T {
|
||||||
|
self.merge_with(result.engine);
|
||||||
|
result.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume_result<T>(&mut self, result: DiagnosticsResult<T>) -> Result<T, Self> {
|
||||||
|
match result {
|
||||||
|
Ok((v, diags)) => {
|
||||||
|
self.merge_with(diags);
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(diags) => {
|
||||||
|
self.merge_with(diags);
|
||||||
|
Err(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume_result_to_option<T>(&mut self, result: DiagnosticsResult<T>) -> Option<T> {
|
||||||
|
match result {
|
||||||
|
Ok((v, diags)) => {
|
||||||
|
self.merge_with(diags);
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(diags) => {
|
||||||
|
self.merge_with(diags);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> Iter<'_, Diagnostic> {
|
||||||
|
self.diagnostics.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(self) -> IntoIter<Diagnostic> {
|
||||||
|
self.diagnostics.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_partial_result<T>(self, value: T) -> DiagnosticsPartialResult<T> {
|
||||||
|
DiagnosticsPartialResult {
|
||||||
|
value,
|
||||||
|
engine: self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_result<T>(self, value: T) -> DiagnosticsResult<T> {
|
||||||
|
if self.has_errors() {
|
||||||
|
Err(self)
|
||||||
|
} else {
|
||||||
|
Ok((value, self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue