moore_common/
errors.rs

1// Copyright (c) 2016-2021 Fabian Schuiki
2
3//! Utilities to implement diagnostics and error reporting facilities.
4
5use crate::source::Span;
6use std::fmt;
7
8/// Print debug information. Omitted in release builds.
9#[macro_export]
10#[cfg(debug_assertions)]
11macro_rules! debugln {
12    ($($arg:tt)*) => { eprintln!("\x1B[34;1mdebug:\x1B[m {}", format!($($arg)*)); }
13}
14
15/// Print debug information. Omitted in release builds.
16#[macro_export]
17#[cfg(not(debug_assertions))]
18macro_rules! debugln {
19    ($($arg:tt)*) => {};
20}
21
22/// A handler deals with errors.
23#[derive(Debug)]
24pub struct Handler {}
25
26pub static DUMMY_HANDLER: Handler = Handler {};
27
28/// Used to emit structured error messages.
29#[must_use]
30#[derive(Clone, Debug)]
31pub struct DiagnosticBuilder<'a> {
32    pub handler: &'a Handler,
33    pub message: String,
34}
35
36/// A diagnostic result type. Either carries the result `T` in the Ok variant,
37/// or an assembled diagnostic in the Err variant.
38pub type DiagResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
39
40/// Emits diagnostic messages.
41pub trait DiagEmitter {
42    /// Emit a diagnostic message.
43    fn emit(&self, diag: DiagBuilder2);
44}
45
46impl<'a, T> DiagEmitter for &'a T
47where
48    T: DiagEmitter + ?Sized,
49{
50    fn emit(&self, diag: DiagBuilder2) {
51        (*self).emit(diag)
52    }
53}
54
55/// Emit errors as diagnostics.
56///
57/// Useful if implemented on the error types returned from results. Allows these
58/// results to be emitted as diagnostics on-the-fly and converted to a `()`
59/// error type result.
60pub trait EmitError {
61    type Output;
62    fn emit<C: DiagEmitter>(self, ctx: C) -> Self::Output;
63}
64
65impl<T, E: EmitError> EmitError for Result<T, E> {
66    type Output = Result<T, E::Output>;
67    fn emit<C: DiagEmitter>(self, ctx: C) -> Result<T, E::Output> {
68        self.map_err(move |e| e.emit(ctx))
69    }
70}
71
72#[must_use]
73#[derive(Clone, Debug)]
74pub struct DiagBuilder2 {
75    pub severity: Severity,
76    pub message: String,
77    pub segments: Vec<DiagSegment>,
78}
79
80#[derive(Clone, Debug)]
81pub enum DiagSegment {
82    Span(Span),
83    Note(String),
84}
85
86/// A diagnostic result type. Either carries the result `T` in the Ok variant,
87/// or an assembled diagnostic in the Err variant.
88pub type DiagResult2<T> = Result<T, DiagBuilder2>;
89
90impl DiagBuilder2 {
91    pub fn new<S: Into<String>>(severity: Severity, message: S) -> DiagBuilder2 {
92        DiagBuilder2 {
93            severity: severity,
94            message: message.into(),
95            segments: Vec::new(),
96        }
97    }
98
99    pub fn bug<S: Into<String>>(message: S) -> DiagBuilder2 {
100        DiagBuilder2::new(Severity::Bug, message)
101    }
102
103    pub fn fatal<S: Into<String>>(message: S) -> DiagBuilder2 {
104        DiagBuilder2::new(Severity::Fatal, message)
105    }
106
107    pub fn error<S: Into<String>>(message: S) -> DiagBuilder2 {
108        DiagBuilder2::new(Severity::Error, message)
109    }
110
111    pub fn warning<S: Into<String>>(message: S) -> DiagBuilder2 {
112        DiagBuilder2::new(Severity::Warning, message)
113    }
114
115    pub fn note<S: Into<String>>(message: S) -> DiagBuilder2 {
116        DiagBuilder2::new(Severity::Note, message)
117    }
118
119    pub fn segment(self, segment: DiagSegment) -> DiagBuilder2 {
120        let mut segments = self.segments;
121        segments.push(segment);
122        DiagBuilder2 {
123            segments: segments,
124            ..self
125        }
126    }
127
128    pub fn span<S: Into<Span>>(self, span: S) -> DiagBuilder2 {
129        self.segment(DiagSegment::Span(span.into()))
130    }
131
132    pub fn add_note<S: Into<String>>(self, message: S) -> DiagBuilder2 {
133        self.segment(DiagSegment::Note(message.into()))
134    }
135
136    pub fn get_severity(&self) -> Severity {
137        self.severity
138    }
139
140    pub fn get_message(&self) -> &String {
141        &self.message
142    }
143
144    pub fn get_segments(&self) -> &[DiagSegment] {
145        &self.segments
146    }
147}
148
149#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
150pub enum Severity {
151    Note,
152    Warning,
153    Error,
154    Fatal,
155    Bug,
156}
157
158impl Severity {
159    pub fn to_str(self) -> &'static str {
160        match self {
161            Severity::Fatal => "fatal",
162            Severity::Error => "error",
163            Severity::Warning => "warning",
164            Severity::Note => "note",
165            Severity::Bug => "compiler bug",
166        }
167    }
168}
169
170impl fmt::Display for Severity {
171    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172        write!(f, "{}", self.to_str())
173    }
174}
175
176impl fmt::Display for DiagBuilder2 {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        let mut colorcode = match self.get_severity() {
179            Severity::Bug | Severity::Fatal | Severity::Error => "\x1B[31;1m",
180            Severity::Warning => "\x1B[33;1m",
181            Severity::Note => "\x1B[36;1m",
182        };
183        write!(
184            f,
185            "{}{}:\x1B[m\x1B[1m {}\x1B[m\n",
186            colorcode,
187            self.get_severity(),
188            self.get_message()
189        )?;
190
191        for segment in &self.segments {
192            match *segment {
193                DiagSegment::Span(sp) => {
194                    let c = sp.source.get_content();
195
196                    // Look for the start of the line.
197                    let (line, col, line_offset) = sp.begin().human();
198
199                    // Print the line in question.
200                    let text: String = c
201                        .iter_from(line_offset)
202                        .map(|x| x.1)
203                        .take_while(|c| *c != '\n' && *c != '\r')
204                        .collect();
205                    write!(
206                        f,
207                        "  --> {}:{}:{}-{}:\n",
208                        sp.source.get_path(),
209                        line,
210                        col,
211                        col + sp.extract().len()
212                    )?;
213                    write!(f, "   | \n")?;
214                    write!(f, "   | ")?;
215                    for (mut i, c) in text.char_indices() {
216                        i += line_offset;
217                        if sp.begin != sp.end {
218                            if i == sp.begin {
219                                write!(f, "{}", colorcode)?;
220                            }
221                            if i == sp.end {
222                                write!(f, "\x1B[m")?;
223                            }
224                        }
225                        match c {
226                            '\t' => write!(f, "    ")?,
227                            c => write!(f, "{}", c)?,
228                        }
229                    }
230                    write!(f, "\x1B[m\n")?;
231                    write!(f, "   | ")?;
232
233                    // Print the caret markers for the line in question.
234                    let mut pd = ' ';
235                    for (mut i, c) in text.char_indices() {
236                        i += line_offset;
237                        let d = if (i >= sp.begin && i < sp.end)
238                            || (i == sp.begin && sp.begin == sp.end)
239                        {
240                            '^'
241                        } else {
242                            ' '
243                        };
244                        if d != pd {
245                            write!(f, "{}", if d == ' ' { "\x1B[m" } else { colorcode })?;
246                        }
247                        pd = d;
248                        match c {
249                            '\t' => write!(f, "{}{}{}{}", d, d, d, d)?,
250                            _ => write!(f, "{}", d)?,
251                        }
252                    }
253                    write!(f, "\x1B[m\n")?;
254                    colorcode = "\x1B[1m";
255                }
256                DiagSegment::Note(ref message) => {
257                    write!(f, "   = \x1B[1mnote:\x1B[m {}\n", message)?
258                }
259            }
260        }
261
262        if self.get_severity() == Severity::Bug {
263            write!(
264                f,
265                "\nYou have encountered a compiler bug. Sorry about that! We would appreciate if \
266                 you open an issue [1] and describe how you triggered the bug, together with a \
267                 minimal snippet of code to reproduce it. Thanks!\n"
268            )?;
269            write!(f, "[1]: https://github.com/fabianschuiki/moore\n")?;
270        }
271
272        Ok(())
273    }
274}