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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
//! Diagnostics.
use std::fmt::{self, Display, Formatter};
use crate::syntax::{Span, Spanned};
/// Early-return with a [`TypError`].
#[macro_export]
macro_rules! bail {
($($tts:tt)*) => {
return Err($crate::error!($($tts)*).into())
};
}
/// Construct a [`TypError`].
#[macro_export]
macro_rules! error {
($span:expr, $message:expr $(,)?) => {
Box::new(vec![$crate::diag::Error::new($span, $message)])
};
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
$crate::error!($span, format!($fmt, $($arg),+))
};
}
/// The result type for typesetting and all its subpasses.
pub type TypResult<T> = Result<T, TypError>;
/// The error type for typesetting and all its subpasses.
pub type TypError = 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)]
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(),
}
}
}
/// A part of an error's [trace](Error::trace).
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
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) => {
write!(f, "error occured in this function call")
}
Tracepoint::Import => {
write!(f, "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, S> At<T> for Result<T, S>
where
S: Into<String>,
{
fn at(self, span: Span) -> TypResult<T> {
self.map_err(|message| error!(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.surrounds(error.span) {
error.trace.push(Spanned::new(make_point(), span));
}
}
errors
})
}
}
/// Transform `expected X, found Y` into `expected X or A, found Y`.
pub fn with_alternative(msg: String, alt: &str) -> String {
let mut parts = msg.split(", found ");
if let (Some(a), Some(b)) = (parts.next(), parts.next()) {
format!("{} or {}, found {}", a, alt, b)
} else {
msg
}
}
|