1 use crate::snippet::Style; 2 use crate::{ 3 CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan, 4 SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, 5 }; 6 use rustc_data_structures::fx::FxHashMap; 7 use rustc_error_messages::fluent_value_from_str_list_sep_by_and; 8 use rustc_error_messages::FluentValue; 9 use rustc_lint_defs::{Applicability, LintExpectationId}; 10 use rustc_span::symbol::Symbol; 11 use rustc_span::{Span, DUMMY_SP}; 12 use std::borrow::Cow; 13 use std::fmt::{self, Debug}; 14 use std::hash::{Hash, Hasher}; 15 use std::panic::Location; 16 17 /// Error type for `Diagnostic`'s `suggestions` field, indicating that 18 /// `.disable_suggestions()` was called on the `Diagnostic`. 19 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] 20 pub struct SuggestionsDisabled; 21 22 /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of 23 /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of 24 /// diagnostic emission. 25 pub type DiagnosticArg<'iter, 'source> = 26 (&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>); 27 28 /// Name of a diagnostic argument. 29 pub type DiagnosticArgName<'source> = Cow<'source, str>; 30 31 /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted 32 /// to a `FluentValue` by the emitter to be used in diagnostic translation. 33 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] 34 pub enum DiagnosticArgValue<'source> { 35 Str(Cow<'source, str>), 36 Number(i128), 37 StrListSepByAnd(Vec<Cow<'source, str>>), 38 } 39 40 /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic` 41 /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type 42 /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*` 43 /// crates to implement this. 44 pub trait IntoDiagnosticArg { into_diagnostic_arg(self) -> DiagnosticArgValue<'static>45 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; 46 } 47 48 impl<'source> IntoDiagnosticArg for DiagnosticArgValue<'source> { into_diagnostic_arg(self) -> DiagnosticArgValue<'static>49 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { 50 match self { 51 DiagnosticArgValue::Str(s) => DiagnosticArgValue::Str(Cow::Owned(s.into_owned())), 52 DiagnosticArgValue::Number(n) => DiagnosticArgValue::Number(n), 53 DiagnosticArgValue::StrListSepByAnd(l) => DiagnosticArgValue::StrListSepByAnd( 54 l.into_iter().map(|s| Cow::Owned(s.into_owned())).collect(), 55 ), 56 } 57 } 58 } 59 60 impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> { into(self) -> FluentValue<'source>61 fn into(self) -> FluentValue<'source> { 62 match self { 63 DiagnosticArgValue::Str(s) => From::from(s), 64 DiagnosticArgValue::Number(n) => From::from(n), 65 DiagnosticArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), 66 } 67 } 68 } 69 70 /// Trait implemented by error types. This should not be implemented manually. Instead, use 71 /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. 72 #[rustc_diagnostic_item = "AddToDiagnostic"] 73 pub trait AddToDiagnostic 74 where 75 Self: Sized, 76 { 77 /// Add a subdiagnostic to an existing diagnostic. add_to_diagnostic(self, diag: &mut Diagnostic)78 fn add_to_diagnostic(self, diag: &mut Diagnostic) { 79 self.add_to_diagnostic_with(diag, |_, m| m); 80 } 81 82 /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used 83 /// (to optionally perform eager translation). add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) where F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage84 fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) 85 where 86 F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage; 87 } 88 89 /// Trait implemented by lint types. This should not be implemented manually. Instead, use 90 /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. 91 #[rustc_diagnostic_item = "DecorateLint"] 92 pub trait DecorateLint<'a, G: EmissionGuarantee> { 93 /// Decorate and emit a lint. decorate_lint<'b>( self, diag: &'b mut DiagnosticBuilder<'a, G>, ) -> &'b mut DiagnosticBuilder<'a, G>94 fn decorate_lint<'b>( 95 self, 96 diag: &'b mut DiagnosticBuilder<'a, G>, 97 ) -> &'b mut DiagnosticBuilder<'a, G>; 98 msg(&self) -> DiagnosticMessage99 fn msg(&self) -> DiagnosticMessage; 100 } 101 102 #[must_use] 103 #[derive(Clone, Debug, Encodable, Decodable)] 104 pub struct Diagnostic { 105 // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, 106 // outside of what methods in this crate themselves allow. 107 pub(crate) level: Level, 108 109 pub message: Vec<(DiagnosticMessage, Style)>, 110 pub code: Option<DiagnosticId>, 111 pub span: MultiSpan, 112 pub children: Vec<SubDiagnostic>, 113 pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, 114 args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>, 115 116 /// This is not used for highlighting or rendering any error message. Rather, it can be used 117 /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of 118 /// `span` if there is one. Otherwise, it is `DUMMY_SP`. 119 pub sort_span: Span, 120 121 /// If diagnostic is from Lint, custom hash function ignores notes 122 /// otherwise hash is based on the all the fields 123 pub is_lint: bool, 124 125 /// With `-Ztrack_diagnostics` enabled, 126 /// we print where in rustc this error was emitted. 127 pub emitted_at: DiagnosticLocation, 128 } 129 130 #[derive(Clone, Debug, Encodable, Decodable)] 131 pub struct DiagnosticLocation { 132 file: Cow<'static, str>, 133 line: u32, 134 col: u32, 135 } 136 137 impl DiagnosticLocation { 138 #[track_caller] caller() -> Self139 fn caller() -> Self { 140 let loc = Location::caller(); 141 DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } 142 } 143 } 144 145 impl fmt::Display for DiagnosticLocation { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 147 write!(f, "{}:{}:{}", self.file, self.line, self.col) 148 } 149 } 150 151 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] 152 pub enum DiagnosticId { 153 Error(String), 154 Lint { name: String, has_future_breakage: bool, is_force_warn: bool }, 155 } 156 157 /// A "sub"-diagnostic attached to a parent diagnostic. 158 /// For example, a note attached to an error. 159 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] 160 pub struct SubDiagnostic { 161 pub level: Level, 162 pub message: Vec<(DiagnosticMessage, Style)>, 163 pub span: MultiSpan, 164 pub render_span: Option<MultiSpan>, 165 } 166 167 #[derive(Debug, PartialEq, Eq)] 168 pub struct DiagnosticStyledString(pub Vec<StringPart>); 169 170 impl DiagnosticStyledString { new() -> DiagnosticStyledString171 pub fn new() -> DiagnosticStyledString { 172 DiagnosticStyledString(vec![]) 173 } push_normal<S: Into<String>>(&mut self, t: S)174 pub fn push_normal<S: Into<String>>(&mut self, t: S) { 175 self.0.push(StringPart::Normal(t.into())); 176 } push_highlighted<S: Into<String>>(&mut self, t: S)177 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) { 178 self.0.push(StringPart::Highlighted(t.into())); 179 } push<S: Into<String>>(&mut self, t: S, highlight: bool)180 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) { 181 if highlight { 182 self.push_highlighted(t); 183 } else { 184 self.push_normal(t); 185 } 186 } normal<S: Into<String>>(t: S) -> DiagnosticStyledString187 pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString { 188 DiagnosticStyledString(vec![StringPart::Normal(t.into())]) 189 } 190 highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString191 pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString { 192 DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) 193 } 194 content(&self) -> String195 pub fn content(&self) -> String { 196 self.0.iter().map(|x| x.content()).collect::<String>() 197 } 198 } 199 200 #[derive(Debug, PartialEq, Eq)] 201 pub enum StringPart { 202 Normal(String), 203 Highlighted(String), 204 } 205 206 impl StringPart { content(&self) -> &str207 pub fn content(&self) -> &str { 208 match self { 209 &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s, 210 } 211 } 212 } 213 214 impl Diagnostic { 215 #[track_caller] new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self216 pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { 217 Diagnostic::new_with_code(level, None, message) 218 } 219 220 #[track_caller] new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self221 pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self { 222 Diagnostic { 223 level, 224 message: messages, 225 code: None, 226 span: MultiSpan::new(), 227 children: vec![], 228 suggestions: Ok(vec![]), 229 args: Default::default(), 230 sort_span: DUMMY_SP, 231 is_lint: false, 232 emitted_at: DiagnosticLocation::caller(), 233 } 234 } 235 236 #[track_caller] new_with_code<M: Into<DiagnosticMessage>>( level: Level, code: Option<DiagnosticId>, message: M, ) -> Self237 pub fn new_with_code<M: Into<DiagnosticMessage>>( 238 level: Level, 239 code: Option<DiagnosticId>, 240 message: M, 241 ) -> Self { 242 Diagnostic { 243 level, 244 message: vec![(message.into(), Style::NoStyle)], 245 code, 246 span: MultiSpan::new(), 247 children: vec![], 248 suggestions: Ok(vec![]), 249 args: Default::default(), 250 sort_span: DUMMY_SP, 251 is_lint: false, 252 emitted_at: DiagnosticLocation::caller(), 253 } 254 } 255 256 #[inline(always)] level(&self) -> Level257 pub fn level(&self) -> Level { 258 self.level 259 } 260 is_error(&self) -> bool261 pub fn is_error(&self) -> bool { 262 match self.level { 263 Level::Bug 264 | Level::DelayedBug 265 | Level::Fatal 266 | Level::Error { .. } 267 | Level::FailureNote => true, 268 269 Level::Warning(_) 270 | Level::Note 271 | Level::OnceNote 272 | Level::Help 273 | Level::Allow 274 | Level::Expect(_) => false, 275 } 276 } 277 update_unstable_expectation_id( &mut self, unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, )278 pub fn update_unstable_expectation_id( 279 &mut self, 280 unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, 281 ) { 282 if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) = 283 &mut self.level 284 { 285 if expectation_id.is_stable() { 286 return; 287 } 288 289 // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index. 290 // The lint index inside the attribute is manually transferred here. 291 let lint_index = expectation_id.get_lint_index(); 292 expectation_id.set_lint_index(None); 293 let mut stable_id = unstable_to_stable 294 .get(expectation_id) 295 .expect("each unstable `LintExpectationId` must have a matching stable id") 296 .normalize(); 297 298 stable_id.set_lint_index(lint_index); 299 *expectation_id = stable_id; 300 } 301 } 302 has_future_breakage(&self) -> bool303 pub fn has_future_breakage(&self) -> bool { 304 match self.code { 305 Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage, 306 _ => false, 307 } 308 } 309 is_force_warn(&self) -> bool310 pub fn is_force_warn(&self) -> bool { 311 match self.code { 312 Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn, 313 _ => false, 314 } 315 } 316 317 /// Delay emission of this diagnostic as a bug. 318 /// 319 /// This can be useful in contexts where an error indicates a bug but 320 /// typically this only happens when other compilation errors have already 321 /// happened. In those cases this can be used to defer emission of this 322 /// diagnostic as a bug in the compiler only if no other errors have been 323 /// emitted. 324 /// 325 /// In the meantime, though, callsites are required to deal with the "bug" 326 /// locally in whichever way makes the most sense. 327 #[track_caller] downgrade_to_delayed_bug(&mut self) -> &mut Self328 pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self { 329 assert!( 330 self.is_error(), 331 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", 332 self.level 333 ); 334 self.level = Level::DelayedBug; 335 336 self 337 } 338 339 /// Adds a span/label to be included in the resulting snippet. 340 /// 341 /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic 342 /// was first built. That means it will be shown together with the original 343 /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods. 344 /// 345 /// This span is *not* considered a ["primary span"][`MultiSpan`]; only 346 /// the `Span` supplied when creating the diagnostic is primary. 347 #[rustc_lint_diagnostics] span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self348 pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self { 349 self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label)); 350 self 351 } 352 353 /// Labels all the given spans with the provided label. 354 /// See [`Self::span_label()`] for more information. span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self355 pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self { 356 for span in spans { 357 self.span_label(span, label.to_string()); 358 } 359 self 360 } 361 replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self362 pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { 363 let before = self.span.clone(); 364 self.set_span(after); 365 for span_label in before.span_labels() { 366 if let Some(label) = span_label.label { 367 if span_label.is_primary && keep_label { 368 self.span.push_span_label(after, label); 369 } else { 370 self.span.push_span_label(span_label.span, label); 371 } 372 } 373 } 374 self 375 } 376 note_expected_found( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, ) -> &mut Self377 pub fn note_expected_found( 378 &mut self, 379 expected_label: &dyn fmt::Display, 380 expected: DiagnosticStyledString, 381 found_label: &dyn fmt::Display, 382 found: DiagnosticStyledString, 383 ) -> &mut Self { 384 self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") 385 } 386 note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, ) -> &mut Self387 pub fn note_unsuccessful_coercion( 388 &mut self, 389 expected: DiagnosticStyledString, 390 found: DiagnosticStyledString, 391 ) -> &mut Self { 392 let mut msg: Vec<_> = 393 vec![(Cow::from("required when trying to coerce from type `"), Style::NoStyle)]; 394 msg.extend(expected.0.iter().map(|x| match *x { 395 StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle), 396 StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight), 397 })); 398 msg.push((Cow::from("` to type '"), Style::NoStyle)); 399 msg.extend(found.0.iter().map(|x| match *x { 400 StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle), 401 StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight), 402 })); 403 msg.push((Cow::from("`"), Style::NoStyle)); 404 405 // For now, just attach these as notes 406 self.highlighted_note(msg); 407 self 408 } 409 note_expected_found_extra( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, expected_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display, ) -> &mut Self410 pub fn note_expected_found_extra( 411 &mut self, 412 expected_label: &dyn fmt::Display, 413 expected: DiagnosticStyledString, 414 found_label: &dyn fmt::Display, 415 found: DiagnosticStyledString, 416 expected_extra: &dyn fmt::Display, 417 found_extra: &dyn fmt::Display, 418 ) -> &mut Self { 419 let expected_label = expected_label.to_string(); 420 let expected_label = if expected_label.is_empty() { 421 "expected".to_string() 422 } else { 423 format!("expected {}", expected_label) 424 }; 425 let found_label = found_label.to_string(); 426 let found_label = if found_label.is_empty() { 427 "found".to_string() 428 } else { 429 format!("found {}", found_label) 430 }; 431 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { 432 (expected_label.len() - found_label.len(), 0) 433 } else { 434 (0, found_label.len() - expected_label.len()) 435 }; 436 let mut msg: Vec<_> = 437 vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)]; 438 msg.extend(expected.0.iter().map(|x| match *x { 439 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), 440 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), 441 })); 442 msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); 443 msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); 444 msg.extend(found.0.iter().map(|x| match *x { 445 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), 446 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), 447 })); 448 msg.push((format!("`{}", found_extra), Style::NoStyle)); 449 450 // For now, just attach these as notes. 451 self.highlighted_note(msg); 452 self 453 } 454 note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self455 pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { 456 self.highlighted_note(vec![ 457 (format!("`{}` from trait: `", name), Style::NoStyle), 458 (signature, Style::Highlight), 459 ("`".to_string(), Style::NoStyle), 460 ]); 461 self 462 } 463 464 /// Add a note attached to this diagnostic. 465 #[rustc_lint_diagnostics] note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self466 pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { 467 self.sub(Level::Note, msg, MultiSpan::new(), None); 468 self 469 } 470 highlighted_note<M: Into<SubdiagnosticMessage>>( &mut self, msg: Vec<(M, Style)>, ) -> &mut Self471 pub fn highlighted_note<M: Into<SubdiagnosticMessage>>( 472 &mut self, 473 msg: Vec<(M, Style)>, 474 ) -> &mut Self { 475 self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); 476 self 477 } 478 479 /// Prints the span with a note above it. 480 /// This is like [`Diagnostic::note()`], but it gets its own span. note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self481 pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { 482 self.sub(Level::OnceNote, msg, MultiSpan::new(), None); 483 self 484 } 485 486 /// Prints the span with a note above it. 487 /// This is like [`Diagnostic::note()`], but it gets its own span. 488 #[rustc_lint_diagnostics] span_note<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self489 pub fn span_note<S: Into<MultiSpan>>( 490 &mut self, 491 sp: S, 492 msg: impl Into<SubdiagnosticMessage>, 493 ) -> &mut Self { 494 self.sub(Level::Note, msg, sp.into(), None); 495 self 496 } 497 498 /// Prints the span with a note above it. 499 /// This is like [`Diagnostic::note()`], but it gets its own span. span_note_once<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self500 pub fn span_note_once<S: Into<MultiSpan>>( 501 &mut self, 502 sp: S, 503 msg: impl Into<SubdiagnosticMessage>, 504 ) -> &mut Self { 505 self.sub(Level::OnceNote, msg, sp.into(), None); 506 self 507 } 508 509 /// Add a warning attached to this diagnostic. 510 #[rustc_lint_diagnostics] warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self511 pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { 512 self.sub(Level::Warning(None), msg, MultiSpan::new(), None); 513 self 514 } 515 516 /// Prints the span with a warning above it. 517 /// This is like [`Diagnostic::warn()`], but it gets its own span. 518 #[rustc_lint_diagnostics] span_warn<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self519 pub fn span_warn<S: Into<MultiSpan>>( 520 &mut self, 521 sp: S, 522 msg: impl Into<SubdiagnosticMessage>, 523 ) -> &mut Self { 524 self.sub(Level::Warning(None), msg, sp.into(), None); 525 self 526 } 527 528 /// Add a help message attached to this diagnostic. 529 #[rustc_lint_diagnostics] help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self530 pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { 531 self.sub(Level::Help, msg, MultiSpan::new(), None); 532 self 533 } 534 535 /// Add a help message attached to this diagnostic with a customizable highlighted message. highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self536 pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self { 537 self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None); 538 self 539 } 540 541 /// Prints the span with some help above it. 542 /// This is like [`Diagnostic::help()`], but it gets its own span. 543 #[rustc_lint_diagnostics] span_help<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self544 pub fn span_help<S: Into<MultiSpan>>( 545 &mut self, 546 sp: S, 547 msg: impl Into<SubdiagnosticMessage>, 548 ) -> &mut Self { 549 self.sub(Level::Help, msg, sp.into(), None); 550 self 551 } 552 553 /// Disallow attaching suggestions this diagnostic. 554 /// Any suggestions attached e.g. with the `span_suggestion_*` methods 555 /// (before and after the call to `disable_suggestions`) will be ignored. disable_suggestions(&mut self) -> &mut Self556 pub fn disable_suggestions(&mut self) -> &mut Self { 557 self.suggestions = Err(SuggestionsDisabled); 558 self 559 } 560 561 /// Clear any existing suggestions. clear_suggestions(&mut self) -> &mut Self562 pub fn clear_suggestions(&mut self) -> &mut Self { 563 if let Ok(suggestions) = &mut self.suggestions { 564 suggestions.clear(); 565 } 566 self 567 } 568 569 /// Helper for pushing to `self.suggestions`, if available (not disable). push_suggestion(&mut self, suggestion: CodeSuggestion)570 fn push_suggestion(&mut self, suggestion: CodeSuggestion) { 571 if let Ok(suggestions) = &mut self.suggestions { 572 suggestions.push(suggestion); 573 } 574 } 575 576 /// Show a suggestion that has multiple parts to it. 577 /// In other words, multiple changes need to be applied as part of this suggestion. multipart_suggestion( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self578 pub fn multipart_suggestion( 579 &mut self, 580 msg: impl Into<SubdiagnosticMessage>, 581 suggestion: Vec<(Span, String)>, 582 applicability: Applicability, 583 ) -> &mut Self { 584 self.multipart_suggestion_with_style( 585 msg, 586 suggestion, 587 applicability, 588 SuggestionStyle::ShowCode, 589 ) 590 } 591 592 /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. 593 /// In other words, multiple changes need to be applied as part of this suggestion. multipart_suggestion_verbose( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self594 pub fn multipart_suggestion_verbose( 595 &mut self, 596 msg: impl Into<SubdiagnosticMessage>, 597 suggestion: Vec<(Span, String)>, 598 applicability: Applicability, 599 ) -> &mut Self { 600 self.multipart_suggestion_with_style( 601 msg, 602 suggestion, 603 applicability, 604 SuggestionStyle::ShowAlways, 605 ) 606 } 607 /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. multipart_suggestion_with_style( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self608 pub fn multipart_suggestion_with_style( 609 &mut self, 610 msg: impl Into<SubdiagnosticMessage>, 611 suggestion: Vec<(Span, String)>, 612 applicability: Applicability, 613 style: SuggestionStyle, 614 ) -> &mut Self { 615 let mut parts = suggestion 616 .into_iter() 617 .map(|(span, snippet)| SubstitutionPart { snippet, span }) 618 .collect::<Vec<_>>(); 619 620 parts.sort_unstable_by_key(|part| part.span); 621 622 assert!(!parts.is_empty()); 623 debug_assert_eq!( 624 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()), 625 None, 626 "Span must not be empty and have no suggestion", 627 ); 628 debug_assert_eq!( 629 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), 630 None, 631 "suggestion must not have overlapping parts", 632 ); 633 634 self.push_suggestion(CodeSuggestion { 635 substitutions: vec![Substitution { parts }], 636 msg: self.subdiagnostic_message_to_diagnostic_message(msg), 637 style, 638 applicability, 639 }); 640 self 641 } 642 643 /// Prints out a message with for a multipart suggestion without showing the suggested code. 644 /// 645 /// This is intended to be used for suggestions that are obvious in what the changes need to 646 /// be from the message, showing the span label inline would be visually unpleasant 647 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't 648 /// improve understandability. tool_only_multipart_suggestion( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self649 pub fn tool_only_multipart_suggestion( 650 &mut self, 651 msg: impl Into<SubdiagnosticMessage>, 652 suggestion: Vec<(Span, String)>, 653 applicability: Applicability, 654 ) -> &mut Self { 655 self.multipart_suggestion_with_style( 656 msg, 657 suggestion, 658 applicability, 659 SuggestionStyle::CompletelyHidden, 660 ) 661 } 662 663 /// Prints out a message with a suggested edit of the code. 664 /// 665 /// In case of short messages and a simple suggestion, rustc displays it as a label: 666 /// 667 /// ```text 668 /// try adding parentheses: `(tup.0).1` 669 /// ``` 670 /// 671 /// The message 672 /// 673 /// * should not end in any punctuation (a `:` is added automatically) 674 /// * should not be a question (avoid language like "did you mean") 675 /// * should not contain any phrases like "the following", "as shown", etc. 676 /// * may look like "to do xyz, use" or "to do xyz, use abc" 677 /// * may contain a name of a function, variable, or type, but not whole expressions 678 /// 679 /// See `CodeSuggestion` for more information. span_suggestion( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self680 pub fn span_suggestion( 681 &mut self, 682 sp: Span, 683 msg: impl Into<SubdiagnosticMessage>, 684 suggestion: impl ToString, 685 applicability: Applicability, 686 ) -> &mut Self { 687 self.span_suggestion_with_style( 688 sp, 689 msg, 690 suggestion, 691 applicability, 692 SuggestionStyle::ShowCode, 693 ); 694 self 695 } 696 697 /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`]. span_suggestion_with_style( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self698 pub fn span_suggestion_with_style( 699 &mut self, 700 sp: Span, 701 msg: impl Into<SubdiagnosticMessage>, 702 suggestion: impl ToString, 703 applicability: Applicability, 704 style: SuggestionStyle, 705 ) -> &mut Self { 706 debug_assert!( 707 !(sp.is_empty() && suggestion.to_string().is_empty()), 708 "Span must not be empty and have no suggestion" 709 ); 710 self.push_suggestion(CodeSuggestion { 711 substitutions: vec![Substitution { 712 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }], 713 }], 714 msg: self.subdiagnostic_message_to_diagnostic_message(msg), 715 style, 716 applicability, 717 }); 718 self 719 } 720 721 /// Always show the suggested change. span_suggestion_verbose( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self722 pub fn span_suggestion_verbose( 723 &mut self, 724 sp: Span, 725 msg: impl Into<SubdiagnosticMessage>, 726 suggestion: impl ToString, 727 applicability: Applicability, 728 ) -> &mut Self { 729 self.span_suggestion_with_style( 730 sp, 731 msg, 732 suggestion, 733 applicability, 734 SuggestionStyle::ShowAlways, 735 ); 736 self 737 } 738 739 /// Prints out a message with multiple suggested edits of the code. 740 /// See also [`Diagnostic::span_suggestion()`]. span_suggestions( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = String>, applicability: Applicability, ) -> &mut Self741 pub fn span_suggestions( 742 &mut self, 743 sp: Span, 744 msg: impl Into<SubdiagnosticMessage>, 745 suggestions: impl IntoIterator<Item = String>, 746 applicability: Applicability, 747 ) -> &mut Self { 748 self.span_suggestions_with_style( 749 sp, 750 msg, 751 suggestions, 752 applicability, 753 SuggestionStyle::ShowCode, 754 ) 755 } 756 757 /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`]. span_suggestions_with_style( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = String>, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self758 pub fn span_suggestions_with_style( 759 &mut self, 760 sp: Span, 761 msg: impl Into<SubdiagnosticMessage>, 762 suggestions: impl IntoIterator<Item = String>, 763 applicability: Applicability, 764 style: SuggestionStyle, 765 ) -> &mut Self { 766 let mut suggestions: Vec<_> = suggestions.into_iter().collect(); 767 suggestions.sort(); 768 769 debug_assert!( 770 !(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())), 771 "Span must not be empty and have no suggestion" 772 ); 773 774 let substitutions = suggestions 775 .into_iter() 776 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) 777 .collect(); 778 self.push_suggestion(CodeSuggestion { 779 substitutions, 780 msg: self.subdiagnostic_message_to_diagnostic_message(msg), 781 style, 782 applicability, 783 }); 784 self 785 } 786 787 /// Prints out a message with multiple suggested edits of the code, where each edit consists of 788 /// multiple parts. 789 /// See also [`Diagnostic::multipart_suggestion()`]. multipart_suggestions( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self790 pub fn multipart_suggestions( 791 &mut self, 792 msg: impl Into<SubdiagnosticMessage>, 793 suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, 794 applicability: Applicability, 795 ) -> &mut Self { 796 let substitutions = suggestions 797 .into_iter() 798 .map(|sugg| { 799 let mut parts = sugg 800 .into_iter() 801 .map(|(span, snippet)| SubstitutionPart { snippet, span }) 802 .collect::<Vec<_>>(); 803 804 parts.sort_unstable_by_key(|part| part.span); 805 806 assert!(!parts.is_empty()); 807 debug_assert_eq!( 808 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()), 809 None, 810 "Span must not be empty and have no suggestion", 811 ); 812 debug_assert_eq!( 813 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), 814 None, 815 "suggestion must not have overlapping parts", 816 ); 817 818 Substitution { parts } 819 }) 820 .collect(); 821 822 self.push_suggestion(CodeSuggestion { 823 substitutions, 824 msg: self.subdiagnostic_message_to_diagnostic_message(msg), 825 style: SuggestionStyle::ShowCode, 826 applicability, 827 }); 828 self 829 } 830 831 /// Prints out a message with a suggested edit of the code. If the suggestion is presented 832 /// inline, it will only show the message and not the suggestion. 833 /// 834 /// See `CodeSuggestion` for more information. span_suggestion_short( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self835 pub fn span_suggestion_short( 836 &mut self, 837 sp: Span, 838 msg: impl Into<SubdiagnosticMessage>, 839 suggestion: impl ToString, 840 applicability: Applicability, 841 ) -> &mut Self { 842 self.span_suggestion_with_style( 843 sp, 844 msg, 845 suggestion, 846 applicability, 847 SuggestionStyle::HideCodeInline, 848 ); 849 self 850 } 851 852 /// Prints out a message for a suggestion without showing the suggested code. 853 /// 854 /// This is intended to be used for suggestions that are obvious in what the changes need to 855 /// be from the message, showing the span label inline would be visually unpleasant 856 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't 857 /// improve understandability. span_suggestion_hidden( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self858 pub fn span_suggestion_hidden( 859 &mut self, 860 sp: Span, 861 msg: impl Into<SubdiagnosticMessage>, 862 suggestion: impl ToString, 863 applicability: Applicability, 864 ) -> &mut Self { 865 self.span_suggestion_with_style( 866 sp, 867 msg, 868 suggestion, 869 applicability, 870 SuggestionStyle::HideCodeAlways, 871 ); 872 self 873 } 874 875 /// Adds a suggestion to the JSON output that will not be shown in the CLI. 876 /// 877 /// This is intended to be used for suggestions that are *very* obvious in what the changes 878 /// need to be from the message, but we still want other tools to be able to apply them. 879 #[rustc_lint_diagnostics] tool_only_span_suggestion( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self880 pub fn tool_only_span_suggestion( 881 &mut self, 882 sp: Span, 883 msg: impl Into<SubdiagnosticMessage>, 884 suggestion: impl ToString, 885 applicability: Applicability, 886 ) -> &mut Self { 887 self.span_suggestion_with_style( 888 sp, 889 msg, 890 suggestion, 891 applicability, 892 SuggestionStyle::CompletelyHidden, 893 ); 894 self 895 } 896 897 /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see 898 /// [rustc_macros::Subdiagnostic]). subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self899 pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self { 900 subdiagnostic.add_to_diagnostic(self); 901 self 902 } 903 904 /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see 905 /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages 906 /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of 907 /// interpolated variables). eager_subdiagnostic( &mut self, handler: &crate::Handler, subdiagnostic: impl AddToDiagnostic, ) -> &mut Self908 pub fn eager_subdiagnostic( 909 &mut self, 910 handler: &crate::Handler, 911 subdiagnostic: impl AddToDiagnostic, 912 ) -> &mut Self { 913 subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { 914 let args = diag.args(); 915 let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); 916 handler.eagerly_translate(msg, args) 917 }); 918 self 919 } 920 set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self921 pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { 922 self.span = sp.into(); 923 if let Some(span) = self.span.primary_span() { 924 self.sort_span = span; 925 } 926 self 927 } 928 set_is_lint(&mut self) -> &mut Self929 pub fn set_is_lint(&mut self) -> &mut Self { 930 self.is_lint = true; 931 self 932 } 933 code(&mut self, s: DiagnosticId) -> &mut Self934 pub fn code(&mut self, s: DiagnosticId) -> &mut Self { 935 self.code = Some(s); 936 self 937 } 938 clear_code(&mut self) -> &mut Self939 pub fn clear_code(&mut self) -> &mut Self { 940 self.code = None; 941 self 942 } 943 get_code(&self) -> Option<DiagnosticId>944 pub fn get_code(&self) -> Option<DiagnosticId> { 945 self.code.clone() 946 } 947 set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self948 pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { 949 self.message[0] = (msg.into(), Style::NoStyle); 950 self 951 } 952 953 // Exact iteration order of diagnostic arguments shouldn't make a difference to output because 954 // they're only used in interpolation. 955 #[allow(rustc::potential_query_instability)] args(&self) -> impl Iterator<Item = DiagnosticArg<'_, 'static>>956 pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_, 'static>> { 957 self.args.iter() 958 } 959 set_arg( &mut self, name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, ) -> &mut Self960 pub fn set_arg( 961 &mut self, 962 name: impl Into<Cow<'static, str>>, 963 arg: impl IntoDiagnosticArg, 964 ) -> &mut Self { 965 self.args.insert(name.into(), arg.into_diagnostic_arg()); 966 self 967 } 968 replace_args( &mut self, args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>, )969 pub fn replace_args( 970 &mut self, 971 args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>, 972 ) { 973 self.args = args; 974 } 975 styled_message(&self) -> &[(DiagnosticMessage, Style)]976 pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] { 977 &self.message 978 } 979 980 /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by 981 /// combining it with the primary message of the diagnostic (if translatable, otherwise it just 982 /// passes the user's string along). subdiagnostic_message_to_diagnostic_message( &self, attr: impl Into<SubdiagnosticMessage>, ) -> DiagnosticMessage983 pub(crate) fn subdiagnostic_message_to_diagnostic_message( 984 &self, 985 attr: impl Into<SubdiagnosticMessage>, 986 ) -> DiagnosticMessage { 987 let msg = 988 self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); 989 msg.with_subdiagnostic_message(attr.into()) 990 } 991 992 /// Convenience function for internal use, clients should use one of the 993 /// public methods above. 994 /// 995 /// Used by `proc_macro_server` for implementing `server::Diagnostic`. sub( &mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan, render_span: Option<MultiSpan>, )996 pub fn sub( 997 &mut self, 998 level: Level, 999 message: impl Into<SubdiagnosticMessage>, 1000 span: MultiSpan, 1001 render_span: Option<MultiSpan>, 1002 ) { 1003 let sub = SubDiagnostic { 1004 level, 1005 message: vec![( 1006 self.subdiagnostic_message_to_diagnostic_message(message), 1007 Style::NoStyle, 1008 )], 1009 span, 1010 render_span, 1011 }; 1012 self.children.push(sub); 1013 } 1014 1015 /// Convenience function for internal use, clients should use one of the 1016 /// public methods above. sub_with_highlights<M: Into<SubdiagnosticMessage>>( &mut self, level: Level, message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, )1017 fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( 1018 &mut self, 1019 level: Level, 1020 message: Vec<(M, Style)>, 1021 span: MultiSpan, 1022 render_span: Option<MultiSpan>, 1023 ) { 1024 let message = message 1025 .into_iter() 1026 .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) 1027 .collect(); 1028 let sub = SubDiagnostic { level, message, span, render_span }; 1029 self.children.push(sub); 1030 } 1031 1032 /// Fields used for Hash, and PartialEq trait keys( &self, ) -> ( &Level, &[(DiagnosticMessage, Style)], Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>, &Option<DiagnosticId>, &MultiSpan, &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, Option<&[SubDiagnostic]>, )1033 fn keys( 1034 &self, 1035 ) -> ( 1036 &Level, 1037 &[(DiagnosticMessage, Style)], 1038 Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>, 1039 &Option<DiagnosticId>, 1040 &MultiSpan, 1041 &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, 1042 Option<&[SubDiagnostic]>, 1043 ) { 1044 ( 1045 &self.level, 1046 &self.message, 1047 self.args().collect(), 1048 &self.code, 1049 &self.span, 1050 &self.suggestions, 1051 (if self.is_lint { None } else { Some(&self.children) }), 1052 ) 1053 } 1054 } 1055 1056 impl Hash for Diagnostic { hash<H>(&self, state: &mut H) where H: Hasher,1057 fn hash<H>(&self, state: &mut H) 1058 where 1059 H: Hasher, 1060 { 1061 self.keys().hash(state); 1062 } 1063 } 1064 1065 impl PartialEq for Diagnostic { eq(&self, other: &Self) -> bool1066 fn eq(&self, other: &Self) -> bool { 1067 self.keys() == other.keys() 1068 } 1069 } 1070