1use std::borrow::Cow;
2use std::fmt::{self, Debug};
3use std::hash::{Hash, Hasher};
4use std::marker::PhantomData;
5use std::ops::{Deref, DerefMut};
6use std::panic;
7use std::path::PathBuf;
8use std::thread::panicking;
9
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_error_messages::{DiagArgName, DiagArgValue, IntoDiagArg};
12use rustc_lint_defs::{Applicability, LintExpectationId};
13use rustc_macros::{Decodable, Encodable};
14use rustc_span::source_map::Spanned;
15use rustc_span::{DUMMY_SP, Span, Symbol};
16use tracing::debug;
17
18use crate::snippet::Style;
19use crate::{
20 CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level,
21 MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
22 Suggestions,
23};
24
25pub type DiagArgMap = FxIndexMap<DiagArgName, DiagArgValue>;
26
27pub trait EmissionGuarantee: Sized {
30 type EmitResult = Self;
33
34 #[track_caller]
38 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult;
39}
40
41impl EmissionGuarantee for ErrorGuaranteed {
42 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
43 diag.emit_producing_error_guaranteed()
44 }
45}
46
47impl EmissionGuarantee for () {
48 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
49 diag.emit_producing_nothing();
50 }
51}
52
53#[derive(Copy, Clone)]
56pub struct BugAbort;
57
58impl EmissionGuarantee for BugAbort {
59 type EmitResult = !;
60
61 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
62 diag.emit_producing_nothing();
63 panic::panic_any(ExplicitBug);
64 }
65}
66
67#[derive(Copy, Clone)]
70pub struct FatalAbort;
71
72impl EmissionGuarantee for FatalAbort {
73 type EmitResult = !;
74
75 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
76 diag.emit_producing_nothing();
77 crate::FatalError.raise()
78 }
79}
80
81impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
82 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
83 diag.emit_producing_nothing();
84 rustc_span::fatal_error::FatalError
85 }
86}
87
88#[rustc_diagnostic_item = "Diagnostic"]
110pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
111 #[must_use]
113 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>;
114}
115
116impl<'a, T, G> Diagnostic<'a, G> for Spanned<T>
117where
118 T: Diagnostic<'a, G>,
119 G: EmissionGuarantee,
120{
121 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
122 self.node.into_diag(dcx, level).with_span(self.span)
123 }
124}
125
126#[rustc_diagnostic_item = "Subdiagnostic"]
129pub trait Subdiagnostic
130where
131 Self: Sized,
132{
133 fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>);
135}
136
137#[rustc_diagnostic_item = "LintDiagnostic"]
140pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
141 fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
143}
144
145pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> {
146 fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>);
147}
148
149impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D {
150 fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>) {
151 self.decorate_lint(diag);
152 }
153}
154
155#[derive(Clone, Debug, Encodable, Decodable)]
156pub(crate) struct DiagLocation {
157 file: Cow<'static, str>,
158 line: u32,
159 col: u32,
160}
161
162impl DiagLocation {
163 #[track_caller]
164 fn caller() -> Self {
165 let loc = panic::Location::caller();
166 DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() }
167 }
168}
169
170impl fmt::Display for DiagLocation {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "{}:{}:{}", self.file, self.line, self.col)
173 }
174}
175
176#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
177pub struct IsLint {
178 pub(crate) name: String,
180 has_future_breakage: bool,
182}
183
184#[derive(Debug, PartialEq, Eq)]
185pub struct DiagStyledString(pub Vec<StringPart>);
186
187impl DiagStyledString {
188 pub fn new() -> DiagStyledString {
189 DiagStyledString(vec![])
190 }
191 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
192 self.0.push(StringPart::normal(t));
193 }
194 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
195 self.0.push(StringPart::highlighted(t));
196 }
197 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
198 if highlight {
199 self.push_highlighted(t);
200 } else {
201 self.push_normal(t);
202 }
203 }
204 pub fn normal<S: Into<String>>(t: S) -> DiagStyledString {
205 DiagStyledString(vec![StringPart::normal(t)])
206 }
207
208 pub fn highlighted<S: Into<String>>(t: S) -> DiagStyledString {
209 DiagStyledString(vec![StringPart::highlighted(t)])
210 }
211
212 pub fn content(&self) -> String {
213 self.0.iter().map(|x| x.content.as_str()).collect::<String>()
214 }
215}
216
217#[derive(Debug, PartialEq, Eq)]
218pub struct StringPart {
219 content: String,
220 style: Style,
221}
222
223impl StringPart {
224 pub fn normal<S: Into<String>>(content: S) -> StringPart {
225 StringPart { content: content.into(), style: Style::NoStyle }
226 }
227
228 pub fn highlighted<S: Into<String>>(content: S) -> StringPart {
229 StringPart { content: content.into(), style: Style::Highlight }
230 }
231}
232
233#[must_use]
238#[derive(Clone, Debug, Encodable, Decodable)]
239pub struct DiagInner {
240 pub(crate) level: Level,
243
244 pub messages: Vec<(DiagMessage, Style)>,
245 pub code: Option<ErrCode>,
246 pub lint_id: Option<LintExpectationId>,
247 pub span: MultiSpan,
248 pub children: Vec<Subdiag>,
249 pub suggestions: Suggestions,
250 pub args: DiagArgMap,
251
252 pub reserved_args: DiagArgMap,
254
255 pub sort_span: Span,
259
260 pub is_lint: Option<IsLint>,
261
262 pub long_ty_path: Option<PathBuf>,
263 pub(crate) emitted_at: DiagLocation,
266}
267
268impl DiagInner {
269 #[track_caller]
270 pub fn new<M: Into<DiagMessage>>(level: Level, message: M) -> Self {
271 DiagInner::new_with_messages(level, vec![(message.into(), Style::NoStyle)])
272 }
273
274 #[track_caller]
275 pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
276 DiagInner {
277 level,
278 lint_id: None,
279 messages,
280 code: None,
281 span: MultiSpan::new(),
282 children: vec![],
283 suggestions: Suggestions::Enabled(vec![]),
284 args: Default::default(),
285 reserved_args: Default::default(),
286 sort_span: DUMMY_SP,
287 is_lint: None,
288 long_ty_path: None,
289 emitted_at: DiagLocation::caller(),
290 }
291 }
292
293 #[inline(always)]
294 pub fn level(&self) -> Level {
295 self.level
296 }
297
298 pub fn is_error(&self) -> bool {
299 match self.level {
300 Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
301
302 Level::ForceWarning
303 | Level::Warning
304 | Level::Note
305 | Level::OnceNote
306 | Level::Help
307 | Level::OnceHelp
308 | Level::FailureNote
309 | Level::Allow
310 | Level::Expect => false,
311 }
312 }
313
314 pub(crate) fn has_future_breakage(&self) -> bool {
316 matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. }))
317 }
318
319 pub(crate) fn is_force_warn(&self) -> bool {
320 match self.level {
321 Level::ForceWarning => {
322 assert!(self.is_lint.is_some());
323 true
324 }
325 _ => false,
326 }
327 }
328
329 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
331 &self,
332 attr: impl Into<SubdiagMessage>,
333 ) -> DiagMessage {
334 let msg =
335 self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
336 msg.with_subdiagnostic_message(attr.into())
337 }
338
339 pub(crate) fn sub(
340 &mut self,
341 level: Level,
342 message: impl Into<SubdiagMessage>,
343 span: MultiSpan,
344 ) {
345 let sub = Subdiag {
346 level,
347 messages: vec![(
348 self.subdiagnostic_message_to_diagnostic_message(message),
349 Style::NoStyle,
350 )],
351 span,
352 };
353 self.children.push(sub);
354 }
355
356 pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) {
357 let name = name.into();
358 let value = arg.into_diag_arg(&mut self.long_ty_path);
359 debug_assert!(
361 !self.args.contains_key(&name) || self.args.get(&name) == Some(&value),
362 "arg {} already exists",
363 name
364 );
365 self.args.insert(name, value);
366 }
367
368 pub fn remove_arg(&mut self, name: &str) {
369 self.args.swap_remove(name);
370 }
371
372 pub fn store_args(&mut self) {
373 self.reserved_args = self.args.clone();
374 }
375
376 pub fn restore_args(&mut self) {
377 self.args = std::mem::take(&mut self.reserved_args);
378 }
379
380 pub fn emitted_at_sub_diag(&self) -> Subdiag {
381 let track = format!("-Ztrack-diagnostics: created at {}", self.emitted_at);
382 Subdiag {
383 level: crate::Level::Note,
384 messages: vec![(DiagMessage::Str(Cow::Owned(track)), Style::NoStyle)],
385 span: MultiSpan::new(),
386 }
387 }
388
389 fn keys(
391 &self,
392 ) -> (
393 &Level,
394 &[(DiagMessage, Style)],
395 &Option<ErrCode>,
396 &MultiSpan,
397 &[Subdiag],
398 &Suggestions,
399 Vec<(&DiagArgName, &DiagArgValue)>,
400 &Option<IsLint>,
401 ) {
402 (
403 &self.level,
404 &self.messages,
405 &self.code,
406 &self.span,
407 &self.children,
408 &self.suggestions,
409 self.args.iter().collect(),
410 &self.is_lint,
412 )
414 }
415}
416
417impl Hash for DiagInner {
418 fn hash<H>(&self, state: &mut H)
419 where
420 H: Hasher,
421 {
422 self.keys().hash(state);
423 }
424}
425
426impl PartialEq for DiagInner {
427 fn eq(&self, other: &Self) -> bool {
428 self.keys() == other.keys()
429 }
430}
431
432#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
435pub struct Subdiag {
436 pub level: Level,
437 pub messages: Vec<(DiagMessage, Style)>,
438 pub span: MultiSpan,
439}
440
441#[must_use]
454pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> {
455 pub dcx: DiagCtxtHandle<'a>,
456
457 diag: Option<Box<DiagInner>>,
467
468 _marker: PhantomData<G>,
469}
470
471impl<G> !Clone for Diag<'_, G> {}
474
475rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * size_of::<usize>());
476
477impl<G: EmissionGuarantee> Deref for Diag<'_, G> {
478 type Target = DiagInner;
479
480 fn deref(&self) -> &DiagInner {
481 self.diag.as_ref().unwrap()
482 }
483}
484
485impl<G: EmissionGuarantee> DerefMut for Diag<'_, G> {
486 fn deref_mut(&mut self) -> &mut DiagInner {
487 self.diag.as_mut().unwrap()
488 }
489}
490
491impl<G: EmissionGuarantee> Debug for Diag<'_, G> {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 self.diag.fmt(f)
494 }
495}
496
497macro_rules! with_fn {
516 {
517 $with_f:ident,
518 $(#[$attrs:meta])*
519 pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self {
520 $($body:tt)*
521 }
522 } => {
523 $(#[$attrs])*
525 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
526 pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self {
527 $($body)*
528 }
529
530 $(#[$attrs])*
532 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
533 pub fn $with_f(mut $self, $($name: $ty),*) -> Self {
534 $self.$f($($name),*);
535 $self
536 }
537 };
538}
539
540impl<'a, G: EmissionGuarantee> Diag<'a, G> {
541 #[rustc_lint_diagnostics]
542 #[track_caller]
543 pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into<DiagMessage>) -> Self {
544 Self::new_diagnostic(dcx, DiagInner::new(level, message))
545 }
546
547 pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> {
549 Diag { dcx, diag: self.diag.take(), _marker: PhantomData }
550 }
551
552 #[track_caller]
554 pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self {
555 debug!("Created new diagnostic");
556 Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
557 }
558
559 #[rustc_lint_diagnostics]
570 #[track_caller]
571 pub fn downgrade_to_delayed_bug(&mut self) {
572 assert!(
573 matches!(self.level, Level::Error | Level::DelayedBug),
574 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
575 self.level
576 );
577 self.level = Level::DelayedBug;
578 }
579
580 with_fn! { with_span_label,
581 #[rustc_lint_diagnostics]
594 pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagMessage>) -> &mut Self {
595 let msg = self.subdiagnostic_message_to_diagnostic_message(label);
596 self.span.push_span_label(span, msg);
597 self
598 } }
599
600 with_fn! { with_span_labels,
601 #[rustc_lint_diagnostics]
604 pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
605 for span in spans {
606 self.span_label(span, label.to_string());
607 }
608 self
609 } }
610
611 #[rustc_lint_diagnostics]
612 pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
613 let before = self.span.clone();
614 self.span(after);
615 for span_label in before.span_labels() {
616 if let Some(label) = span_label.label {
617 if span_label.is_primary && keep_label {
618 self.span.push_span_label(after, label);
619 } else {
620 self.span.push_span_label(span_label.span, label);
621 }
622 }
623 }
624 self
625 }
626
627 #[rustc_lint_diagnostics]
628 pub fn note_expected_found(
629 &mut self,
630 expected_label: &str,
631 expected: DiagStyledString,
632 found_label: &str,
633 found: DiagStyledString,
634 ) -> &mut Self {
635 self.note_expected_found_extra(
636 expected_label,
637 expected,
638 found_label,
639 found,
640 DiagStyledString::normal(""),
641 DiagStyledString::normal(""),
642 )
643 }
644
645 #[rustc_lint_diagnostics]
646 pub fn note_expected_found_extra(
647 &mut self,
648 expected_label: &str,
649 expected: DiagStyledString,
650 found_label: &str,
651 found: DiagStyledString,
652 expected_extra: DiagStyledString,
653 found_extra: DiagStyledString,
654 ) -> &mut Self {
655 let expected_label = expected_label.to_string();
656 let expected_label = if expected_label.is_empty() {
657 "expected".to_string()
658 } else {
659 format!("expected {expected_label}")
660 };
661 let found_label = found_label.to_string();
662 let found_label = if found_label.is_empty() {
663 "found".to_string()
664 } else {
665 format!("found {found_label}")
666 };
667 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
668 (expected_label.len() - found_label.len(), 0)
669 } else {
670 (0, found_label.len() - expected_label.len())
671 };
672 let mut msg = vec![StringPart::normal(format!(
673 "{}{} `",
674 " ".repeat(expected_padding),
675 expected_label
676 ))];
677 msg.extend(expected.0);
678 msg.push(StringPart::normal(format!("`")));
679 msg.extend(expected_extra.0);
680 msg.push(StringPart::normal(format!("\n")));
681 msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label)));
682 msg.extend(found.0);
683 msg.push(StringPart::normal(format!("`")));
684 msg.extend(found_extra.0);
685
686 self.highlighted_note(msg);
688 self
689 }
690
691 #[rustc_lint_diagnostics]
692 pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
693 self.highlighted_note(vec![
694 StringPart::normal(format!("`{name}` from trait: `")),
695 StringPart::highlighted(signature),
696 StringPart::normal("`"),
697 ]);
698 self
699 }
700
701 with_fn! { with_note,
702 #[rustc_lint_diagnostics]
704 pub fn note(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
705 self.sub(Level::Note, msg, MultiSpan::new());
706 self
707 } }
708
709 #[rustc_lint_diagnostics]
710 pub fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
711 self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
712 self
713 }
714
715 #[rustc_lint_diagnostics]
716 pub fn highlighted_span_note(
717 &mut self,
718 span: impl Into<MultiSpan>,
719 msg: Vec<StringPart>,
720 ) -> &mut Self {
721 self.sub_with_highlights(Level::Note, msg, span.into());
722 self
723 }
724
725 #[rustc_lint_diagnostics]
727 pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
728 self.sub(Level::OnceNote, msg, MultiSpan::new());
729 self
730 }
731
732 with_fn! { with_span_note,
733 #[rustc_lint_diagnostics]
736 pub fn span_note(
737 &mut self,
738 sp: impl Into<MultiSpan>,
739 msg: impl Into<SubdiagMessage>,
740 ) -> &mut Self {
741 self.sub(Level::Note, msg, sp.into());
742 self
743 } }
744
745 #[rustc_lint_diagnostics]
748 pub fn span_note_once<S: Into<MultiSpan>>(
749 &mut self,
750 sp: S,
751 msg: impl Into<SubdiagMessage>,
752 ) -> &mut Self {
753 self.sub(Level::OnceNote, msg, sp.into());
754 self
755 }
756
757 with_fn! { with_warn,
758 #[rustc_lint_diagnostics]
760 pub fn warn(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
761 self.sub(Level::Warning, msg, MultiSpan::new());
762 self
763 } }
764
765 #[rustc_lint_diagnostics]
768 pub fn span_warn<S: Into<MultiSpan>>(
769 &mut self,
770 sp: S,
771 msg: impl Into<SubdiagMessage>,
772 ) -> &mut Self {
773 self.sub(Level::Warning, msg, sp.into());
774 self
775 }
776
777 with_fn! { with_help,
778 #[rustc_lint_diagnostics]
780 pub fn help(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
781 self.sub(Level::Help, msg, MultiSpan::new());
782 self
783 } }
784
785 #[rustc_lint_diagnostics]
787 pub fn help_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
788 self.sub(Level::OnceHelp, msg, MultiSpan::new());
789 self
790 }
791
792 #[rustc_lint_diagnostics]
794 pub fn highlighted_help(&mut self, msg: Vec<StringPart>) -> &mut Self {
795 self.sub_with_highlights(Level::Help, msg, MultiSpan::new());
796 self
797 }
798
799 #[rustc_lint_diagnostics]
801 pub fn highlighted_span_help(
802 &mut self,
803 span: impl Into<MultiSpan>,
804 msg: Vec<StringPart>,
805 ) -> &mut Self {
806 self.sub_with_highlights(Level::Help, msg, span.into());
807 self
808 }
809
810 with_fn! { with_span_help,
811 #[rustc_lint_diagnostics]
814 pub fn span_help(
815 &mut self,
816 sp: impl Into<MultiSpan>,
817 msg: impl Into<SubdiagMessage>,
818 ) -> &mut Self {
819 self.sub(Level::Help, msg, sp.into());
820 self
821 } }
822
823 #[rustc_lint_diagnostics]
827 pub fn disable_suggestions(&mut self) -> &mut Self {
828 self.suggestions = Suggestions::Disabled;
829 self
830 }
831
832 #[rustc_lint_diagnostics]
837 pub fn seal_suggestions(&mut self) -> &mut Self {
838 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
839 let suggestions_slice = std::mem::take(suggestions).into_boxed_slice();
840 self.suggestions = Suggestions::Sealed(suggestions_slice);
841 }
842 self
843 }
844
845 #[rustc_lint_diagnostics]
850 fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
851 for subst in &suggestion.substitutions {
852 for part in &subst.parts {
853 let span = part.span;
854 let call_site = span.ctxt().outer_expn_data().call_site;
855 if span.in_derive_expansion() && span.overlaps_or_adjacent(call_site) {
856 return;
858 }
859 }
860 }
861
862 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
863 suggestions.push(suggestion);
864 }
865 }
866
867 with_fn! { with_multipart_suggestion,
868 #[rustc_lint_diagnostics]
871 pub fn multipart_suggestion(
872 &mut self,
873 msg: impl Into<SubdiagMessage>,
874 suggestion: Vec<(Span, String)>,
875 applicability: Applicability,
876 ) -> &mut Self {
877 self.multipart_suggestion_with_style(
878 msg,
879 suggestion,
880 applicability,
881 SuggestionStyle::ShowCode,
882 )
883 } }
884
885 #[rustc_lint_diagnostics]
888 pub fn multipart_suggestion_verbose(
889 &mut self,
890 msg: impl Into<SubdiagMessage>,
891 suggestion: Vec<(Span, String)>,
892 applicability: Applicability,
893 ) -> &mut Self {
894 self.multipart_suggestion_with_style(
895 msg,
896 suggestion,
897 applicability,
898 SuggestionStyle::ShowAlways,
899 )
900 }
901
902 #[rustc_lint_diagnostics]
904 pub fn multipart_suggestion_with_style(
905 &mut self,
906 msg: impl Into<SubdiagMessage>,
907 mut suggestion: Vec<(Span, String)>,
908 applicability: Applicability,
909 style: SuggestionStyle,
910 ) -> &mut Self {
911 let mut seen = crate::FxHashSet::default();
912 suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone())));
913
914 let parts = suggestion
915 .into_iter()
916 .map(|(span, snippet)| SubstitutionPart { snippet, span })
917 .collect::<Vec<_>>();
918
919 assert!(!parts.is_empty());
920 debug_assert_eq!(
921 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
922 None,
923 "Span must not be empty and have no suggestion",
924 );
925 debug_assert_eq!(
926 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
927 None,
928 "suggestion must not have overlapping parts",
929 );
930
931 self.push_suggestion(CodeSuggestion {
932 substitutions: vec![Substitution { parts }],
933 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
934 style,
935 applicability,
936 });
937 self
938 }
939
940 #[rustc_lint_diagnostics]
947 pub fn tool_only_multipart_suggestion(
948 &mut self,
949 msg: impl Into<SubdiagMessage>,
950 suggestion: Vec<(Span, String)>,
951 applicability: Applicability,
952 ) -> &mut Self {
953 self.multipart_suggestion_with_style(
954 msg,
955 suggestion,
956 applicability,
957 SuggestionStyle::CompletelyHidden,
958 )
959 }
960
961 with_fn! { with_span_suggestion,
962 #[rustc_lint_diagnostics]
980 pub fn span_suggestion(
981 &mut self,
982 sp: Span,
983 msg: impl Into<SubdiagMessage>,
984 suggestion: impl ToString,
985 applicability: Applicability,
986 ) -> &mut Self {
987 self.span_suggestion_with_style(
988 sp,
989 msg,
990 suggestion,
991 applicability,
992 SuggestionStyle::ShowCode,
993 );
994 self
995 } }
996
997 #[rustc_lint_diagnostics]
999 pub fn span_suggestion_with_style(
1000 &mut self,
1001 sp: Span,
1002 msg: impl Into<SubdiagMessage>,
1003 suggestion: impl ToString,
1004 applicability: Applicability,
1005 style: SuggestionStyle,
1006 ) -> &mut Self {
1007 debug_assert!(
1008 !(sp.is_empty() && suggestion.to_string().is_empty()),
1009 "Span must not be empty and have no suggestion"
1010 );
1011 self.push_suggestion(CodeSuggestion {
1012 substitutions: vec![Substitution {
1013 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
1014 }],
1015 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1016 style,
1017 applicability,
1018 });
1019 self
1020 }
1021
1022 with_fn! { with_span_suggestion_verbose,
1023 #[rustc_lint_diagnostics]
1025 pub fn span_suggestion_verbose(
1026 &mut self,
1027 sp: Span,
1028 msg: impl Into<SubdiagMessage>,
1029 suggestion: impl ToString,
1030 applicability: Applicability,
1031 ) -> &mut Self {
1032 self.span_suggestion_with_style(
1033 sp,
1034 msg,
1035 suggestion,
1036 applicability,
1037 SuggestionStyle::ShowAlways,
1038 );
1039 self
1040 } }
1041
1042 with_fn! { with_span_suggestions,
1043 #[rustc_lint_diagnostics]
1046 pub fn span_suggestions(
1047 &mut self,
1048 sp: Span,
1049 msg: impl Into<SubdiagMessage>,
1050 suggestions: impl IntoIterator<Item = String>,
1051 applicability: Applicability,
1052 ) -> &mut Self {
1053 self.span_suggestions_with_style(
1054 sp,
1055 msg,
1056 suggestions,
1057 applicability,
1058 SuggestionStyle::ShowCode,
1059 )
1060 } }
1061
1062 #[rustc_lint_diagnostics]
1063 pub fn span_suggestions_with_style(
1064 &mut self,
1065 sp: Span,
1066 msg: impl Into<SubdiagMessage>,
1067 suggestions: impl IntoIterator<Item = String>,
1068 applicability: Applicability,
1069 style: SuggestionStyle,
1070 ) -> &mut Self {
1071 let substitutions = suggestions
1072 .into_iter()
1073 .map(|snippet| {
1074 debug_assert!(
1075 !(sp.is_empty() && snippet.is_empty()),
1076 "Span `{sp:?}` must not be empty and have no suggestion"
1077 );
1078 Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
1079 })
1080 .collect();
1081 self.push_suggestion(CodeSuggestion {
1082 substitutions,
1083 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1084 style,
1085 applicability,
1086 });
1087 self
1088 }
1089
1090 #[rustc_lint_diagnostics]
1094 pub fn multipart_suggestions(
1095 &mut self,
1096 msg: impl Into<SubdiagMessage>,
1097 suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
1098 applicability: Applicability,
1099 ) -> &mut Self {
1100 let substitutions = suggestions
1101 .into_iter()
1102 .map(|sugg| {
1103 let mut parts = sugg
1104 .into_iter()
1105 .map(|(span, snippet)| SubstitutionPart { snippet, span })
1106 .collect::<Vec<_>>();
1107
1108 parts.sort_unstable_by_key(|part| part.span);
1109
1110 assert!(!parts.is_empty());
1111 debug_assert_eq!(
1112 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
1113 None,
1114 "Span must not be empty and have no suggestion",
1115 );
1116 debug_assert_eq!(
1117 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
1118 None,
1119 "suggestion must not have overlapping parts",
1120 );
1121
1122 Substitution { parts }
1123 })
1124 .collect();
1125
1126 self.push_suggestion(CodeSuggestion {
1127 substitutions,
1128 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1129 style: SuggestionStyle::ShowAlways,
1130 applicability,
1131 });
1132 self
1133 }
1134
1135 with_fn! { with_span_suggestion_short,
1136 #[rustc_lint_diagnostics]
1141 pub fn span_suggestion_short(
1142 &mut self,
1143 sp: Span,
1144 msg: impl Into<SubdiagMessage>,
1145 suggestion: impl ToString,
1146 applicability: Applicability,
1147 ) -> &mut Self {
1148 self.span_suggestion_with_style(
1149 sp,
1150 msg,
1151 suggestion,
1152 applicability,
1153 SuggestionStyle::HideCodeInline,
1154 );
1155 self
1156 } }
1157
1158 #[rustc_lint_diagnostics]
1165 pub fn span_suggestion_hidden(
1166 &mut self,
1167 sp: Span,
1168 msg: impl Into<SubdiagMessage>,
1169 suggestion: impl ToString,
1170 applicability: Applicability,
1171 ) -> &mut Self {
1172 self.span_suggestion_with_style(
1173 sp,
1174 msg,
1175 suggestion,
1176 applicability,
1177 SuggestionStyle::HideCodeAlways,
1178 );
1179 self
1180 }
1181
1182 with_fn! { with_tool_only_span_suggestion,
1183 #[rustc_lint_diagnostics]
1188 pub fn tool_only_span_suggestion(
1189 &mut self,
1190 sp: Span,
1191 msg: impl Into<SubdiagMessage>,
1192 suggestion: impl ToString,
1193 applicability: Applicability,
1194 ) -> &mut Self {
1195 self.span_suggestion_with_style(
1196 sp,
1197 msg,
1198 suggestion,
1199 applicability,
1200 SuggestionStyle::CompletelyHidden,
1201 );
1202 self
1203 } }
1204
1205 #[rustc_lint_diagnostics]
1210 pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self {
1211 subdiagnostic.add_to_diag(self);
1212 self
1213 }
1214
1215 pub fn eagerly_translate(&self, msg: impl Into<SubdiagMessage>) -> SubdiagMessage {
1221 let args = self.args.iter();
1222 let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into());
1223 self.dcx.eagerly_translate(msg, args)
1224 }
1225
1226 with_fn! { with_span,
1227 #[rustc_lint_diagnostics]
1229 pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
1230 self.span = sp.into();
1231 if let Some(span) = self.span.primary_span() {
1232 self.sort_span = span;
1233 }
1234 self
1235 } }
1236
1237 #[rustc_lint_diagnostics]
1238 pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
1239 self.is_lint = Some(IsLint { name, has_future_breakage });
1240 self
1241 }
1242
1243 with_fn! { with_code,
1244 #[rustc_lint_diagnostics]
1246 pub fn code(&mut self, code: ErrCode) -> &mut Self {
1247 self.code = Some(code);
1248 self
1249 } }
1250
1251 with_fn! { with_lint_id,
1252 #[rustc_lint_diagnostics]
1254 pub fn lint_id(
1255 &mut self,
1256 id: LintExpectationId,
1257 ) -> &mut Self {
1258 self.lint_id = Some(id);
1259 self
1260 } }
1261
1262 with_fn! { with_primary_message,
1263 #[rustc_lint_diagnostics]
1265 pub fn primary_message(&mut self, msg: impl Into<DiagMessage>) -> &mut Self {
1266 self.messages[0] = (msg.into(), Style::NoStyle);
1267 self
1268 } }
1269
1270 with_fn! { with_arg,
1271 #[rustc_lint_diagnostics]
1273 pub fn arg(
1274 &mut self,
1275 name: impl Into<DiagArgName>,
1276 arg: impl IntoDiagArg,
1277 ) -> &mut Self {
1278 self.deref_mut().arg(name, arg);
1279 self
1280 } }
1281
1282 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
1286 &self,
1287 attr: impl Into<SubdiagMessage>,
1288 ) -> DiagMessage {
1289 self.deref().subdiagnostic_message_to_diagnostic_message(attr)
1290 }
1291
1292 pub fn sub(&mut self, level: Level, message: impl Into<SubdiagMessage>, span: MultiSpan) {
1297 self.deref_mut().sub(level, message, span);
1298 }
1299
1300 fn sub_with_highlights(&mut self, level: Level, messages: Vec<StringPart>, span: MultiSpan) {
1303 let messages = messages
1304 .into_iter()
1305 .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style))
1306 .collect();
1307 let sub = Subdiag { level, messages, span };
1308 self.children.push(sub);
1309 }
1310
1311 fn take_diag(&mut self) -> DiagInner {
1315 if let Some(path) = &self.long_ty_path {
1316 self.note(format!(
1317 "the full name for the type has been written to '{}'",
1318 path.display()
1319 ));
1320 self.note("consider using `--verbose` to print the full type name to the console");
1321 }
1322 *self.diag.take().unwrap()
1323 }
1324
1325 pub fn long_ty_path(&mut self) -> &mut Option<PathBuf> {
1343 &mut self.long_ty_path
1344 }
1345
1346 pub fn with_long_ty_path(mut self, long_ty_path: Option<PathBuf>) -> Self {
1347 self.long_ty_path = long_ty_path;
1348 self
1349 }
1350
1351 fn emit_producing_nothing(mut self) {
1353 let diag = self.take_diag();
1354 self.dcx.emit_diagnostic(diag);
1355 }
1356
1357 fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
1359 let diag = self.take_diag();
1360
1361 assert!(
1370 matches!(diag.level, Level::Error | Level::DelayedBug),
1371 "invalid diagnostic level ({:?})",
1372 diag.level,
1373 );
1374
1375 let guar = self.dcx.emit_diagnostic(diag);
1376 guar.unwrap()
1377 }
1378
1379 #[track_caller]
1381 pub fn emit(self) -> G::EmitResult {
1382 G::emit_producing_guarantee(self)
1383 }
1384
1385 #[track_caller]
1390 pub fn emit_unless_delay(mut self, delay: bool) -> G::EmitResult {
1391 if delay {
1392 self.downgrade_to_delayed_bug();
1393 }
1394 self.emit()
1395 }
1396
1397 pub fn cancel(mut self) {
1400 self.diag = None;
1401 drop(self);
1402 }
1403
1404 pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
1406 let diag = self.take_diag();
1407 self.dcx.stash_diagnostic(span, key, diag)
1408 }
1409
1410 #[track_caller]
1421 pub fn delay_as_bug(mut self) -> G::EmitResult {
1422 self.downgrade_to_delayed_bug();
1423 self.emit()
1424 }
1425
1426 pub fn remove_arg(&mut self, name: &str) {
1427 if let Some(diag) = self.diag.as_mut() {
1428 diag.remove_arg(name);
1429 }
1430 }
1431}
1432
1433impl<G: EmissionGuarantee> Drop for Diag<'_, G> {
1436 fn drop(&mut self) {
1437 match self.diag.take() {
1438 Some(diag) if !panicking() => {
1439 self.dcx.emit_diagnostic(DiagInner::new(
1440 Level::Bug,
1441 DiagMessage::from("the following error was constructed but not emitted"),
1442 ));
1443 self.dcx.emit_diagnostic(*diag);
1444 panic!("error was constructed but not emitted");
1445 }
1446 _ => {}
1447 }
1448 }
1449}
1450
1451#[macro_export]
1452macro_rules! struct_span_code_err {
1453 ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
1454 $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
1455 })
1456}