]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use crate::diagnostic::IntoDiagnosticArg; |
923072b8 FG |
2 | use crate::{ |
3 | Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed, | |
9c376795 | 4 | ExplicitBug, SubdiagnosticMessage, |
923072b8 | 5 | }; |
04454e1e | 6 | use crate::{Handler, Level, MultiSpan, StashKey}; |
29967ef6 | 7 | use rustc_lint_defs::Applicability; |
2b03887a | 8 | use rustc_span::source_map::Spanned; |
cc61c64b | 9 | |
04454e1e FG |
10 | use rustc_span::Span; |
11 | use std::borrow::Cow; | |
c30ab7b3 | 12 | use std::fmt::{self, Debug}; |
5e7ed085 | 13 | use std::marker::PhantomData; |
c30ab7b3 | 14 | use std::ops::{Deref, DerefMut}; |
9c376795 | 15 | use std::panic; |
c30ab7b3 | 16 | use std::thread::panicking; |
c30ab7b3 | 17 | |
2b03887a FG |
18 | /// Trait implemented by error types. This should not be implemented manually. Instead, use |
19 | /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. | |
487cf647 | 20 | #[rustc_diagnostic_item = "IntoDiagnostic"] |
2b03887a FG |
21 | pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> { |
22 | /// Write out as a diagnostic out of `Handler`. | |
23 | #[must_use] | |
24 | fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, T>; | |
25 | } | |
26 | ||
27 | impl<'a, T, E> IntoDiagnostic<'a, E> for Spanned<T> | |
28 | where | |
29 | T: IntoDiagnostic<'a, E>, | |
30 | E: EmissionGuarantee, | |
31 | { | |
32 | fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, E> { | |
33 | let mut diag = self.node.into_diagnostic(handler); | |
34 | diag.set_span(self.span); | |
35 | diag | |
36 | } | |
37 | } | |
38 | ||
c30ab7b3 | 39 | /// Used for emitting structured error messages and other diagnostic information. |
0bf4aa26 XL |
40 | /// |
41 | /// If there is some state in a downstream crate you would like to | |
42 | /// access in the methods of `DiagnosticBuilder` here, consider | |
43 | /// extending `HandlerFlags`, accessed via `self.handler.flags`. | |
c30ab7b3 SL |
44 | #[must_use] |
45 | #[derive(Clone)] | |
5e7ed085 FG |
46 | pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> { |
47 | inner: DiagnosticBuilderInner<'a>, | |
48 | _marker: PhantomData<G>, | |
49 | } | |
50 | ||
51 | /// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it: | |
52 | /// 1. lacks the `G` parameter and therefore `DiagnosticBuilder<G1>` can be | |
53 | /// converted into `DiagnosticBuilder<G2>` while reusing the `inner` field | |
54 | /// 2. can implement the `Drop` "bomb" instead of `DiagnosticBuilder`, as it | |
55 | /// contains all of the data (`state` + `diagnostic`) of `DiagnosticBuilder` | |
56 | /// | |
57 | /// The `diagnostic` field is not `Copy` and can't be moved out of whichever | |
58 | /// type implements the `Drop` "bomb", but because of the above two facts, that | |
59 | /// never needs to happen - instead, the whole `inner: DiagnosticBuilderInner` | |
60 | /// can be moved out of a `DiagnosticBuilder` and into another. | |
61 | #[must_use] | |
62 | #[derive(Clone)] | |
63 | struct DiagnosticBuilderInner<'a> { | |
64 | state: DiagnosticBuilderState<'a>, | |
5099ac24 FG |
65 | |
66 | /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a | |
67 | /// return value, especially within the frequently-used `PResult` type. | |
68 | /// In theory, return value optimization (RVO) should avoid unnecessary | |
69 | /// copying. In practice, it does not (at the time of writing). | |
70 | diagnostic: Box<Diagnostic>, | |
c30ab7b3 SL |
71 | } |
72 | ||
5e7ed085 FG |
73 | #[derive(Clone)] |
74 | enum DiagnosticBuilderState<'a> { | |
75 | /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`. | |
76 | /// | |
77 | /// The `Diagnostic` will be emitted through this `Handler`. | |
78 | Emittable(&'a Handler), | |
79 | ||
80 | /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`. | |
81 | /// | |
82 | /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be | |
83 | /// assumed that `.emit()` was previously called, to end up in this state. | |
84 | /// | |
85 | /// While this is also used by `.cancel()`, this state is only observed by | |
86 | /// the `Drop` `impl` of `DiagnosticBuilderInner`, as `.cancel()` takes | |
87 | /// `self` by-value specifically to prevent any attempts to `.emit()`. | |
88 | /// | |
89 | // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`, | |
90 | // despite that being potentially lossy, if important information is added | |
91 | // *after* the original `.emit()` call. | |
92 | AlreadyEmittedOrDuringCancellation, | |
93 | } | |
94 | ||
95 | // `DiagnosticBuilderState` should be pointer-sized. | |
96 | rustc_data_structures::static_assert_size!( | |
97 | DiagnosticBuilderState<'_>, | |
98 | std::mem::size_of::<&Handler>() | |
99 | ); | |
100 | ||
101 | /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" | |
102 | /// (or "proof") token that the emission happened. | |
103 | pub trait EmissionGuarantee: Sized { | |
104 | /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each | |
105 | /// `impl` of `EmissionGuarantee`, to make it impossible to create a value | |
106 | /// of `Self` without actually performing the emission. | |
107 | #[track_caller] | |
108 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self; | |
f2b60f7d FG |
109 | |
110 | /// Creates a new `DiagnosticBuilder` that will return this type of guarantee. | |
111 | #[track_caller] | |
112 | fn make_diagnostic_builder( | |
113 | handler: &Handler, | |
114 | msg: impl Into<DiagnosticMessage>, | |
115 | ) -> DiagnosticBuilder<'_, Self>; | |
5e7ed085 FG |
116 | } |
117 | ||
118 | /// Private module for sealing the `IsError` helper trait. | |
119 | mod sealed_level_is_error { | |
120 | use crate::Level; | |
121 | ||
122 | /// Sealed helper trait for statically checking that a `Level` is an error. | |
923072b8 | 123 | pub(crate) trait IsError<const L: Level> {} |
5e7ed085 FG |
124 | |
125 | impl IsError<{ Level::Bug }> for () {} | |
126 | impl IsError<{ Level::DelayedBug }> for () {} | |
127 | impl IsError<{ Level::Fatal }> for () {} | |
128 | // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints | |
129 | // don't need error guarantees, as their levels are always dynamic. | |
130 | impl IsError<{ Level::Error { lint: false } }> for () {} | |
131 | } | |
132 | ||
133 | impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { | |
134 | /// Convenience function for internal use, clients should use one of the | |
135 | /// `struct_*` methods on [`Handler`]. | |
487cf647 | 136 | #[track_caller] |
923072b8 | 137 | pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>( |
04454e1e FG |
138 | handler: &'a Handler, |
139 | message: M, | |
140 | ) -> Self | |
5e7ed085 FG |
141 | where |
142 | (): sealed_level_is_error::IsError<L>, | |
143 | { | |
144 | Self { | |
145 | inner: DiagnosticBuilderInner { | |
146 | state: DiagnosticBuilderState::Emittable(handler), | |
147 | diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)), | |
148 | }, | |
149 | _marker: PhantomData, | |
150 | } | |
151 | } | |
152 | ||
153 | /// Discard the guarantee `.emit()` would return, in favor of having the | |
154 | /// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there | |
155 | /// is a common codepath handling both errors and warnings. | |
156 | pub fn forget_guarantee(self) -> DiagnosticBuilder<'a, ()> { | |
157 | DiagnosticBuilder { inner: self.inner, _marker: PhantomData } | |
158 | } | |
159 | } | |
160 | ||
161 | // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. | |
162 | impl EmissionGuarantee for ErrorGuaranteed { | |
163 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
164 | match db.inner.state { | |
165 | // First `.emit()` call, the `&Handler` is still available. | |
166 | DiagnosticBuilderState::Emittable(handler) => { | |
167 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
168 | ||
169 | let guar = handler.emit_diagnostic(&mut db.inner.diagnostic); | |
170 | ||
171 | // Only allow a guarantee if the `level` wasn't switched to a | |
172 | // non-error - the field isn't `pub`, but the whole `Diagnostic` | |
173 | // can be overwritten with a new one, thanks to `DerefMut`. | |
174 | assert!( | |
175 | db.inner.diagnostic.is_error(), | |
176 | "emitted non-error ({:?}) diagnostic \ | |
177 | from `DiagnosticBuilder<ErrorGuaranteed>`", | |
178 | db.inner.diagnostic.level, | |
179 | ); | |
180 | guar.unwrap() | |
181 | } | |
182 | // `.emit()` was previously called, disallowed from repeating it, | |
183 | // but can take advantage of the previous `.emit()`'s guarantee | |
184 | // still being applicable (i.e. as a form of idempotency). | |
185 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => { | |
186 | // Only allow a guarantee if the `level` wasn't switched to a | |
187 | // non-error - the field isn't `pub`, but the whole `Diagnostic` | |
188 | // can be overwritten with a new one, thanks to `DerefMut`. | |
189 | assert!( | |
190 | db.inner.diagnostic.is_error(), | |
191 | "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \ | |
192 | became non-error ({:?}), after original `.emit()`", | |
193 | db.inner.diagnostic.level, | |
194 | ); | |
49aad941 | 195 | #[allow(deprecated)] |
5e7ed085 FG |
196 | ErrorGuaranteed::unchecked_claim_error_was_emitted() |
197 | } | |
198 | } | |
199 | } | |
f2b60f7d | 200 | |
487cf647 | 201 | #[track_caller] |
f2b60f7d FG |
202 | fn make_diagnostic_builder( |
203 | handler: &Handler, | |
204 | msg: impl Into<DiagnosticMessage>, | |
205 | ) -> DiagnosticBuilder<'_, Self> { | |
206 | DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>( | |
207 | handler, msg, | |
208 | ) | |
209 | } | |
5e7ed085 FG |
210 | } |
211 | ||
212 | impl<'a> DiagnosticBuilder<'a, ()> { | |
213 | /// Convenience function for internal use, clients should use one of the | |
214 | /// `struct_*` methods on [`Handler`]. | |
487cf647 | 215 | #[track_caller] |
923072b8 | 216 | pub(crate) fn new<M: Into<DiagnosticMessage>>( |
04454e1e FG |
217 | handler: &'a Handler, |
218 | level: Level, | |
219 | message: M, | |
220 | ) -> Self { | |
5e7ed085 FG |
221 | let diagnostic = Diagnostic::new_with_code(level, None, message); |
222 | Self::new_diagnostic(handler, diagnostic) | |
223 | } | |
224 | ||
225 | /// Creates a new `DiagnosticBuilder` with an already constructed | |
226 | /// diagnostic. | |
487cf647 | 227 | #[track_caller] |
923072b8 | 228 | pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self { |
5e7ed085 FG |
229 | debug!("Created new diagnostic"); |
230 | Self { | |
231 | inner: DiagnosticBuilderInner { | |
232 | state: DiagnosticBuilderState::Emittable(handler), | |
233 | diagnostic: Box::new(diagnostic), | |
234 | }, | |
235 | _marker: PhantomData, | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | // FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well? | |
241 | impl EmissionGuarantee for () { | |
242 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
243 | match db.inner.state { | |
244 | // First `.emit()` call, the `&Handler` is still available. | |
245 | DiagnosticBuilderState::Emittable(handler) => { | |
246 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
247 | ||
248 | handler.emit_diagnostic(&mut db.inner.diagnostic); | |
249 | } | |
250 | // `.emit()` was previously called, disallowed from repeating it. | |
251 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
252 | } | |
253 | } | |
f2b60f7d FG |
254 | |
255 | fn make_diagnostic_builder( | |
256 | handler: &Handler, | |
257 | msg: impl Into<DiagnosticMessage>, | |
258 | ) -> DiagnosticBuilder<'_, Self> { | |
259 | DiagnosticBuilder::new(handler, Level::Warning(None), msg) | |
260 | } | |
5e7ed085 FG |
261 | } |
262 | ||
2b03887a FG |
263 | /// Marker type which enables implementation of `create_note` and `emit_note` functions for |
264 | /// note-without-error struct diagnostics. | |
265 | #[derive(Copy, Clone)] | |
266 | pub struct Noted; | |
267 | ||
268 | impl<'a> DiagnosticBuilder<'a, Noted> { | |
269 | /// Convenience function for internal use, clients should use one of the | |
270 | /// `struct_*` methods on [`Handler`]. | |
271 | pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { | |
272 | let diagnostic = Diagnostic::new_with_code(Level::Note, None, message); | |
273 | Self::new_diagnostic_note(handler, diagnostic) | |
274 | } | |
275 | ||
276 | /// Creates a new `DiagnosticBuilder` with an already constructed | |
277 | /// diagnostic. | |
278 | pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self { | |
279 | debug!("Created new diagnostic"); | |
280 | Self { | |
281 | inner: DiagnosticBuilderInner { | |
282 | state: DiagnosticBuilderState::Emittable(handler), | |
283 | diagnostic: Box::new(diagnostic), | |
284 | }, | |
285 | _marker: PhantomData, | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | impl EmissionGuarantee for Noted { | |
291 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
292 | match db.inner.state { | |
293 | // First `.emit()` call, the `&Handler` is still available. | |
294 | DiagnosticBuilderState::Emittable(handler) => { | |
295 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
296 | handler.emit_diagnostic(&mut db.inner.diagnostic); | |
297 | } | |
298 | // `.emit()` was previously called, disallowed from repeating it. | |
299 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
300 | } | |
301 | ||
302 | Noted | |
303 | } | |
304 | ||
305 | fn make_diagnostic_builder( | |
306 | handler: &Handler, | |
307 | msg: impl Into<DiagnosticMessage>, | |
308 | ) -> DiagnosticBuilder<'_, Self> { | |
309 | DiagnosticBuilder::new_note(handler, msg) | |
310 | } | |
311 | } | |
312 | ||
9c376795 FG |
313 | /// Marker type which enables implementation of `create_bug` and `emit_bug` functions for |
314 | /// bug struct diagnostics. | |
315 | #[derive(Copy, Clone)] | |
316 | pub struct Bug; | |
317 | ||
318 | impl<'a> DiagnosticBuilder<'a, Bug> { | |
319 | /// Convenience function for internal use, clients should use one of the | |
320 | /// `struct_*` methods on [`Handler`]. | |
321 | #[track_caller] | |
322 | pub(crate) fn new_bug(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { | |
323 | let diagnostic = Diagnostic::new_with_code(Level::Bug, None, message); | |
324 | Self::new_diagnostic_bug(handler, diagnostic) | |
325 | } | |
326 | ||
327 | /// Creates a new `DiagnosticBuilder` with an already constructed | |
328 | /// diagnostic. | |
329 | pub(crate) fn new_diagnostic_bug(handler: &'a Handler, diagnostic: Diagnostic) -> Self { | |
330 | debug!("Created new diagnostic bug"); | |
331 | Self { | |
332 | inner: DiagnosticBuilderInner { | |
333 | state: DiagnosticBuilderState::Emittable(handler), | |
334 | diagnostic: Box::new(diagnostic), | |
335 | }, | |
336 | _marker: PhantomData, | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | impl EmissionGuarantee for Bug { | |
342 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
343 | match db.inner.state { | |
344 | // First `.emit()` call, the `&Handler` is still available. | |
345 | DiagnosticBuilderState::Emittable(handler) => { | |
346 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
347 | ||
348 | handler.emit_diagnostic(&mut db.inner.diagnostic); | |
349 | } | |
350 | // `.emit()` was previously called, disallowed from repeating it. | |
351 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
352 | } | |
353 | // Then panic. No need to return the marker type. | |
354 | panic::panic_any(ExplicitBug); | |
355 | } | |
356 | ||
357 | fn make_diagnostic_builder( | |
358 | handler: &Handler, | |
359 | msg: impl Into<DiagnosticMessage>, | |
360 | ) -> DiagnosticBuilder<'_, Self> { | |
361 | DiagnosticBuilder::new_bug(handler, msg) | |
362 | } | |
363 | } | |
364 | ||
5e7ed085 FG |
365 | impl<'a> DiagnosticBuilder<'a, !> { |
366 | /// Convenience function for internal use, clients should use one of the | |
367 | /// `struct_*` methods on [`Handler`]. | |
487cf647 | 368 | #[track_caller] |
923072b8 | 369 | pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { |
5e7ed085 FG |
370 | let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); |
371 | Self::new_diagnostic_fatal(handler, diagnostic) | |
372 | } | |
373 | ||
374 | /// Creates a new `DiagnosticBuilder` with an already constructed | |
375 | /// diagnostic. | |
923072b8 | 376 | pub(crate) fn new_diagnostic_fatal(handler: &'a Handler, diagnostic: Diagnostic) -> Self { |
5e7ed085 FG |
377 | debug!("Created new diagnostic"); |
378 | Self { | |
379 | inner: DiagnosticBuilderInner { | |
380 | state: DiagnosticBuilderState::Emittable(handler), | |
381 | diagnostic: Box::new(diagnostic), | |
382 | }, | |
383 | _marker: PhantomData, | |
384 | } | |
385 | } | |
386 | } | |
387 | ||
388 | impl EmissionGuarantee for ! { | |
389 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
390 | match db.inner.state { | |
391 | // First `.emit()` call, the `&Handler` is still available. | |
392 | DiagnosticBuilderState::Emittable(handler) => { | |
393 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
394 | ||
395 | handler.emit_diagnostic(&mut db.inner.diagnostic); | |
396 | } | |
397 | // `.emit()` was previously called, disallowed from repeating it. | |
398 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
399 | } | |
400 | // Then fatally error, returning `!` | |
401 | crate::FatalError.raise() | |
402 | } | |
f2b60f7d FG |
403 | |
404 | fn make_diagnostic_builder( | |
405 | handler: &Handler, | |
406 | msg: impl Into<DiagnosticMessage>, | |
407 | ) -> DiagnosticBuilder<'_, Self> { | |
408 | DiagnosticBuilder::new_fatal(handler, msg) | |
409 | } | |
5e7ed085 FG |
410 | } |
411 | ||
9ffffee4 FG |
412 | impl<'a> DiagnosticBuilder<'a, rustc_span::fatal_error::FatalError> { |
413 | /// Convenience function for internal use, clients should use one of the | |
414 | /// `struct_*` methods on [`Handler`]. | |
415 | #[track_caller] | |
416 | pub(crate) fn new_almost_fatal( | |
417 | handler: &'a Handler, | |
418 | message: impl Into<DiagnosticMessage>, | |
419 | ) -> Self { | |
420 | let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); | |
421 | Self::new_diagnostic_almost_fatal(handler, diagnostic) | |
422 | } | |
423 | ||
424 | /// Creates a new `DiagnosticBuilder` with an already constructed | |
425 | /// diagnostic. | |
426 | pub(crate) fn new_diagnostic_almost_fatal( | |
427 | handler: &'a Handler, | |
428 | diagnostic: Diagnostic, | |
429 | ) -> Self { | |
430 | debug!("Created new diagnostic"); | |
431 | Self { | |
432 | inner: DiagnosticBuilderInner { | |
433 | state: DiagnosticBuilderState::Emittable(handler), | |
434 | diagnostic: Box::new(diagnostic), | |
435 | }, | |
436 | _marker: PhantomData, | |
437 | } | |
438 | } | |
439 | } | |
440 | ||
441 | impl EmissionGuarantee for rustc_span::fatal_error::FatalError { | |
442 | fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { | |
443 | match db.inner.state { | |
444 | // First `.emit()` call, the `&Handler` is still available. | |
445 | DiagnosticBuilderState::Emittable(handler) => { | |
446 | db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
447 | ||
448 | handler.emit_diagnostic(&mut db.inner.diagnostic); | |
449 | } | |
450 | // `.emit()` was previously called, disallowed from repeating it. | |
451 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
452 | } | |
453 | // Then fatally error.. | |
454 | rustc_span::fatal_error::FatalError | |
455 | } | |
456 | ||
457 | fn make_diagnostic_builder( | |
458 | handler: &Handler, | |
459 | msg: impl Into<DiagnosticMessage>, | |
460 | ) -> DiagnosticBuilder<'_, Self> { | |
461 | DiagnosticBuilder::new_almost_fatal(handler, msg) | |
462 | } | |
463 | } | |
464 | ||
c30ab7b3 SL |
465 | /// In general, the `DiagnosticBuilder` uses deref to allow access to |
466 | /// the fields and methods of the embedded `diagnostic` in a | |
9fa01778 | 467 | /// transparent way. *However,* many of the methods are intended to |
c30ab7b3 SL |
468 | /// be used in a chained way, and hence ought to return `self`. In |
469 | /// that case, we can't just naively forward to the method on the | |
470 | /// `diagnostic`, because the return type would be a `&Diagnostic` | |
471 | /// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes | |
472 | /// it easy to declare such methods on the builder. | |
473 | macro_rules! forward { | |
c30ab7b3 | 474 | // Forward pattern for &mut self -> &mut Self |
9fa01778 XL |
475 | ( |
476 | $(#[$attrs:meta])* | |
477 | pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self | |
478 | ) => { | |
479 | $(#[$attrs])* | |
6a06907d | 480 | #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] |
c30ab7b3 | 481 | pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { |
5e7ed085 | 482 | self.inner.diagnostic.$n($($name),*); |
c30ab7b3 | 483 | self |
6a06907d | 484 | } |
c30ab7b3 SL |
485 | }; |
486 | } | |
487 | ||
5e7ed085 | 488 | impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> { |
c30ab7b3 SL |
489 | type Target = Diagnostic; |
490 | ||
491 | fn deref(&self) -> &Diagnostic { | |
5e7ed085 | 492 | &self.inner.diagnostic |
c30ab7b3 SL |
493 | } |
494 | } | |
495 | ||
5e7ed085 | 496 | impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> { |
c30ab7b3 | 497 | fn deref_mut(&mut self) -> &mut Diagnostic { |
5e7ed085 | 498 | &mut self.inner.diagnostic |
c30ab7b3 SL |
499 | } |
500 | } | |
501 | ||
5e7ed085 | 502 | impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { |
c30ab7b3 | 503 | /// Emit the diagnostic. |
5e7ed085 FG |
504 | #[track_caller] |
505 | pub fn emit(&mut self) -> G { | |
506 | G::diagnostic_builder_emit_producing_guarantee(self) | |
2c00a5a8 XL |
507 | } |
508 | ||
48663c56 XL |
509 | /// Emit the diagnostic unless `delay` is true, |
510 | /// in which case the emission will be delayed as a bug. | |
511 | /// | |
512 | /// See `emit` and `delay_as_bug` for details. | |
5e7ed085 FG |
513 | #[track_caller] |
514 | pub fn emit_unless(&mut self, delay: bool) -> G { | |
74b04a01 | 515 | if delay { |
5e7ed085 | 516 | self.downgrade_to_delayed_bug(); |
74b04a01 | 517 | } |
5e7ed085 FG |
518 | self.emit() |
519 | } | |
520 | ||
521 | /// Cancel the diagnostic (a structured diagnostic must either be emitted or | |
522 | /// cancelled or it will panic when dropped). | |
523 | /// | |
524 | /// This method takes `self` by-value to disallow calling `.emit()` on it, | |
525 | /// which may be expected to *guarantee* the emission of an error, either | |
526 | /// at the time of the call, or through a prior `.emit()` call. | |
527 | pub fn cancel(mut self) { | |
528 | self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; | |
529 | drop(self); | |
48663c56 XL |
530 | } |
531 | ||
e74abb32 XL |
532 | /// Stashes diagnostic for possible later improvement in a different, |
533 | /// later stage of the compiler. The diagnostic can be accessed with | |
fc512014 | 534 | /// the provided `span` and `key` through [`Handler::steal_diagnostic()`]. |
e74abb32 XL |
535 | /// |
536 | /// As with `buffer`, this is unless the handler has disabled such buffering. | |
537 | pub fn stash(self, span: Span, key: StashKey) { | |
538 | if let Some((diag, handler)) = self.into_diagnostic() { | |
539 | handler.stash_diagnostic(span, key, diag); | |
540 | } | |
541 | } | |
542 | ||
543 | /// Converts the builder to a `Diagnostic` for later emission, | |
5e7ed085 | 544 | /// unless handler has disabled such buffering, or `.emit()` was called. |
e74abb32 | 545 | pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { |
5e7ed085 FG |
546 | let handler = match self.inner.state { |
547 | // No `.emit()` calls, the `&Handler` is still available. | |
548 | DiagnosticBuilderState::Emittable(handler) => handler, | |
549 | // `.emit()` was previously called, nothing we can do. | |
550 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => { | |
551 | return None; | |
552 | } | |
553 | }; | |
554 | ||
555 | if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() { | |
0bf4aa26 | 556 | self.emit(); |
e74abb32 | 557 | return None; |
0bf4aa26 XL |
558 | } |
559 | ||
5e7ed085 | 560 | // Take the `Diagnostic` by replacing it with a dummy. |
04454e1e | 561 | let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string())); |
5e7ed085 | 562 | let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy); |
e74abb32 | 563 | |
5e7ed085 FG |
564 | // Disable the ICE on `Drop`. |
565 | self.cancel(); | |
dfeec247 | 566 | |
0bf4aa26 XL |
567 | // Logging here is useful to help track down where in logs an error was |
568 | // actually emitted. | |
569 | debug!("buffer: diagnostic={:?}", diagnostic); | |
e74abb32 XL |
570 | |
571 | Some((diagnostic, handler)) | |
572 | } | |
573 | ||
49aad941 FG |
574 | /// Retrieves the [`Handler`] if available |
575 | pub fn handler(&self) -> Option<&Handler> { | |
576 | match self.inner.state { | |
577 | DiagnosticBuilderState::Emittable(handler) => Some(handler), | |
578 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None, | |
579 | } | |
580 | } | |
581 | ||
e74abb32 XL |
582 | /// Buffers the diagnostic for later emission, |
583 | /// unless handler has disabled such buffering. | |
584 | pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) { | |
585 | buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); | |
c30ab7b3 SL |
586 | } |
587 | ||
3b2f2976 XL |
588 | /// Delay emission of this diagnostic as a bug. |
589 | /// | |
590 | /// This can be useful in contexts where an error indicates a bug but | |
591 | /// typically this only happens when other compilation errors have already | |
592 | /// happened. In those cases this can be used to defer emission of this | |
593 | /// diagnostic as a bug in the compiler only if no other errors have been | |
594 | /// emitted. | |
595 | /// | |
596 | /// In the meantime, though, callsites are required to deal with the "bug" | |
597 | /// locally in whichever way makes the most sense. | |
5e7ed085 | 598 | #[track_caller] |
487cf647 | 599 | pub fn delay_as_bug(&mut self) -> G { |
5e7ed085 | 600 | self.downgrade_to_delayed_bug(); |
487cf647 | 601 | self.emit() |
3b2f2976 XL |
602 | } |
603 | ||
5e7ed085 FG |
604 | forward!( |
605 | #[track_caller] | |
606 | pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self | |
607 | ); | |
608 | ||
609 | forward!( | |
fc512014 | 610 | /// Appends a labeled span to the diagnostic. |
3dfed10e | 611 | /// |
fc512014 XL |
612 | /// Labels are used to convey additional context for the diagnostic's primary span. They will |
613 | /// be shown together with the original diagnostic's span, *not* with spans added by | |
614 | /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because | |
615 | /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed | |
616 | /// either. | |
3dfed10e | 617 | /// |
fc512014 XL |
618 | /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when |
619 | /// the diagnostic was constructed. However, the label span is *not* considered a | |
620 | /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is | |
621 | /// primary. | |
923072b8 | 622 | pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self); |
c30ab7b3 | 623 | |
5e7ed085 | 624 | forward!( |
74b04a01 | 625 | /// Labels all the given spans with the provided label. |
fc512014 | 626 | /// See [`Diagnostic::span_label()`] for more information. |
74b04a01 XL |
627 | pub fn span_labels( |
628 | &mut self, | |
629 | spans: impl IntoIterator<Item = Span>, | |
630 | label: impl AsRef<str>, | |
5e7ed085 | 631 | ) -> &mut Self); |
74b04a01 | 632 | |
60c5eb7d XL |
633 | forward!(pub fn note_expected_found( |
634 | &mut self, | |
635 | expected_label: &dyn fmt::Display, | |
636 | expected: DiagnosticStyledString, | |
637 | found_label: &dyn fmt::Display, | |
638 | found: DiagnosticStyledString, | |
639 | ) -> &mut Self); | |
c30ab7b3 | 640 | |
60c5eb7d XL |
641 | forward!(pub fn note_expected_found_extra( |
642 | &mut self, | |
643 | expected_label: &dyn fmt::Display, | |
644 | expected: DiagnosticStyledString, | |
645 | found_label: &dyn fmt::Display, | |
646 | found: DiagnosticStyledString, | |
647 | expected_extra: &dyn fmt::Display, | |
648 | found_extra: &dyn fmt::Display, | |
649 | ) -> &mut Self); | |
c30ab7b3 | 650 | |
fc512014 | 651 | forward!(pub fn note_unsuccessful_coercion( |
60c5eb7d XL |
652 | &mut self, |
653 | expected: DiagnosticStyledString, | |
654 | found: DiagnosticStyledString, | |
655 | ) -> &mut Self); | |
e74abb32 | 656 | |
923072b8 FG |
657 | forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); |
658 | forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); | |
5e7ed085 FG |
659 | forward!(pub fn span_note( |
660 | &mut self, | |
661 | sp: impl Into<MultiSpan>, | |
923072b8 | 662 | msg: impl Into<SubdiagnosticMessage>, |
5e7ed085 FG |
663 | ) -> &mut Self); |
664 | forward!(pub fn span_note_once( | |
60c5eb7d | 665 | &mut self, |
5e7ed085 | 666 | sp: impl Into<MultiSpan>, |
923072b8 | 667 | msg: impl Into<SubdiagnosticMessage>, |
60c5eb7d | 668 | ) -> &mut Self); |
923072b8 FG |
669 | forward!(pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); |
670 | forward!(pub fn span_warn( | |
671 | &mut self, | |
672 | sp: impl Into<MultiSpan>, | |
673 | msg: impl Into<SubdiagnosticMessage>, | |
674 | ) -> &mut Self); | |
675 | forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); | |
5e7ed085 | 676 | forward!(pub fn span_help( |
60c5eb7d | 677 | &mut self, |
5e7ed085 | 678 | sp: impl Into<MultiSpan>, |
923072b8 | 679 | msg: impl Into<SubdiagnosticMessage>, |
60c5eb7d | 680 | ) -> &mut Self); |
c295e0f8 | 681 | forward!(pub fn set_is_lint(&mut self,) -> &mut Self); |
0bf4aa26 | 682 | |
5099ac24 | 683 | forward!(pub fn disable_suggestions(&mut self,) -> &mut Self); |
064997fb | 684 | forward!(pub fn clear_suggestions(&mut self,) -> &mut Self); |
5099ac24 FG |
685 | |
686 | forward!(pub fn multipart_suggestion( | |
9fa01778 | 687 | &mut self, |
923072b8 | 688 | msg: impl Into<SubdiagnosticMessage>, |
9fa01778 XL |
689 | suggestion: Vec<(Span, String)>, |
690 | applicability: Applicability, | |
5099ac24 FG |
691 | ) -> &mut Self); |
692 | forward!(pub fn multipart_suggestion_verbose( | |
c295e0f8 | 693 | &mut self, |
923072b8 | 694 | msg: impl Into<SubdiagnosticMessage>, |
c295e0f8 XL |
695 | suggestion: Vec<(Span, String)>, |
696 | applicability: Applicability, | |
5099ac24 FG |
697 | ) -> &mut Self); |
698 | forward!(pub fn tool_only_multipart_suggestion( | |
94b46f34 | 699 | &mut self, |
923072b8 | 700 | msg: impl Into<SubdiagnosticMessage>, |
0bf4aa26 | 701 | suggestion: Vec<(Span, String)>, |
9fa01778 | 702 | applicability: Applicability, |
5099ac24 FG |
703 | ) -> &mut Self); |
704 | forward!(pub fn span_suggestion( | |
9fa01778 XL |
705 | &mut self, |
706 | sp: Span, | |
923072b8 | 707 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 708 | suggestion: impl ToString, |
9fa01778 | 709 | applicability: Applicability, |
5099ac24 FG |
710 | ) -> &mut Self); |
711 | forward!(pub fn span_suggestions( | |
9fa01778 XL |
712 | &mut self, |
713 | sp: Span, | |
923072b8 | 714 | msg: impl Into<SubdiagnosticMessage>, |
487cf647 | 715 | suggestions: impl IntoIterator<Item = String>, |
9fa01778 | 716 | applicability: Applicability, |
5099ac24 FG |
717 | ) -> &mut Self); |
718 | forward!(pub fn multipart_suggestions( | |
94222f64 | 719 | &mut self, |
923072b8 | 720 | msg: impl Into<SubdiagnosticMessage>, |
487cf647 | 721 | suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, |
94222f64 | 722 | applicability: Applicability, |
5099ac24 FG |
723 | ) -> &mut Self); |
724 | forward!(pub fn span_suggestion_short( | |
9fa01778 XL |
725 | &mut self, |
726 | sp: Span, | |
923072b8 | 727 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 728 | suggestion: impl ToString, |
9fa01778 | 729 | applicability: Applicability, |
5099ac24 FG |
730 | ) -> &mut Self); |
731 | forward!(pub fn span_suggestion_verbose( | |
ba9703b0 XL |
732 | &mut self, |
733 | sp: Span, | |
923072b8 | 734 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 735 | suggestion: impl ToString, |
ba9703b0 | 736 | applicability: Applicability, |
5099ac24 FG |
737 | ) -> &mut Self); |
738 | forward!(pub fn span_suggestion_hidden( | |
9fa01778 XL |
739 | &mut self, |
740 | sp: Span, | |
923072b8 | 741 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 742 | suggestion: impl ToString, |
9fa01778 | 743 | applicability: Applicability, |
5099ac24 FG |
744 | ) -> &mut Self); |
745 | forward!(pub fn tool_only_span_suggestion( | |
9fa01778 XL |
746 | &mut self, |
747 | sp: Span, | |
923072b8 | 748 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 749 | suggestion: impl ToString, |
9fa01778 | 750 | applicability: Applicability, |
5099ac24 | 751 | ) -> &mut Self); |
9fa01778 | 752 | |
064997fb | 753 | forward!(pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); |
5e7ed085 | 754 | forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self); |
abe05a73 | 755 | forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); |
04454e1e FG |
756 | forward!(pub fn set_arg( |
757 | &mut self, | |
758 | name: impl Into<Cow<'static, str>>, | |
759 | arg: impl IntoDiagnosticArg, | |
760 | ) -> &mut Self); | |
761 | ||
762 | forward!(pub fn subdiagnostic( | |
763 | &mut self, | |
2b03887a | 764 | subdiagnostic: impl crate::AddToDiagnostic |
04454e1e | 765 | ) -> &mut Self); |
c30ab7b3 SL |
766 | } |
767 | ||
5e7ed085 | 768 | impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { |
9fa01778 | 769 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
5e7ed085 | 770 | self.inner.diagnostic.fmt(f) |
c30ab7b3 SL |
771 | } |
772 | } | |
773 | ||
5e7ed085 | 774 | /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled |
7cac9316 | 775 | /// or we emit a bug. |
5e7ed085 | 776 | impl Drop for DiagnosticBuilderInner<'_> { |
c30ab7b3 | 777 | fn drop(&mut self) { |
5e7ed085 FG |
778 | match self.state { |
779 | // No `.emit()` or `.cancel()` calls. | |
780 | DiagnosticBuilderState::Emittable(handler) => { | |
781 | if !panicking() { | |
782 | handler.emit_diagnostic(&mut Diagnostic::new( | |
783 | Level::Bug, | |
04454e1e FG |
784 | DiagnosticMessage::Str( |
785 | "the following error was constructed but not emitted".to_string(), | |
786 | ), | |
5e7ed085 FG |
787 | )); |
788 | handler.emit_diagnostic(&mut self.diagnostic); | |
f2b60f7d | 789 | panic!("error was constructed but not emitted"); |
5e7ed085 FG |
790 | } |
791 | } | |
792 | // `.emit()` was previously called, or maybe we're during `.cancel()`. | |
793 | DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} | |
c30ab7b3 SL |
794 | } |
795 | } | |
796 | } | |
dfeec247 XL |
797 | |
798 | #[macro_export] | |
799 | macro_rules! struct_span_err { | |
800 | ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ | |
801 | $session.struct_span_err_with_code( | |
802 | $span, | |
49aad941 | 803 | format!($($message)*), |
dfeec247 XL |
804 | $crate::error_code!($code), |
805 | ) | |
806 | }) | |
807 | } | |
808 | ||
809 | #[macro_export] | |
810 | macro_rules! error_code { | |
811 | ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }}; | |
812 | } |