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