1 use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
2 use crate::{Handler, Level, StashKey};
3 use rustc_lint_defs::Applicability;
4 
5 use rustc_span::{MultiSpan, Span};
6 use std::fmt::{self, Debug};
7 use std::ops::{Deref, DerefMut};
8 use std::thread::panicking;
9 use tracing::debug;
10 
11 /// Used for emitting structured error messages and other diagnostic information.
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`.
16 #[must_use]
17 #[derive(Clone)]
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,
29     diagnostic: Diagnostic,
30     allow_suggestions: bool,
31 }
32 
33 /// In general, the `DiagnosticBuilder` uses deref to allow access to
34 /// the fields and methods of the embedded `diagnostic` in a
35 /// transparent way. *However,* many of the methods are intended to
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
43     (
44         $(#[$attrs:meta])*
45         pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
46     ) => {
47         $(#[$attrs])*
48         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
49         pub fn $n(&self, $($name: $ty),*) -> &Self {
50             self.diagnostic.$n($($name),*);
51             self
52         }
53     };
54 
55     // Forward pattern for &mut self -> &mut Self
56     (
57         $(#[$attrs:meta])*
58         pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self
59     ) => {
60         $(#[$attrs])*
61         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
62         pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
63             self.0.diagnostic.$n($($name),*);
64             self
65         }
66     };
67 
68     // Forward pattern for &mut self -> &mut Self, with generic parameters.
69     (
70         $(#[$attrs:meta])*
71         pub fn $n:ident<$($generic:ident: $bound:path),*>(
72             &mut self,
73             $($name:ident: $ty:ty),*
74             $(,)?
75         ) -> &mut Self
76     ) => {
77         $(#[$attrs])*
78         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
79         pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self {
80             self.0.diagnostic.$n($($name),*);
81             self
82         }
83     };
84 }
85 
86 impl<'a> Deref for DiagnosticBuilder<'a> {
87     type Target = Diagnostic;
88 
deref(&self) -> &Diagnostic89     fn deref(&self) -> &Diagnostic {
90         &self.0.diagnostic
91     }
92 }
93 
94 impl<'a> DerefMut for DiagnosticBuilder<'a> {
deref_mut(&mut self) -> &mut Diagnostic95     fn deref_mut(&mut self) -> &mut Diagnostic {
96         &mut self.0.diagnostic
97     }
98 }
99 
100 impl<'a> DiagnosticBuilder<'a> {
101     /// Emit the diagnostic.
emit(&mut self)102     pub fn emit(&mut self) {
103         self.0.handler.emit_diagnostic(&self);
104         self.cancel();
105     }
106 
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.
emit_unless(&mut self, delay: bool)111     pub fn emit_unless(&mut self, delay: bool) {
112         if delay {
113             self.delay_as_bug();
114         } else {
115             self.emit();
116         }
117     }
118 
119     /// Stashes diagnostic for possible later improvement in a different,
120     /// later stage of the compiler. The diagnostic can be accessed with
121     /// the provided `span` and `key` through [`Handler::steal_diagnostic()`].
122     ///
123     /// As with `buffer`, this is unless the handler has disabled such buffering.
stash(self, span: Span, key: StashKey)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.
into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)>132     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
133         if self.0.handler.flags.dont_buffer_diagnostics
134             || self.0.handler.flags.treat_err_as_bug.is_some()
135         {
136             self.emit();
137             return None;
138         }
139 
140         let handler = self.0.handler;
141 
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 
147         // Logging here is useful to help track down where in logs an error was
148         // actually emitted.
149         debug!("buffer: diagnostic={:?}", diagnostic);
150 
151         Some((diagnostic, handler))
152     }
153 
154     /// Buffers the diagnostic for later emission,
155     /// unless handler has disabled such buffering.
buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>)156     pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
157         buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
158     }
159 
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.
delay_as_bug(&mut self)170     pub fn delay_as_bug(&mut self) {
171         self.level = Level::Bug;
172         self.0.handler.delay_as_bug(self.0.diagnostic.clone());
173         self.cancel();
174     }
175 
176     /// Appends a labeled span to the diagnostic.
177     ///
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.
183     ///
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.
span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self188     pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
189         self.0.diagnostic.span_label(span, label);
190         self
191     }
192 
193     /// Labels all the given spans with the provided label.
194     /// See [`Diagnostic::span_label()`] for more information.
span_labels( &mut self, spans: impl IntoIterator<Item = Span>, label: impl AsRef<str>, ) -> &mut Self195     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 
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);
214 
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);
224 
225     forward!(pub fn note_unsuccessful_coercion(
226         &mut self,
227         expected: DiagnosticStyledString,
228         found: DiagnosticStyledString,
229     ) -> &mut Self);
230 
231     forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
232     forward!(pub fn span_note<S: Into<MultiSpan>>(
233         &mut self,
234         sp: S,
235         msg: &str,
236     ) -> &mut Self);
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);
239     forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
240     forward!(pub fn span_help<S: Into<MultiSpan>>(
241         &mut self,
242         sp: S,
243         msg: &str,
244     ) -> &mut Self);
245 
246     /// See [`Diagnostic::multipart_suggestion()`].
multipart_suggestion( &mut self, msg: &str, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self247     pub fn multipart_suggestion(
248         &mut self,
249         msg: &str,
250         suggestion: Vec<(Span, String)>,
251         applicability: Applicability,
252     ) -> &mut Self {
253         if !self.0.allow_suggestions {
254             return self;
255         }
256         self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability);
257         self
258     }
259 
260     /// See [`Diagnostic::tool_only_multipart_suggestion()`].
tool_only_multipart_suggestion( &mut self, msg: &str, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self261     pub fn tool_only_multipart_suggestion(
262         &mut self,
263         msg: &str,
264         suggestion: Vec<(Span, String)>,
265         applicability: Applicability,
266     ) -> &mut Self {
267         if !self.0.allow_suggestions {
268             return self;
269         }
270         self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability);
271         self
272     }
273 
274     /// See [`Diagnostic::span_suggestion()`].
span_suggestion( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, ) -> &mut Self275     pub fn span_suggestion(
276         &mut self,
277         sp: Span,
278         msg: &str,
279         suggestion: String,
280         applicability: Applicability,
281     ) -> &mut Self {
282         if !self.0.allow_suggestions {
283             return self;
284         }
285         self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability);
286         self
287     }
288 
289     /// See [`Diagnostic::span_suggestions()`].
span_suggestions( &mut self, sp: Span, msg: &str, suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self290     pub fn span_suggestions(
291         &mut self,
292         sp: Span,
293         msg: &str,
294         suggestions: impl Iterator<Item = String>,
295         applicability: Applicability,
296     ) -> &mut Self {
297         if !self.0.allow_suggestions {
298             return self;
299         }
300         self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability);
301         self
302     }
303 
304     /// See [`Diagnostic::span_suggestion_short()`].
span_suggestion_short( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, ) -> &mut Self305     pub fn span_suggestion_short(
306         &mut self,
307         sp: Span,
308         msg: &str,
309         suggestion: String,
310         applicability: Applicability,
311     ) -> &mut Self {
312         if !self.0.allow_suggestions {
313             return self;
314         }
315         self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability);
316         self
317     }
318 
319     /// See [`Diagnostic::span_suggestion_verbose()`].
span_suggestion_verbose( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, ) -> &mut Self320     pub fn span_suggestion_verbose(
321         &mut self,
322         sp: Span,
323         msg: &str,
324         suggestion: String,
325         applicability: Applicability,
326     ) -> &mut Self {
327         if !self.0.allow_suggestions {
328             return self;
329         }
330         self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability);
331         self
332     }
333 
334     /// See [`Diagnostic::span_suggestion_hidden()`].
span_suggestion_hidden( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, ) -> &mut Self335     pub fn span_suggestion_hidden(
336         &mut self,
337         sp: Span,
338         msg: &str,
339         suggestion: String,
340         applicability: Applicability,
341     ) -> &mut Self {
342         if !self.0.allow_suggestions {
343             return self;
344         }
345         self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability);
346         self
347     }
348 
349     /// See [`Diagnostic::tool_only_span_suggestion()`] for more information.
tool_only_span_suggestion( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, ) -> &mut Self350     pub fn tool_only_span_suggestion(
351         &mut self,
352         sp: Span,
353         msg: &str,
354         suggestion: String,
355         applicability: Applicability,
356     ) -> &mut Self {
357         if !self.0.allow_suggestions {
358             return self;
359         }
360         self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability);
361         self
362     }
363 
364     forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self);
365     forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
366     forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
367 
368     /// Allow attaching suggestions this diagnostic.
369     /// If this is set to `false`, then any suggestions attached with the `span_suggestion_*`
370     /// methods after this is set to `false` will be ignored.
allow_suggestions(&mut self, allow: bool) -> &mut Self371     pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
372         self.0.allow_suggestions = allow;
373         self
374     }
375 
376     /// Convenience function for internal use, clients should use one of the
377     /// `struct_*` methods on [`Handler`].
new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a>378     crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
379         DiagnosticBuilder::new_with_code(handler, level, None, message)
380     }
381 
382     /// Convenience function for internal use, clients should use one of the
383     /// `struct_*` methods on [`Handler`].
new_with_code( handler: &'a Handler, level: Level, code: Option<DiagnosticId>, message: &str, ) -> DiagnosticBuilder<'a>384     crate fn new_with_code(
385         handler: &'a Handler,
386         level: Level,
387         code: Option<DiagnosticId>,
388         message: &str,
389     ) -> DiagnosticBuilder<'a> {
390         let diagnostic = Diagnostic::new_with_code(level, code, message);
391         DiagnosticBuilder::new_diagnostic(handler, diagnostic)
392     }
393 
394     /// Creates a new `DiagnosticBuilder` with an already constructed
395     /// diagnostic.
new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a>396     crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
397         debug!("Created new diagnostic");
398         DiagnosticBuilder(Box::new(DiagnosticBuilderInner {
399             handler,
400             diagnostic,
401             allow_suggestions: true,
402         }))
403     }
404 }
405 
406 impl<'a> Debug for DiagnosticBuilder<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result407     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408         self.0.diagnostic.fmt(f)
409     }
410 }
411 
412 /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
413 /// or we emit a bug.
414 impl<'a> Drop for DiagnosticBuilder<'a> {
drop(&mut self)415     fn drop(&mut self) {
416         if !panicking() && !self.cancelled() {
417             let mut db = DiagnosticBuilder::new(
418                 self.0.handler,
419                 Level::Bug,
420                 "the following error was constructed but not emitted",
421             );
422             db.emit();
423             self.emit();
424             panic!();
425         }
426     }
427 }
428 
429 #[macro_export]
430 macro_rules! struct_span_err {
431     ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
432         $session.struct_span_err_with_code(
433             $span,
434             &format!($($message)*),
435             $crate::error_code!($code),
436         )
437     })
438 }
439 
440 #[macro_export]
441 macro_rules! error_code {
442     ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }};
443 }
444