use crate::source::Span;
use std::fmt;
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! debugln {
($($arg:tt)*) => { eprintln!("\x1B[34;1mdebug:\x1B[m {}", format!($($arg)*)); }
}
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! debugln {
($($arg:tt)*) => {};
}
#[derive(Debug)]
pub struct Handler {}
pub static DUMMY_HANDLER: Handler = Handler {};
#[must_use]
#[derive(Clone, Debug)]
pub struct DiagnosticBuilder<'a> {
pub handler: &'a Handler,
pub message: String,
}
pub type DiagResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
pub trait DiagEmitter {
fn emit(&self, diag: DiagBuilder2);
}
impl<'a, T> DiagEmitter for &'a T
where
T: DiagEmitter + ?Sized,
{
fn emit(&self, diag: DiagBuilder2) {
(*self).emit(diag)
}
}
pub trait EmitError {
type Output;
fn emit<C: DiagEmitter>(self, ctx: C) -> Self::Output;
}
impl<T, E: EmitError> EmitError for Result<T, E> {
type Output = Result<T, E::Output>;
fn emit<C: DiagEmitter>(self, ctx: C) -> Result<T, E::Output> {
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<DiagSegment>,
}
#[derive(Clone, Debug)]
pub enum DiagSegment {
Span(Span),
Note(String),
}
pub type DiagResult2<T> = Result<T, DiagBuilder2>;
impl DiagBuilder2 {
pub fn new<S: Into<String>>(severity: Severity, message: S) -> DiagBuilder2 {
DiagBuilder2 {
severity: severity,
message: message.into(),
segments: Vec::new(),
}
}
pub fn bug<S: Into<String>>(message: S) -> DiagBuilder2 {
DiagBuilder2::new(Severity::Bug, message)
}
pub fn fatal<S: Into<String>>(message: S) -> DiagBuilder2 {
DiagBuilder2::new(Severity::Fatal, message)
}
pub fn error<S: Into<String>>(message: S) -> DiagBuilder2 {
DiagBuilder2::new(Severity::Error, message)
}
pub fn warning<S: Into<String>>(message: S) -> DiagBuilder2 {
DiagBuilder2::new(Severity::Warning, message)
}
pub fn note<S: Into<String>>(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<S: Into<Span>>(self, span: S) -> DiagBuilder2 {
self.segment(DiagSegment::Span(span.into()))
}
pub fn add_note<S: Into<String>>(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();
let (line, col, line_offset) = sp.begin().human();
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, " | ")?;
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(())
}
}