use crate::*;
use std::{
cell::RefCell,
fmt::{self, Debug, Display},
sync::Mutex,
};
use thiserror::Error;
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
Debug = 0,
Warning = 1,
#[default]
Error = 2,
Internal = 3,
}
impl Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
Level::Debug => "debug",
Level::Warning => "warning",
Level::Error => "error",
Level::Internal => "internal error",
};
f.write_str(msg)
}
}
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum NoteKind {
#[default]
Note = 0,
Trace = 1,
}
#[derive(Debug, Clone, Copy, Error)]
pub struct DiagnosticEmitted;
impl Display for DiagnosticEmitted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "see previous error messages")
}
}
pub trait Emit: Contextual {
fn emit(&self) -> DiagnosticEmitted;
}
pub trait Diagnosable: Display + Located + Contextual {
fn level(&self) -> Level {
Level::Error
}
fn notes(&self) -> DiagnosticEmitted {
DiagnosticEmitted
}
}
impl<T: Diagnosable> Emit for T {
fn emit(&self) -> DiagnosticEmitted {
self.context()
.emit(self.level(), Diagnostic::new(self.span(), self));
self.notes()
}
}
impl<T: Emit> From<T> for DiagnosticEmitted {
fn from(err: T) -> Self {
err.emit();
DiagnosticEmitted
}
}
pub type Result<T> = std::result::Result<T, DiagnosticEmitted>;
pub trait Recover<T, E> {
fn recover(self, value: T) -> std::result::Result<T, E>;
}
impl<T, E> Recover<T, E> for std::result::Result<T, E> {
fn recover(self, value: T) -> std::result::Result<T, E> {
match self {
Ok(ok) => Ok(ok),
Err(_) => Ok(value),
}
}
}
pub trait Emitter {
fn emit(&self, level: Level, diag: Diagnostic);
fn note(&self, kind: NoteKind, note: Diagnostic);
}
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub span: Option<Span>,
pub msg: String,
}
impl Diagnostic {
pub fn new(span: Option<Span>, msg: impl Display) -> Diagnostic {
Diagnostic {
span,
msg: format!("{msg}"),
}
}
}
enum Batched {
Diagnostic(Level, Diagnostic),
Note(NoteKind, Diagnostic),
}
pub struct NullEmitter;
impl Emitter for NullEmitter {
fn emit(&self, _level: Level, _diag: Diagnostic) {}
fn note(&self, _kind: NoteKind, _note: Diagnostic) {}
}
pub struct BatchEmitter<'e> {
emitter: &'e dyn Emitter,
batched: RefCell<Vec<Batched>>,
}
impl<'e> BatchEmitter<'e> {
pub fn new(emitter: &'e dyn Emitter) -> BatchEmitter<'e> {
BatchEmitter {
emitter,
batched: RefCell::default(),
}
}
pub fn commit(self) {
for batched in self.batched.into_inner() {
match batched {
Batched::Diagnostic(level, diag) => self.emitter.emit(level, diag),
Batched::Note(kind, diag) => self.emitter.note(kind, diag),
}
}
}
}
impl Emitter for BatchEmitter<'_> {
fn emit(&self, level: Level, diag: Diagnostic) {
self.batched
.borrow_mut()
.push(Batched::Diagnostic(level, diag));
}
fn note(&self, kind: NoteKind, note: Diagnostic) {
self.batched.borrow_mut().push(Batched::Note(kind, note));
}
}
pub struct StdErrEmitter {
trace_emitted: Mutex<bool>,
note_emitted: Mutex<bool>,
}
impl StdErrEmitter {
pub fn new() -> StdErrEmitter {
StdErrEmitter {
trace_emitted: Mutex::new(false),
note_emitted: Mutex::new(false),
}
}
fn print(&self, preamble: impl Display, span: Option<Span>, msg: impl Display) {
if let Some(span) = span {
eprint!("{span}:")
}
eprintln!("{preamble}: {msg}");
}
}
impl Default for StdErrEmitter {
fn default() -> Self {
StdErrEmitter::new()
}
}
impl Emitter for StdErrEmitter {
fn emit(&self, level: Level, diag: Diagnostic) {
let mut note_emitted = self.note_emitted.lock().unwrap();
let mut trace_emitted = self.trace_emitted.lock().unwrap();
if *note_emitted {
eprintln!()
}
self.print(level, diag.span, diag.msg);
*trace_emitted = false;
*note_emitted = false;
}
fn note(&self, kind: NoteKind, note: Diagnostic) {
if kind == NoteKind::Trace {
let mut trace_emitted = self.trace_emitted.lock().unwrap();
if *trace_emitted {
return;
} else {
*trace_emitted = true;
}
}
*self.note_emitted.lock().unwrap() = true;
self.print("note", note.span, note.msg);
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! diagnose {
($emitter:expr, $ty:ident::$level:ident, $arg:literal) => {
$crate::diagnose!(
impl $emitter, $ty, $level, None, format!($arg)
)
};
($emitter:expr, $ty:ident::$level:ident, $arg:expr) => {
$crate::diagnose!(
impl $emitter, $ty, $level, None, $arg
)
};
($emitter:expr, $ty:ident::$level:ident, $span:expr, $arg:literal) => {
$crate::diagnose!(
impl $emitter, $ty, $level, $span.clone().into(), format!($arg)
)
};
($emitter:expr, $ty:ident::$level:ident, $span:expr, $arg:expr) => {
$crate::diagnose!(
impl $emitter, $ty, $level, $span.clone().into(), $arg
)
};
($emitter:expr, $ty:ident::$level:ident, $span:expr, $($args:tt)+) => {
$crate::diagnose!(
impl $emitter, $ty, $level, $span.clone().into(), format!($($args)*)
)
};
($emitter:expr, $ty:ident::$level:ident, $($args:tt)+) => {
$crate::diagnose!(
impl $emitter, $ty, $level, None, format!($($args)*)
)
};
(impl $emitter:expr, NoteKind, $level:ident, $span:expr, $arg:expr) => {
$crate::Emitter::note(
$emitter,
$crate::NoteKind::$level,
$crate::Diagnostic::new($span, $arg),
)
};
(impl $emitter:expr, Level, $level:ident, $span:expr, $arg:expr) => {
$crate::Emitter::emit(
$emitter,
$crate::Level::$level,
$crate::Diagnostic::new($span, $arg),
)
};
}
#[macro_export]
macro_rules! note {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, NoteKind::Note, $($args)*)
};
}
#[macro_export]
macro_rules! trace {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, NoteKind::Trace, $($args)*)
};
}
#[macro_export]
macro_rules! internal {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, Level::Internal, $($args)*)
};
}
#[macro_export]
macro_rules! error {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, Level::Error, $($args)*)
};
}
#[macro_export]
macro_rules! warning {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, Level::Warning, $($args)*)
};
}
#[macro_export]
macro_rules! debug {
($emitter:expr, $($args:tt)+) => {
$crate::diagnose!($emitter, Level::Debug, $($args)*)
};
}