// Copyright (c) 2016-2021 Fabian Schuiki //! Utilities to implement diagnostics and error reporting facilities. use crate::source::Span; use std::fmt; /// Print debug information. Omitted in release builds. #[macro_export] #[cfg(debug_assertions)] macro_rules! debugln { ($($arg:tt)*) => { eprintln!("\x1B[34;1mdebug:\x1B[m {}", format!($($arg)*)); } } /// Print debug information. Omitted in release builds. #[macro_export] #[cfg(not(debug_assertions))] macro_rules! debugln { ($($arg:tt)*) => {}; } /// A handler deals with errors. #[derive(Debug)] pub struct Handler {} pub static DUMMY_HANDLER: Handler = Handler {}; /// Used to emit structured error messages. #[must_use] #[derive(Clone, Debug)] pub struct DiagnosticBuilder<'a> { pub handler: &'a Handler, pub message: String, } /// A diagnostic result type. Either carries the result `T` in the Ok variant, /// or an assembled diagnostic in the Err variant. pub type DiagResult<'a, T> = Result>; /// Emits diagnostic messages. pub trait DiagEmitter { /// Emit a diagnostic message. fn emit(&self, diag: DiagBuilder2); } impl<'a, T> DiagEmitter for &'a T where T: DiagEmitter + ?Sized, { fn emit(&self, diag: DiagBuilder2) { (*self).emit(diag) } } /// Emit errors as diagnostics. /// /// Useful if implemented on the error types returned from results. Allows these /// results to be emitted as diagnostics on-the-fly and converted to a `()` /// error type result. pub trait EmitError { type Output; fn emit(self, ctx: C) -> Self::Output; } impl EmitError for Result { type Output = Result; fn emit(self, ctx: C) -> Result { self.map_err(move |e| e.emit(ctx)) } } #[must_use] #[derive(Clone, Debug)] pub struct DiagBuilder2 { pub severity: Severity, pub message: String, pub segments: Vec, } #[derive(Clone, Debug)] pub enum DiagSegment { Span(Span), Note(String), } /// A diagnostic result type. Either carries the result `T` in the Ok variant, /// or an assembled diagnostic in the Err variant. pub type DiagResult2 = Result; impl DiagBuilder2 { pub fn new>(severity: Severity, message: S) -> DiagBuilder2 { DiagBuilder2 { severity: severity, message: message.into(), segments: Vec::new(), } } pub fn bug>(message: S) -> DiagBuilder2 { DiagBuilder2::new(Severity::Bug, message) } pub fn fatal>(message: S) -> DiagBuilder2 { DiagBuilder2::new(Severity::Fatal, message) } pub fn error>(message: S) -> DiagBuilder2 { DiagBuilder2::new(Severity::Error, message) } pub fn warning>(message: S) -> DiagBuilder2 { DiagBuilder2::new(Severity::Warning, message) } pub fn note>(message: S) -> DiagBuilder2 { DiagBuilder2::new(Severity::Note, message) } pub fn segment(self, segment: DiagSegment) -> DiagBuilder2 { let mut segments = self.segments; segments.push(segment); DiagBuilder2 { segments: segments, ..self } } pub fn span>(self, span: S) -> DiagBuilder2 { self.segment(DiagSegment::Span(span.into())) } pub fn add_note>(self, message: S) -> DiagBuilder2 { self.segment(DiagSegment::Note(message.into())) } pub fn get_severity(&self) -> Severity { self.severity } pub fn get_message(&self) -> &String { &self.message } pub fn get_segments(&self) -> &[DiagSegment] { &self.segments } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum Severity { Note, Warning, Error, Fatal, Bug, } impl Severity { pub fn to_str(self) -> &'static str { match self { Severity::Fatal => "fatal", Severity::Error => "error", Severity::Warning => "warning", Severity::Note => "note", Severity::Bug => "compiler bug", } } } impl fmt::Display for Severity { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_str()) } } impl fmt::Display for DiagBuilder2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut colorcode = match self.get_severity() { Severity::Bug | Severity::Fatal | Severity::Error => "\x1B[31;1m", Severity::Warning => "\x1B[33;1m", Severity::Note => "\x1B[36;1m", }; write!( f, "{}{}:\x1B[m\x1B[1m {}\x1B[m\n", colorcode, self.get_severity(), self.get_message() )?; for segment in &self.segments { match *segment { DiagSegment::Span(sp) => { let c = sp.source.get_content(); // Look for the start of the line. let (line, col, line_offset) = sp.begin().human(); // Print the line in question. let text: String = c .iter_from(line_offset) .map(|x| x.1) .take_while(|c| *c != '\n' && *c != '\r') .collect(); write!( f, " --> {}:{}:{}-{}:\n", sp.source.get_path(), line, col, col + sp.extract().len() )?; write!(f, " | \n")?; write!(f, " | ")?; for (mut i, c) in text.char_indices() { i += line_offset; if sp.begin != sp.end { if i == sp.begin { write!(f, "{}", colorcode)?; } if i == sp.end { write!(f, "\x1B[m")?; } } match c { '\t' => write!(f, " ")?, c => write!(f, "{}", c)?, } } write!(f, "\x1B[m\n")?; write!(f, " | ")?; // Print the caret markers for the line in question. let mut pd = ' '; for (mut i, c) in text.char_indices() { i += line_offset; let d = if (i >= sp.begin && i < sp.end) || (i == sp.begin && sp.begin == sp.end) { '^' } else { ' ' }; if d != pd { write!(f, "{}", if d == ' ' { "\x1B[m" } else { colorcode })?; } pd = d; match c { '\t' => write!(f, "{}{}{}{}", d, d, d, d)?, _ => write!(f, "{}", d)?, } } write!(f, "\x1B[m\n")?; colorcode = "\x1B[1m"; } DiagSegment::Note(ref message) => { write!(f, " = \x1B[1mnote:\x1B[m {}\n", message)? } } } if self.get_severity() == Severity::Bug { write!( f, "\nYou have encountered a compiler bug. Sorry about that! We would appreciate if \ you open an issue [1] and describe how you triggered the bug, together with a \ minimal snippet of code to reproduce it. Thanks!\n" )?; write!(f, "[1]: https://github.com/fabianschuiki/moore\n")?; } Ok(()) } }