1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
//! Diagnostics.
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::syntax::{Span, Spanned};
/// Early-return with a vec-boxed [`Error`].
#[macro_export]
macro_rules! bail {
($span:expr, $message:expr $(,)?) => {
return Err($crate::diag::Error::boxed($span, $message,))
};
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
bail!($span, format!($fmt, $($arg),+))
};
}
/// The result type for typesetting and all its subpasses.
pub type TypResult<T> = Result<T, Box<Vec<Error>>>;
/// A result type with a string error message.
pub type StrResult<T> = Result<T, String>;
/// An error in a source file.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Error {
/// The erroneous location in the source code.
pub span: Span,
/// A diagnostic message describing the problem.
pub message: String,
/// The trace of function calls leading to the error.
pub trace: Vec<Spanned<Tracepoint>>,
}
impl Error {
/// Create a new, bare error.
pub fn new(span: Span, message: impl Into<String>) -> Self {
Self {
span,
trace: vec![],
message: message.into(),
}
}
/// Create a boxed vector containing one error. The return value is suitable
/// as the `Err` variant of a [`TypResult`].
pub fn boxed(span: Span, message: impl Into<String>) -> Box<Vec<Self>> {
Box::new(vec![Self::new(span, message)])
}
}
/// A part of an error's [trace](Error::trace).
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Tracepoint {
/// A function call.
Call(Option<String>),
/// A module import.
Import,
}
impl Display for Tracepoint {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Tracepoint::Call(Some(name)) => {
write!(f, "error occured in this call of function `{}`", name)
}
Tracepoint::Call(None) => f.pad("error occured in this function call"),
Tracepoint::Import => f.pad("error occured while importing this module"),
}
}
}
/// Convert a [`StrResult`] to a [`TypResult`] by adding span information.
pub trait At<T> {
/// Add the span information.
fn at(self, span: Span) -> TypResult<T>;
}
impl<T> At<T> for StrResult<T> {
fn at(self, span: Span) -> TypResult<T> {
self.map_err(|message| Error::boxed(span, message))
}
}
/// Enrich a [`TypResult`] with a tracepoint.
pub trait Trace<T> {
/// Add the tracepoint to all errors that lie outside the `span`.
fn trace<F>(self, make_point: F, span: Span) -> Self
where
F: Fn() -> Tracepoint;
}
impl<T> Trace<T> for TypResult<T> {
fn trace<F>(self, make_point: F, span: Span) -> Self
where
F: Fn() -> Tracepoint,
{
self.map_err(|mut errors| {
for error in errors.iter_mut() {
if !span.contains(error.span) {
error.trace.push(Spanned::new(make_point(), span));
}
}
errors
})
}
}
|