]>
Commit | Line | Data |
---|---|---|
e74abb32 | 1 | use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString}; |
29967ef6 XL |
2 | use crate::{Handler, Level, StashKey}; |
3 | use rustc_lint_defs::Applicability; | |
cc61c64b | 4 | |
dfeec247 | 5 | use rustc_span::{MultiSpan, Span}; |
c30ab7b3 SL |
6 | use std::fmt::{self, Debug}; |
7 | use std::ops::{Deref, DerefMut}; | |
8 | use std::thread::panicking; | |
3dfed10e | 9 | use tracing::debug; |
c30ab7b3 SL |
10 | |
11 | /// Used for emitting structured error messages and other diagnostic information. | |
0bf4aa26 XL |
12 | /// |
13 | /// If there is some state in a downstream crate you would like to | |
14 | /// access in the methods of `DiagnosticBuilder` here, consider | |
15 | /// extending `HandlerFlags`, accessed via `self.handler.flags`. | |
c30ab7b3 SL |
16 | #[must_use] |
17 | #[derive(Clone)] | |
e1599b0c XL |
18 | pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>); |
19 | ||
20 | /// This is a large type, and often used as a return value, especially within | |
21 | /// the frequently-used `PResult` type. In theory, return value optimization | |
22 | /// (RVO) should avoid unnecessary copying. In practice, it does not (at the | |
23 | /// time of writing). The split between `DiagnosticBuilder` and | |
24 | /// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls. | |
25 | #[must_use] | |
26 | #[derive(Clone)] | |
27 | struct DiagnosticBuilderInner<'a> { | |
28 | handler: &'a Handler, | |
c30ab7b3 | 29 | diagnostic: Diagnostic, |
b7449926 | 30 | allow_suggestions: bool, |
c30ab7b3 SL |
31 | } |
32 | ||
33 | /// In general, the `DiagnosticBuilder` uses deref to allow access to | |
34 | /// the fields and methods of the embedded `diagnostic` in a | |
9fa01778 | 35 | /// transparent way. *However,* many of the methods are intended to |
c30ab7b3 SL |
36 | /// be used in a chained way, and hence ought to return `self`. In |
37 | /// that case, we can't just naively forward to the method on the | |
38 | /// `diagnostic`, because the return type would be a `&Diagnostic` | |
39 | /// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes | |
40 | /// it easy to declare such methods on the builder. | |
41 | macro_rules! forward { | |
42 | // Forward pattern for &self -> &Self | |
9fa01778 XL |
43 | ( |
44 | $(#[$attrs:meta])* | |
45 | pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self | |
46 | ) => { | |
47 | $(#[$attrs])* | |
6a06907d | 48 | #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] |
c30ab7b3 SL |
49 | pub fn $n(&self, $($name: $ty),*) -> &Self { |
50 | self.diagnostic.$n($($name),*); | |
51 | self | |
6a06907d | 52 | } |
c30ab7b3 SL |
53 | }; |
54 | ||
55 | // Forward pattern for &mut self -> &mut Self | |
9fa01778 XL |
56 | ( |
57 | $(#[$attrs:meta])* | |
58 | pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self | |
59 | ) => { | |
60 | $(#[$attrs])* | |
6a06907d | 61 | #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] |
c30ab7b3 | 62 | pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { |
e1599b0c | 63 | self.0.diagnostic.$n($($name),*); |
c30ab7b3 | 64 | self |
6a06907d | 65 | } |
c30ab7b3 SL |
66 | }; |
67 | ||
5869c6ff | 68 | // Forward pattern for &mut self -> &mut Self, with generic parameters. |
9fa01778 XL |
69 | ( |
70 | $(#[$attrs:meta])* | |
5869c6ff | 71 | pub fn $n:ident<$($generic:ident: $bound:path),*>( |
9fa01778 XL |
72 | &mut self, |
73 | $($name:ident: $ty:ty),* | |
74 | $(,)? | |
75 | ) -> &mut Self | |
76 | ) => { | |
77 | $(#[$attrs])* | |
6a06907d | 78 | #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] |
5869c6ff | 79 | pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { |
e1599b0c | 80 | self.0.diagnostic.$n($($name),*); |
c30ab7b3 | 81 | self |
6a06907d | 82 | } |
c30ab7b3 SL |
83 | }; |
84 | } | |
85 | ||
86 | impl<'a> Deref for DiagnosticBuilder<'a> { | |
87 | type Target = Diagnostic; | |
88 | ||
89 | fn deref(&self) -> &Diagnostic { | |
e1599b0c | 90 | &self.0.diagnostic |
c30ab7b3 SL |
91 | } |
92 | } | |
93 | ||
94 | impl<'a> DerefMut for DiagnosticBuilder<'a> { | |
95 | fn deref_mut(&mut self) -> &mut Diagnostic { | |
e1599b0c | 96 | &mut self.0.diagnostic |
c30ab7b3 SL |
97 | } |
98 | } | |
99 | ||
100 | impl<'a> DiagnosticBuilder<'a> { | |
101 | /// Emit the diagnostic. | |
102 | pub fn emit(&mut self) { | |
e1599b0c | 103 | self.0.handler.emit_diagnostic(&self); |
2c00a5a8 XL |
104 | self.cancel(); |
105 | } | |
106 | ||
48663c56 XL |
107 | /// Emit the diagnostic unless `delay` is true, |
108 | /// in which case the emission will be delayed as a bug. | |
109 | /// | |
110 | /// See `emit` and `delay_as_bug` for details. | |
111 | pub fn emit_unless(&mut self, delay: bool) { | |
74b04a01 XL |
112 | if delay { |
113 | self.delay_as_bug(); | |
114 | } else { | |
115 | self.emit(); | |
116 | } | |
48663c56 XL |
117 | } |
118 | ||
e74abb32 XL |
119 | /// Stashes diagnostic for possible later improvement in a different, |
120 | /// later stage of the compiler. The diagnostic can be accessed with | |
fc512014 | 121 | /// the provided `span` and `key` through [`Handler::steal_diagnostic()`]. |
e74abb32 XL |
122 | /// |
123 | /// As with `buffer`, this is unless the handler has disabled such buffering. | |
124 | pub fn stash(self, span: Span, key: StashKey) { | |
125 | if let Some((diag, handler)) = self.into_diagnostic() { | |
126 | handler.stash_diagnostic(span, key, diag); | |
127 | } | |
128 | } | |
129 | ||
130 | /// Converts the builder to a `Diagnostic` for later emission, | |
131 | /// unless handler has disabled such buffering. | |
132 | pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { | |
dfeec247 XL |
133 | if self.0.handler.flags.dont_buffer_diagnostics |
134 | || self.0.handler.flags.treat_err_as_bug.is_some() | |
532ac7d7 | 135 | { |
0bf4aa26 | 136 | self.emit(); |
e74abb32 | 137 | return None; |
0bf4aa26 XL |
138 | } |
139 | ||
e74abb32 XL |
140 | let handler = self.0.handler; |
141 | ||
dfeec247 XL |
142 | // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an |
143 | // unused diagnostic. | |
144 | let dummy = Diagnostic::new(Level::Cancelled, ""); | |
145 | let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy); | |
146 | ||
0bf4aa26 XL |
147 | // Logging here is useful to help track down where in logs an error was |
148 | // actually emitted. | |
149 | debug!("buffer: diagnostic={:?}", diagnostic); | |
e74abb32 XL |
150 | |
151 | Some((diagnostic, handler)) | |
152 | } | |
153 | ||
154 | /// Buffers the diagnostic for later emission, | |
155 | /// unless handler has disabled such buffering. | |
156 | pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) { | |
157 | buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); | |
c30ab7b3 SL |
158 | } |
159 | ||
3b2f2976 XL |
160 | /// Delay emission of this diagnostic as a bug. |
161 | /// | |
162 | /// This can be useful in contexts where an error indicates a bug but | |
163 | /// typically this only happens when other compilation errors have already | |
164 | /// happened. In those cases this can be used to defer emission of this | |
165 | /// diagnostic as a bug in the compiler only if no other errors have been | |
166 | /// emitted. | |
167 | /// | |
168 | /// In the meantime, though, callsites are required to deal with the "bug" | |
169 | /// locally in whichever way makes the most sense. | |
170 | pub fn delay_as_bug(&mut self) { | |
171 | self.level = Level::Bug; | |
e1599b0c | 172 | self.0.handler.delay_as_bug(self.0.diagnostic.clone()); |
3b2f2976 XL |
173 | self.cancel(); |
174 | } | |
175 | ||
fc512014 | 176 | /// Appends a labeled span to the diagnostic. |
3dfed10e | 177 | /// |
fc512014 XL |
178 | /// Labels are used to convey additional context for the diagnostic's primary span. They will |
179 | /// be shown together with the original diagnostic's span, *not* with spans added by | |
180 | /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because | |
181 | /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed | |
182 | /// either. | |
3dfed10e | 183 | /// |
fc512014 XL |
184 | /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when |
185 | /// the diagnostic was constructed. However, the label span is *not* considered a | |
186 | /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is | |
187 | /// primary. | |
74b04a01 | 188 | pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self { |
e1599b0c | 189 | self.0.diagnostic.span_label(span, label); |
7cac9316 XL |
190 | self |
191 | } | |
c30ab7b3 | 192 | |
74b04a01 | 193 | /// Labels all the given spans with the provided label. |
fc512014 | 194 | /// See [`Diagnostic::span_label()`] for more information. |
74b04a01 XL |
195 | pub fn span_labels( |
196 | &mut self, | |
197 | spans: impl IntoIterator<Item = Span>, | |
198 | label: impl AsRef<str>, | |
199 | ) -> &mut Self { | |
200 | let label = label.as_ref(); | |
201 | for span in spans { | |
202 | self.0.diagnostic.span_label(span, label); | |
203 | } | |
204 | self | |
205 | } | |
206 | ||
60c5eb7d XL |
207 | forward!(pub fn note_expected_found( |
208 | &mut self, | |
209 | expected_label: &dyn fmt::Display, | |
210 | expected: DiagnosticStyledString, | |
211 | found_label: &dyn fmt::Display, | |
212 | found: DiagnosticStyledString, | |
213 | ) -> &mut Self); | |
c30ab7b3 | 214 | |
60c5eb7d XL |
215 | forward!(pub fn note_expected_found_extra( |
216 | &mut self, | |
217 | expected_label: &dyn fmt::Display, | |
218 | expected: DiagnosticStyledString, | |
219 | found_label: &dyn fmt::Display, | |
220 | found: DiagnosticStyledString, | |
221 | expected_extra: &dyn fmt::Display, | |
222 | found_extra: &dyn fmt::Display, | |
223 | ) -> &mut Self); | |
c30ab7b3 | 224 | |
fc512014 | 225 | forward!(pub fn note_unsuccessful_coercion( |
60c5eb7d XL |
226 | &mut self, |
227 | expected: DiagnosticStyledString, | |
228 | found: DiagnosticStyledString, | |
229 | ) -> &mut Self); | |
e74abb32 | 230 | |
c30ab7b3 | 231 | forward!(pub fn note(&mut self, msg: &str) -> &mut Self); |
60c5eb7d XL |
232 | forward!(pub fn span_note<S: Into<MultiSpan>>( |
233 | &mut self, | |
234 | sp: S, | |
235 | msg: &str, | |
236 | ) -> &mut Self); | |
c30ab7b3 SL |
237 | forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); |
238 | forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self); | |
48663c56 | 239 | forward!(pub fn help(&mut self, msg: &str) -> &mut Self); |
60c5eb7d XL |
240 | forward!(pub fn span_help<S: Into<MultiSpan>>( |
241 | &mut self, | |
242 | sp: S, | |
243 | msg: &str, | |
244 | ) -> &mut Self); | |
c295e0f8 | 245 | forward!(pub fn set_is_lint(&mut self,) -> &mut Self); |
0bf4aa26 | 246 | |
fc512014 | 247 | /// See [`Diagnostic::multipart_suggestion()`]. |
9fa01778 XL |
248 | pub fn multipart_suggestion( |
249 | &mut self, | |
250 | msg: &str, | |
251 | suggestion: Vec<(Span, String)>, | |
252 | applicability: Applicability, | |
253 | ) -> &mut Self { | |
e1599b0c | 254 | if !self.0.allow_suggestions { |
dfeec247 | 255 | return self; |
9fa01778 | 256 | } |
dfeec247 | 257 | self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability); |
c295e0f8 XL |
258 | self |
259 | } | |
260 | ||
261 | /// See [`Diagnostic::multipart_suggestion()`]. | |
262 | pub fn multipart_suggestion_verbose( | |
263 | &mut self, | |
264 | msg: &str, | |
265 | suggestion: Vec<(Span, String)>, | |
266 | applicability: Applicability, | |
267 | ) -> &mut Self { | |
268 | if !self.0.allow_suggestions { | |
269 | return self; | |
270 | } | |
271 | self.0.diagnostic.multipart_suggestion_verbose(msg, suggestion, applicability); | |
9fa01778 XL |
272 | self |
273 | } | |
0bf4aa26 | 274 | |
fc512014 | 275 | /// See [`Diagnostic::tool_only_multipart_suggestion()`]. |
9fa01778 | 276 | pub fn tool_only_multipart_suggestion( |
94b46f34 XL |
277 | &mut self, |
278 | msg: &str, | |
0bf4aa26 | 279 | suggestion: Vec<(Span, String)>, |
9fa01778 XL |
280 | applicability: Applicability, |
281 | ) -> &mut Self { | |
e1599b0c | 282 | if !self.0.allow_suggestions { |
dfeec247 | 283 | return self; |
0bf4aa26 | 284 | } |
dfeec247 | 285 | self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability); |
0bf4aa26 XL |
286 | self |
287 | } | |
288 | ||
fc512014 | 289 | /// See [`Diagnostic::span_suggestion()`]. |
9fa01778 XL |
290 | pub fn span_suggestion( |
291 | &mut self, | |
292 | sp: Span, | |
293 | msg: &str, | |
294 | suggestion: String, | |
295 | applicability: Applicability, | |
296 | ) -> &mut Self { | |
e1599b0c | 297 | if !self.0.allow_suggestions { |
dfeec247 | 298 | return self; |
b7449926 | 299 | } |
dfeec247 | 300 | self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability); |
b7449926 XL |
301 | self |
302 | } | |
303 | ||
fc512014 | 304 | /// See [`Diagnostic::span_suggestions()`]. |
9fa01778 XL |
305 | pub fn span_suggestions( |
306 | &mut self, | |
307 | sp: Span, | |
308 | msg: &str, | |
309 | suggestions: impl Iterator<Item = String>, | |
310 | applicability: Applicability, | |
311 | ) -> &mut Self { | |
e1599b0c | 312 | if !self.0.allow_suggestions { |
dfeec247 | 313 | return self; |
b7449926 | 314 | } |
dfeec247 | 315 | self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability); |
b7449926 XL |
316 | self |
317 | } | |
318 | ||
94222f64 XL |
319 | /// See [`Diagnostic::multipart_suggestions()`]. |
320 | pub fn multipart_suggestions( | |
321 | &mut self, | |
322 | msg: &str, | |
323 | suggestions: impl Iterator<Item = Vec<(Span, String)>>, | |
324 | applicability: Applicability, | |
325 | ) -> &mut Self { | |
326 | if !self.0.allow_suggestions { | |
327 | return self; | |
328 | } | |
329 | self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability); | |
330 | self | |
331 | } | |
332 | ||
fc512014 | 333 | /// See [`Diagnostic::span_suggestion_short()`]. |
9fa01778 XL |
334 | pub fn span_suggestion_short( |
335 | &mut self, | |
336 | sp: Span, | |
337 | msg: &str, | |
338 | suggestion: String, | |
339 | applicability: Applicability, | |
340 | ) -> &mut Self { | |
e1599b0c | 341 | if !self.0.allow_suggestions { |
dfeec247 | 342 | return self; |
b7449926 | 343 | } |
dfeec247 | 344 | self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability); |
b7449926 XL |
345 | self |
346 | } | |
9fa01778 | 347 | |
fc512014 | 348 | /// See [`Diagnostic::span_suggestion_verbose()`]. |
ba9703b0 XL |
349 | pub fn span_suggestion_verbose( |
350 | &mut self, | |
351 | sp: Span, | |
352 | msg: &str, | |
353 | suggestion: String, | |
354 | applicability: Applicability, | |
355 | ) -> &mut Self { | |
356 | if !self.0.allow_suggestions { | |
357 | return self; | |
358 | } | |
359 | self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability); | |
360 | self | |
361 | } | |
362 | ||
fc512014 | 363 | /// See [`Diagnostic::span_suggestion_hidden()`]. |
9fa01778 XL |
364 | pub fn span_suggestion_hidden( |
365 | &mut self, | |
366 | sp: Span, | |
367 | msg: &str, | |
368 | suggestion: String, | |
369 | applicability: Applicability, | |
370 | ) -> &mut Self { | |
e1599b0c | 371 | if !self.0.allow_suggestions { |
dfeec247 | 372 | return self; |
9fa01778 | 373 | } |
dfeec247 | 374 | self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability); |
9fa01778 XL |
375 | self |
376 | } | |
377 | ||
fc512014 | 378 | /// See [`Diagnostic::tool_only_span_suggestion()`] for more information. |
9fa01778 XL |
379 | pub fn tool_only_span_suggestion( |
380 | &mut self, | |
381 | sp: Span, | |
382 | msg: &str, | |
383 | suggestion: String, | |
384 | applicability: Applicability, | |
385 | ) -> &mut Self { | |
e1599b0c | 386 | if !self.0.allow_suggestions { |
dfeec247 | 387 | return self; |
9fa01778 | 388 | } |
dfeec247 | 389 | self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability); |
9fa01778 XL |
390 | self |
391 | } | |
392 | ||
5869c6ff | 393 | forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self); |
c30ab7b3 | 394 | forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self); |
abe05a73 | 395 | forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); |
c30ab7b3 | 396 | |
fc512014 XL |
397 | /// Allow attaching suggestions this diagnostic. |
398 | /// If this is set to `false`, then any suggestions attached with the `span_suggestion_*` | |
399 | /// methods after this is set to `false` will be ignored. | |
b7449926 | 400 | pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self { |
e1599b0c | 401 | self.0.allow_suggestions = allow; |
b7449926 XL |
402 | self |
403 | } | |
404 | ||
c30ab7b3 | 405 | /// Convenience function for internal use, clients should use one of the |
fc512014 | 406 | /// `struct_*` methods on [`Handler`]. |
e1599b0c | 407 | crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { |
c30ab7b3 SL |
408 | DiagnosticBuilder::new_with_code(handler, level, None, message) |
409 | } | |
410 | ||
411 | /// Convenience function for internal use, clients should use one of the | |
fc512014 | 412 | /// `struct_*` methods on [`Handler`]. |
dfeec247 XL |
413 | crate fn new_with_code( |
414 | handler: &'a Handler, | |
415 | level: Level, | |
416 | code: Option<DiagnosticId>, | |
417 | message: &str, | |
418 | ) -> DiagnosticBuilder<'a> { | |
3b2f2976 XL |
419 | let diagnostic = Diagnostic::new_with_code(level, code, message); |
420 | DiagnosticBuilder::new_diagnostic(handler, diagnostic) | |
c30ab7b3 SL |
421 | } |
422 | ||
3b2f2976 XL |
423 | /// Creates a new `DiagnosticBuilder` with an already constructed |
424 | /// diagnostic. | |
dfeec247 | 425 | crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { |
74b04a01 | 426 | debug!("Created new diagnostic"); |
e1599b0c | 427 | DiagnosticBuilder(Box::new(DiagnosticBuilderInner { |
b7449926 XL |
428 | handler, |
429 | diagnostic, | |
430 | allow_suggestions: true, | |
e1599b0c | 431 | })) |
c30ab7b3 SL |
432 | } |
433 | } | |
434 | ||
435 | impl<'a> Debug for DiagnosticBuilder<'a> { | |
9fa01778 | 436 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
e1599b0c | 437 | self.0.diagnostic.fmt(f) |
c30ab7b3 SL |
438 | } |
439 | } | |
440 | ||
3b2f2976 | 441 | /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled |
7cac9316 | 442 | /// or we emit a bug. |
c30ab7b3 SL |
443 | impl<'a> Drop for DiagnosticBuilder<'a> { |
444 | fn drop(&mut self) { | |
445 | if !panicking() && !self.cancelled() { | |
416331ca | 446 | let mut db = DiagnosticBuilder::new( |
e1599b0c | 447 | self.0.handler, |
416331ca XL |
448 | Level::Bug, |
449 | "the following error was constructed but not emitted", | |
450 | ); | |
c30ab7b3 | 451 | db.emit(); |
416331ca | 452 | self.emit(); |
c30ab7b3 SL |
453 | panic!(); |
454 | } | |
455 | } | |
456 | } | |
dfeec247 XL |
457 | |
458 | #[macro_export] | |
459 | macro_rules! struct_span_err { | |
460 | ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ | |
461 | $session.struct_span_err_with_code( | |
462 | $span, | |
463 | &format!($($message)*), | |
464 | $crate::error_code!($code), | |
465 | ) | |
466 | }) | |
467 | } | |
468 | ||
469 | #[macro_export] | |
470 | macro_rules! error_code { | |
471 | ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }}; | |
472 | } |