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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use crate::*;
/// Constructs an in-flight diagnostic using the builder pattern
pub struct InFlightDiagnostic<'h> {
handler: &'h DiagnosticsHandler,
file_id: Option<SourceId>,
diagnostic: Diagnostic,
severity: Severity,
}
impl<'h> InFlightDiagnostic<'h> {
pub(crate) fn new(handler: &'h DiagnosticsHandler, severity: Severity) -> Self {
Self {
handler,
file_id: None,
diagnostic: Diagnostic::new(severity),
severity,
}
}
/// Returns the severity level of this diagnostic
pub fn severity(&self) -> Severity {
self.severity
}
/// Returns whether this diagnostic should be generated
/// with verbose detail. Intended to be used when building
/// diagnostics in-flight by formatting functions which do
/// not know what the current diagnostic configuration is
pub fn verbose(&self) -> bool {
use crate::term::DisplayStyle;
matches!(self.handler.display.display_style, DisplayStyle::Rich)
}
/// Sets the current source file to which this diagnostic applies
pub fn set_source_file(mut self, filename: impl Into<FileName>) -> Self {
let filename = filename.into();
let file_id = self.handler.codemap.get_file_id(&filename);
self.file_id = file_id;
self
}
/// Sets the diagnostic message to `message`
pub fn with_message(mut self, message: impl ToString) -> Self {
self.diagnostic.message = message.to_string();
self
}
/// Adds a primary label for `span` to this diagnostic, with no label message.
pub fn with_primary_span(mut self, span: SourceSpan) -> Self {
self.diagnostic
.labels
.push(Label::primary(span.source_id(), span));
self
}
/// Adds a primary label for `span` to this diagnostic, with the given message
///
/// A primary label is one which should be rendered as the relevant source code
/// at which a diagnostic originates. Secondary labels are used for related items
/// involved in the diagnostic.
pub fn with_primary_label(mut self, span: SourceSpan, message: impl ToString) -> Self {
self.diagnostic
.labels
.push(Label::primary(span.source_id(), span).with_message(message.to_string()));
self
}
/// Adds a secondary label for `span` to this diagnostic, with the given message
///
/// A secondary label is used to point out related items in the source code which
/// are relevant to the diagnostic, but which are not themselves the point at which
/// the diagnostic originates.
pub fn with_secondary_label(mut self, span: SourceSpan, message: impl ToString) -> Self {
self.diagnostic
.labels
.push(Label::secondary(span.source_id(), span).with_message(message.to_string()));
self
}
/// Like `with_primary_label`, but rather than a [SourceSpan], it accepts a
/// line and column number, which will be mapped to an appropriate span by
/// the [CodeMap].
pub fn with_primary_label_line_and_col(
self,
line: u32,
column: u32,
message: Option<String>,
) -> Self {
let file_id = self.file_id;
self.with_label_and_file_id(LabelStyle::Primary, file_id, line, column, message)
}
/// This is a lower-level function for adding labels to diagnostics, providing
/// full control over its style, content, and location in the source code.
pub fn with_label(
self,
style: LabelStyle,
filename: Option<FileName>,
line: u32,
column: u32,
message: Option<String>,
) -> Self {
if let Some(name) = filename {
let id = self.handler.lookup_file_id(name);
self.with_label_and_file_id(style, id, line, column, message)
} else {
self
}
}
fn with_label_and_file_id(
mut self,
style: LabelStyle,
file_id: Option<SourceId>,
line: u32,
_column: u32,
message: Option<String>,
) -> Self {
if let Some(id) = file_id {
let source_file = self.handler.codemap.get(id).unwrap();
let line_index = (line - 1).into();
let span = source_file
.line_span(line_index)
.expect("invalid line index");
let label = if let Some(msg) = message {
Label::new(style, id, span).with_message(msg)
} else {
Label::new(style, id, span)
};
self.diagnostic.labels.push(label);
self
} else {
self
}
}
/// Adds a note to the diagnostic
///
/// Notes are used for explaining general concepts or suggestions
/// related to a diagnostic, and are not associated with any particular
/// source location. They are always rendered after the other diagnostic
/// content.
pub fn with_note(mut self, note: impl ToString) -> Self {
self.diagnostic.notes.push(note.to_string());
self
}
/// Like `with_note`, but is intended for use cases where the
/// fluent/builder pattern used here is cumbersome.
pub fn add_note(&mut self, note: impl ToString) {
self.diagnostic.notes.push(note.to_string());
}
/// Consume this [InFlightDiagnostic] and extract the underlying [Diagnostic]
pub fn take(self) -> Diagnostic {
self.diagnostic
}
/// Emit the underlying [Diagnostic] via the [DiagnosticHandler]
pub fn emit(self) {
self.handler.emit(self.diagnostic);
}
}