]>
Commit | Line | Data |
---|---|---|
9fa01778 XL |
1 | use crate::Diagnostic; |
2 | use crate::DiagnosticId; | |
3 | use crate::DiagnosticStyledString; | |
4 | use crate::Applicability; | |
cc61c64b | 5 | |
9fa01778 XL |
6 | use crate::Level; |
7 | use crate::Handler; | |
c30ab7b3 SL |
8 | use std::fmt::{self, Debug}; |
9 | use std::ops::{Deref, DerefMut}; | |
10 | use std::thread::panicking; | |
11 | use syntax_pos::{MultiSpan, Span}; | |
9fa01778 | 12 | use log::debug; |
c30ab7b3 SL |
13 | |
14 | /// Used for emitting structured error messages and other diagnostic information. | |
0bf4aa26 XL |
15 | /// |
16 | /// If there is some state in a downstream crate you would like to | |
17 | /// access in the methods of `DiagnosticBuilder` here, consider | |
18 | /// extending `HandlerFlags`, accessed via `self.handler.flags`. | |
c30ab7b3 SL |
19 | #[must_use] |
20 | #[derive(Clone)] | |
21 | pub struct DiagnosticBuilder<'a> { | |
ff7c6d11 | 22 | pub handler: &'a Handler, |
c30ab7b3 | 23 | diagnostic: Diagnostic, |
b7449926 | 24 | allow_suggestions: bool, |
c30ab7b3 SL |
25 | } |
26 | ||
27 | /// In general, the `DiagnosticBuilder` uses deref to allow access to | |
28 | /// the fields and methods of the embedded `diagnostic` in a | |
9fa01778 | 29 | /// transparent way. *However,* many of the methods are intended to |
c30ab7b3 SL |
30 | /// be used in a chained way, and hence ought to return `self`. In |
31 | /// that case, we can't just naively forward to the method on the | |
32 | /// `diagnostic`, because the return type would be a `&Diagnostic` | |
33 | /// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes | |
34 | /// it easy to declare such methods on the builder. | |
35 | macro_rules! forward { | |
36 | // Forward pattern for &self -> &Self | |
9fa01778 XL |
37 | ( |
38 | $(#[$attrs:meta])* | |
39 | pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self | |
40 | ) => { | |
41 | $(#[$attrs])* | |
c30ab7b3 SL |
42 | pub fn $n(&self, $($name: $ty),*) -> &Self { |
43 | self.diagnostic.$n($($name),*); | |
44 | self | |
45 | } | |
46 | }; | |
47 | ||
48 | // Forward pattern for &mut self -> &mut Self | |
9fa01778 XL |
49 | ( |
50 | $(#[$attrs:meta])* | |
51 | pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self | |
52 | ) => { | |
53 | $(#[$attrs])* | |
c30ab7b3 SL |
54 | pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { |
55 | self.diagnostic.$n($($name),*); | |
56 | self | |
57 | } | |
58 | }; | |
59 | ||
60 | // Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan> | |
61 | // type parameter. No obvious way to make this more generic. | |
9fa01778 XL |
62 | ( |
63 | $(#[$attrs:meta])* | |
64 | pub fn $n:ident<S: Into<MultiSpan>>( | |
65 | &mut self, | |
66 | $($name:ident: $ty:ty),* | |
67 | $(,)? | |
68 | ) -> &mut Self | |
69 | ) => { | |
70 | $(#[$attrs])* | |
c30ab7b3 SL |
71 | pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self { |
72 | self.diagnostic.$n($($name),*); | |
73 | self | |
74 | } | |
75 | }; | |
76 | } | |
77 | ||
78 | impl<'a> Deref for DiagnosticBuilder<'a> { | |
79 | type Target = Diagnostic; | |
80 | ||
81 | fn deref(&self) -> &Diagnostic { | |
82 | &self.diagnostic | |
83 | } | |
84 | } | |
85 | ||
86 | impl<'a> DerefMut for DiagnosticBuilder<'a> { | |
87 | fn deref_mut(&mut self) -> &mut Diagnostic { | |
88 | &mut self.diagnostic | |
89 | } | |
90 | } | |
91 | ||
92 | impl<'a> DiagnosticBuilder<'a> { | |
93 | /// Emit the diagnostic. | |
94 | pub fn emit(&mut self) { | |
95 | if self.cancelled() { | |
96 | return; | |
97 | } | |
98 | ||
2c00a5a8 XL |
99 | self.handler.emit_db(&self); |
100 | self.cancel(); | |
101 | } | |
102 | ||
48663c56 XL |
103 | /// Emit the diagnostic unless `delay` is true, |
104 | /// in which case the emission will be delayed as a bug. | |
105 | /// | |
106 | /// See `emit` and `delay_as_bug` for details. | |
107 | pub fn emit_unless(&mut self, delay: bool) { | |
108 | if delay { | |
109 | self.delay_as_bug() | |
110 | } else { | |
111 | self.emit() | |
112 | } | |
113 | } | |
114 | ||
0bf4aa26 XL |
115 | /// Buffers the diagnostic for later emission, unless handler |
116 | /// has disabled such buffering. | |
117 | pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) { | |
532ac7d7 XL |
118 | if self.handler.flags.dont_buffer_diagnostics || |
119 | self.handler.flags.treat_err_as_bug.is_some() | |
120 | { | |
0bf4aa26 XL |
121 | self.emit(); |
122 | return; | |
123 | } | |
124 | ||
8faf50e0 XL |
125 | // We need to use `ptr::read` because `DiagnosticBuilder` |
126 | // implements `Drop`. | |
127 | let diagnostic; | |
128 | unsafe { | |
9fa01778 XL |
129 | diagnostic = std::ptr::read(&self.diagnostic); |
130 | std::mem::forget(self); | |
8faf50e0 | 131 | }; |
0bf4aa26 XL |
132 | // Logging here is useful to help track down where in logs an error was |
133 | // actually emitted. | |
134 | debug!("buffer: diagnostic={:?}", diagnostic); | |
8faf50e0 | 135 | buffered_diagnostics.push(diagnostic); |
c30ab7b3 SL |
136 | } |
137 | ||
ea8adc8c XL |
138 | /// Convenience function for internal use, clients should use one of the |
139 | /// span_* methods instead. | |
140 | pub fn sub<S: Into<MultiSpan>>( | |
141 | &mut self, | |
142 | level: Level, | |
143 | message: &str, | |
144 | span: Option<S>, | |
145 | ) -> &mut Self { | |
0bf4aa26 | 146 | let span = span.map(|s| s.into()).unwrap_or_else(|| MultiSpan::new()); |
ea8adc8c XL |
147 | self.diagnostic.sub(level, message, span, None); |
148 | self | |
149 | } | |
150 | ||
3b2f2976 XL |
151 | /// Delay emission of this diagnostic as a bug. |
152 | /// | |
153 | /// This can be useful in contexts where an error indicates a bug but | |
154 | /// typically this only happens when other compilation errors have already | |
155 | /// happened. In those cases this can be used to defer emission of this | |
156 | /// diagnostic as a bug in the compiler only if no other errors have been | |
157 | /// emitted. | |
158 | /// | |
159 | /// In the meantime, though, callsites are required to deal with the "bug" | |
160 | /// locally in whichever way makes the most sense. | |
161 | pub fn delay_as_bug(&mut self) { | |
162 | self.level = Level::Bug; | |
8faf50e0 | 163 | self.handler.delay_as_bug(self.diagnostic.clone()); |
3b2f2976 XL |
164 | self.cancel(); |
165 | } | |
166 | ||
9fa01778 | 167 | /// Adds a span/label to be included in the resulting snippet. |
c30ab7b3 SL |
168 | /// This is pushed onto the `MultiSpan` that was created when the |
169 | /// diagnostic was first built. If you don't call this function at | |
170 | /// all, and you just supplied a `Span` to create the diagnostic, | |
171 | /// then the snippet will just include that `Span`, which is | |
172 | /// called the primary span. | |
7cac9316 XL |
173 | pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self { |
174 | self.diagnostic.span_label(span, label); | |
175 | self | |
176 | } | |
c30ab7b3 SL |
177 | |
178 | forward!(pub fn note_expected_found(&mut self, | |
8faf50e0 | 179 | label: &dyn fmt::Display, |
cc61c64b | 180 | expected: DiagnosticStyledString, |
0bf4aa26 XL |
181 | found: DiagnosticStyledString, |
182 | ) -> &mut Self); | |
c30ab7b3 SL |
183 | |
184 | forward!(pub fn note_expected_found_extra(&mut self, | |
8faf50e0 | 185 | label: &dyn fmt::Display, |
cc61c64b XL |
186 | expected: DiagnosticStyledString, |
187 | found: DiagnosticStyledString, | |
8faf50e0 | 188 | expected_extra: &dyn fmt::Display, |
0bf4aa26 XL |
189 | found_extra: &dyn fmt::Display, |
190 | ) -> &mut Self); | |
c30ab7b3 SL |
191 | |
192 | forward!(pub fn note(&mut self, msg: &str) -> &mut Self); | |
193 | forward!(pub fn span_note<S: Into<MultiSpan>>(&mut self, | |
194 | sp: S, | |
0bf4aa26 XL |
195 | msg: &str, |
196 | ) -> &mut Self); | |
c30ab7b3 SL |
197 | forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); |
198 | forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self); | |
48663c56 | 199 | forward!(pub fn help(&mut self, msg: &str) -> &mut Self); |
c30ab7b3 SL |
200 | forward!(pub fn span_help<S: Into<MultiSpan>>(&mut self, |
201 | sp: S, | |
0bf4aa26 XL |
202 | msg: &str, |
203 | ) -> &mut Self); | |
204 | ||
9fa01778 XL |
205 | pub fn multipart_suggestion( |
206 | &mut self, | |
207 | msg: &str, | |
208 | suggestion: Vec<(Span, String)>, | |
209 | applicability: Applicability, | |
210 | ) -> &mut Self { | |
211 | if !self.allow_suggestions { | |
212 | return self | |
213 | } | |
214 | self.diagnostic.multipart_suggestion( | |
215 | msg, | |
216 | suggestion, | |
217 | applicability, | |
218 | ); | |
219 | self | |
220 | } | |
0bf4aa26 | 221 | |
9fa01778 | 222 | pub fn tool_only_multipart_suggestion( |
94b46f34 XL |
223 | &mut self, |
224 | msg: &str, | |
0bf4aa26 | 225 | suggestion: Vec<(Span, String)>, |
9fa01778 XL |
226 | applicability: Applicability, |
227 | ) -> &mut Self { | |
0bf4aa26 XL |
228 | if !self.allow_suggestions { |
229 | return self | |
230 | } | |
9fa01778 | 231 | self.diagnostic.tool_only_multipart_suggestion( |
0bf4aa26 XL |
232 | msg, |
233 | suggestion, | |
234 | applicability, | |
235 | ); | |
236 | self | |
237 | } | |
238 | ||
9fa01778 XL |
239 | |
240 | pub fn span_suggestion( | |
241 | &mut self, | |
242 | sp: Span, | |
243 | msg: &str, | |
244 | suggestion: String, | |
245 | applicability: Applicability, | |
246 | ) -> &mut Self { | |
b7449926 XL |
247 | if !self.allow_suggestions { |
248 | return self | |
249 | } | |
9fa01778 | 250 | self.diagnostic.span_suggestion( |
b7449926 XL |
251 | sp, |
252 | msg, | |
253 | suggestion, | |
254 | applicability, | |
255 | ); | |
256 | self | |
257 | } | |
258 | ||
9fa01778 XL |
259 | pub fn span_suggestions( |
260 | &mut self, | |
261 | sp: Span, | |
262 | msg: &str, | |
263 | suggestions: impl Iterator<Item = String>, | |
264 | applicability: Applicability, | |
265 | ) -> &mut Self { | |
b7449926 XL |
266 | if !self.allow_suggestions { |
267 | return self | |
268 | } | |
9fa01778 | 269 | self.diagnostic.span_suggestions( |
b7449926 XL |
270 | sp, |
271 | msg, | |
272 | suggestions, | |
273 | applicability, | |
274 | ); | |
275 | self | |
276 | } | |
277 | ||
9fa01778 XL |
278 | pub fn span_suggestion_short( |
279 | &mut self, | |
280 | sp: Span, | |
281 | msg: &str, | |
282 | suggestion: String, | |
283 | applicability: Applicability, | |
284 | ) -> &mut Self { | |
b7449926 XL |
285 | if !self.allow_suggestions { |
286 | return self | |
287 | } | |
9fa01778 | 288 | self.diagnostic.span_suggestion_short( |
b7449926 XL |
289 | sp, |
290 | msg, | |
291 | suggestion, | |
292 | applicability, | |
293 | ); | |
294 | self | |
295 | } | |
9fa01778 XL |
296 | |
297 | pub fn span_suggestion_hidden( | |
298 | &mut self, | |
299 | sp: Span, | |
300 | msg: &str, | |
301 | suggestion: String, | |
302 | applicability: Applicability, | |
303 | ) -> &mut Self { | |
304 | if !self.allow_suggestions { | |
305 | return self | |
306 | } | |
307 | self.diagnostic.span_suggestion_hidden( | |
308 | sp, | |
309 | msg, | |
310 | suggestion, | |
311 | applicability, | |
312 | ); | |
313 | self | |
314 | } | |
315 | ||
316 | pub fn tool_only_span_suggestion( | |
317 | &mut self, | |
318 | sp: Span, | |
319 | msg: &str, | |
320 | suggestion: String, | |
321 | applicability: Applicability, | |
322 | ) -> &mut Self { | |
323 | if !self.allow_suggestions { | |
324 | return self | |
325 | } | |
326 | self.diagnostic.tool_only_span_suggestion( | |
327 | sp, | |
328 | msg, | |
329 | suggestion, | |
330 | applicability, | |
331 | ); | |
332 | self | |
333 | } | |
334 | ||
c30ab7b3 | 335 | forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self); |
abe05a73 | 336 | forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); |
c30ab7b3 | 337 | |
b7449926 XL |
338 | pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self { |
339 | self.allow_suggestions = allow; | |
340 | self | |
341 | } | |
342 | ||
c30ab7b3 SL |
343 | /// Convenience function for internal use, clients should use one of the |
344 | /// struct_* methods on Handler. | |
345 | pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { | |
346 | DiagnosticBuilder::new_with_code(handler, level, None, message) | |
347 | } | |
348 | ||
349 | /// Convenience function for internal use, clients should use one of the | |
350 | /// struct_* methods on Handler. | |
dc9dc135 | 351 | crate fn new_with_code(handler: &'a Handler, |
c30ab7b3 | 352 | level: Level, |
abe05a73 | 353 | code: Option<DiagnosticId>, |
c30ab7b3 SL |
354 | message: &str) |
355 | -> DiagnosticBuilder<'a> { | |
3b2f2976 XL |
356 | let diagnostic = Diagnostic::new_with_code(level, code, message); |
357 | DiagnosticBuilder::new_diagnostic(handler, diagnostic) | |
c30ab7b3 SL |
358 | } |
359 | ||
3b2f2976 XL |
360 | /// Creates a new `DiagnosticBuilder` with an already constructed |
361 | /// diagnostic. | |
362 | pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) | |
363 | -> DiagnosticBuilder<'a> { | |
b7449926 XL |
364 | DiagnosticBuilder { |
365 | handler, | |
366 | diagnostic, | |
367 | allow_suggestions: true, | |
368 | } | |
c30ab7b3 SL |
369 | } |
370 | } | |
371 | ||
372 | impl<'a> Debug for DiagnosticBuilder<'a> { | |
9fa01778 | 373 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
c30ab7b3 SL |
374 | self.diagnostic.fmt(f) |
375 | } | |
376 | } | |
377 | ||
3b2f2976 | 378 | /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled |
7cac9316 | 379 | /// or we emit a bug. |
c30ab7b3 SL |
380 | impl<'a> Drop for DiagnosticBuilder<'a> { |
381 | fn drop(&mut self) { | |
382 | if !panicking() && !self.cancelled() { | |
383 | let mut db = DiagnosticBuilder::new(self.handler, | |
384 | Level::Bug, | |
385 | "Error constructed but not emitted"); | |
386 | db.emit(); | |
387 | panic!(); | |
388 | } | |
389 | } | |
390 | } |