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