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
131
use codespan_reporting::diagnostic::{Diagnostic, Label};
use arret_syntax::span::{FileId, Span};
use crate::source::SourceLoader;
#[derive(Debug, PartialEq, Clone)]
pub struct LocTrace {
origin: Span,
macro_invocation: Option<Span>,
}
impl LocTrace {
pub fn new(origin: Span, macro_invocation: Option<Span>) -> LocTrace {
LocTrace {
origin,
macro_invocation,
}
}
pub fn with_macro_invocation(self, macro_invocation: Span) -> LocTrace {
LocTrace {
macro_invocation: Some(macro_invocation),
..self
}
}
pub fn origin(&self) -> Span {
self.origin
}
pub fn macro_invocation(&self) -> Option<Span> {
self.macro_invocation
}
pub fn label_macro_invocation(&self, mut diagnostic: Diagnostic<FileId>) -> Diagnostic<FileId> {
match self.macro_invocation {
Some(macro_invocation_span) if !macro_invocation_span.contains(self.origin) => {
let secondary_label =
new_secondary_label(macro_invocation_span, "in this macro invocation");
diagnostic.labels.push(secondary_label);
diagnostic
}
_ => diagnostic,
}
}
}
impl From<Span> for LocTrace {
fn from(span: Span) -> LocTrace {
LocTrace::new(span, None)
}
}
pub fn errors_to_diagnostics<E: Into<Diagnostic<FileId>>>(
errors: Vec<E>,
) -> Vec<Diagnostic<FileId>> {
errors.into_iter().map(Into::into).collect()
}
pub fn diagnostic_for_syntax_error(error: &arret_syntax::error::Error) -> Diagnostic<FileId> {
let origin = error.span();
let within = error.kind().within_context();
let primary_label_message = within
.and_then(|within| within.expected_next())
.map(|en| en.description())
.unwrap_or_else(|| "syntax error".to_owned());
let primary_label = new_primary_label(origin, primary_label_message);
let diagnostic = Diagnostic::error()
.with_message(error.kind().message())
.with_labels(vec![]);
if let Some(within) = within {
if let Some(open_char_span) = within.open_char_span() {
let secondary_label = new_secondary_label(
open_char_span,
format!("{} starts here", within.description()),
);
return diagnostic.with_labels(vec![primary_label, secondary_label]);
}
}
diagnostic.with_labels(vec![primary_label])
}
pub fn new_primary_label(span: Span, message: impl Into<String>) -> Label<FileId> {
Label::primary(span.file_id().unwrap(), span.byte_range()).with_message(message)
}
pub fn new_secondary_label(span: Span, message: impl Into<String>) -> Label<FileId> {
Label::secondary(span.file_id().unwrap(), span.byte_range()).with_message(message)
}
pub fn emit_diagnostics_to_stderr(
source_loader: &SourceLoader,
diagnostics: impl IntoIterator<Item = Diagnostic<FileId>>,
) {
use codespan_reporting::term;
use termcolor::{ColorChoice, StandardStream};
let config = term::Config::default();
let stderr = StandardStream::stderr(ColorChoice::Auto);
let mut stderr_lock = stderr.lock();
for diagnostic in diagnostics {
let _ = codespan_reporting::term::emit(
&mut stderr_lock,
&config,
&source_loader.files(),
&diagnostic,
);
}
}