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::{FluentValue, fluent_value_from_str_list_sep_by_and};
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 DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue);
29
30pub type DiagArgName = Cow<'static, str>;
32
33#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
36pub enum DiagArgValue {
37 Str(Cow<'static, str>),
38 Number(i32),
42 StrListSepByAnd(Vec<Cow<'static, str>>),
43}
44
45pub type DiagArgMap = FxIndexMap<DiagArgName, DiagArgValue>;
46
47pub trait EmissionGuarantee: Sized {
50 type EmitResult = Self;
53
54 #[track_caller]
58 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult;
59}
60
61impl EmissionGuarantee for ErrorGuaranteed {
62 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
63 diag.emit_producing_error_guaranteed()
64 }
65}
66
67impl EmissionGuarantee for () {
68 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
69 diag.emit_producing_nothing();
70 }
71}
72
73#[derive(Copy, Clone)]
76pub struct BugAbort;
77
78impl EmissionGuarantee for BugAbort {
79 type EmitResult = !;
80
81 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
82 diag.emit_producing_nothing();
83 panic::panic_any(ExplicitBug);
84 }
85}
86
87#[derive(Copy, Clone)]
90pub struct FatalAbort;
91
92impl EmissionGuarantee for FatalAbort {
93 type EmitResult = !;
94
95 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
96 diag.emit_producing_nothing();
97 crate::FatalError.raise()
98 }
99}
100
101impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
102 fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult {
103 diag.emit_producing_nothing();
104 rustc_span::fatal_error::FatalError
105 }
106}
107
108#[rustc_diagnostic_item = "Diagnostic"]
130pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
131 #[must_use]
133 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>;
134}
135
136impl<'a, T, G> Diagnostic<'a, G> for Spanned<T>
137where
138 T: Diagnostic<'a, G>,
139 G: EmissionGuarantee,
140{
141 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
142 self.node.into_diag(dcx, level).with_span(self.span)
143 }
144}
145
146pub trait IntoDiagArg {
151 fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue;
158}
159
160impl IntoDiagArg for DiagArgValue {
161 fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
162 self
163 }
164}
165
166impl From<DiagArgValue> for FluentValue<'static> {
167 fn from(val: DiagArgValue) -> Self {
168 match val {
169 DiagArgValue::Str(s) => From::from(s),
170 DiagArgValue::Number(n) => From::from(n),
171 DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l),
172 }
173 }
174}
175
176#[rustc_diagnostic_item = "Subdiagnostic"]
179pub trait Subdiagnostic
180where
181 Self: Sized,
182{
183 fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>);
185}
186
187#[rustc_diagnostic_item = "LintDiagnostic"]
190pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
191 fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
193}
194
195#[derive(Clone, Debug, Encodable, Decodable)]
196pub(crate) struct DiagLocation {
197 file: Cow<'static, str>,
198 line: u32,
199 col: u32,
200}
201
202impl DiagLocation {
203 #[track_caller]
204 fn caller() -> Self {
205 let loc = panic::Location::caller();
206 DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() }
207 }
208}
209
210impl fmt::Display for DiagLocation {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 write!(f, "{}:{}:{}", self.file, self.line, self.col)
213 }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
217pub struct IsLint {
218 pub(crate) name: String,
220 has_future_breakage: bool,
222}
223
224#[derive(Debug, PartialEq, Eq)]
225pub struct DiagStyledString(pub Vec<StringPart>);
226
227impl DiagStyledString {
228 pub fn new() -> DiagStyledString {
229 DiagStyledString(vec![])
230 }
231 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
232 self.0.push(StringPart::normal(t));
233 }
234 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
235 self.0.push(StringPart::highlighted(t));
236 }
237 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
238 if highlight {
239 self.push_highlighted(t);
240 } else {
241 self.push_normal(t);
242 }
243 }
244 pub fn normal<S: Into<String>>(t: S) -> DiagStyledString {
245 DiagStyledString(vec![StringPart::normal(t)])
246 }
247
248 pub fn highlighted<S: Into<String>>(t: S) -> DiagStyledString {
249 DiagStyledString(vec![StringPart::highlighted(t)])
250 }
251
252 pub fn content(&self) -> String {
253 self.0.iter().map(|x| x.content.as_str()).collect::<String>()
254 }
255}
256
257#[derive(Debug, PartialEq, Eq)]
258pub struct StringPart {
259 content: String,
260 style: Style,
261}
262
263impl StringPart {
264 pub fn normal<S: Into<String>>(content: S) -> StringPart {
265 StringPart { content: content.into(), style: Style::NoStyle }
266 }
267
268 pub fn highlighted<S: Into<String>>(content: S) -> StringPart {
269 StringPart { content: content.into(), style: Style::Highlight }
270 }
271}
272
273#[must_use]
278#[derive(Clone, Debug, Encodable, Decodable)]
279pub struct DiagInner {
280 pub(crate) level: Level,
283
284 pub messages: Vec<(DiagMessage, Style)>,
285 pub code: Option<ErrCode>,
286 pub lint_id: Option<LintExpectationId>,
287 pub span: MultiSpan,
288 pub children: Vec<Subdiag>,
289 pub suggestions: Suggestions,
290 pub args: DiagArgMap,
291
292 pub reserved_args: DiagArgMap,
294
295 pub sort_span: Span,
299
300 pub is_lint: Option<IsLint>,
301
302 pub long_ty_path: Option<PathBuf>,
303 pub(crate) emitted_at: DiagLocation,
306}
307
308impl DiagInner {
309 #[track_caller]
310 pub fn new<M: Into<DiagMessage>>(level: Level, message: M) -> Self {
311 DiagInner::new_with_messages(level, vec![(message.into(), Style::NoStyle)])
312 }
313
314 #[track_caller]
315 pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
316 DiagInner {
317 level,
318 lint_id: None,
319 messages,
320 code: None,
321 span: MultiSpan::new(),
322 children: vec![],
323 suggestions: Suggestions::Enabled(vec![]),
324 args: Default::default(),
325 reserved_args: Default::default(),
326 sort_span: DUMMY_SP,
327 is_lint: None,
328 long_ty_path: None,
329 emitted_at: DiagLocation::caller(),
330 }
331 }
332
333 #[inline(always)]
334 pub fn level(&self) -> Level {
335 self.level
336 }
337
338 pub fn is_error(&self) -> bool {
339 match self.level {
340 Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
341
342 Level::ForceWarning
343 | Level::Warning
344 | Level::Note
345 | Level::OnceNote
346 | Level::Help
347 | Level::OnceHelp
348 | Level::FailureNote
349 | Level::Allow
350 | Level::Expect => false,
351 }
352 }
353
354 pub(crate) fn has_future_breakage(&self) -> bool {
356 matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. }))
357 }
358
359 pub(crate) fn is_force_warn(&self) -> bool {
360 match self.level {
361 Level::ForceWarning => {
362 assert!(self.is_lint.is_some());
363 true
364 }
365 _ => false,
366 }
367 }
368
369 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
371 &self,
372 attr: impl Into<SubdiagMessage>,
373 ) -> DiagMessage {
374 let msg =
375 self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
376 msg.with_subdiagnostic_message(attr.into())
377 }
378
379 pub(crate) fn sub(
380 &mut self,
381 level: Level,
382 message: impl Into<SubdiagMessage>,
383 span: MultiSpan,
384 ) {
385 let sub = Subdiag {
386 level,
387 messages: vec![(
388 self.subdiagnostic_message_to_diagnostic_message(message),
389 Style::NoStyle,
390 )],
391 span,
392 };
393 self.children.push(sub);
394 }
395
396 pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) {
397 let name = name.into();
398 let value = arg.into_diag_arg(&mut self.long_ty_path);
399 debug_assert!(
401 !self.args.contains_key(&name) || self.args.get(&name) == Some(&value),
402 "arg {} already exists",
403 name
404 );
405 self.args.insert(name, value);
406 }
407
408 pub fn remove_arg(&mut self, name: &str) {
409 self.args.swap_remove(name);
410 }
411
412 pub fn store_args(&mut self) {
413 self.reserved_args = self.args.clone();
414 }
415
416 pub fn restore_args(&mut self) {
417 self.args = std::mem::take(&mut self.reserved_args);
418 }
419
420 pub fn emitted_at_sub_diag(&self) -> Subdiag {
421 let track = format!("-Ztrack-diagnostics: created at {}", self.emitted_at);
422 Subdiag {
423 level: crate::Level::Note,
424 messages: vec![(DiagMessage::Str(Cow::Owned(track)), Style::NoStyle)],
425 span: MultiSpan::new(),
426 }
427 }
428
429 fn keys(
431 &self,
432 ) -> (
433 &Level,
434 &[(DiagMessage, Style)],
435 &Option<ErrCode>,
436 &MultiSpan,
437 &[Subdiag],
438 &Suggestions,
439 Vec<(&DiagArgName, &DiagArgValue)>,
440 &Option<IsLint>,
441 ) {
442 (
443 &self.level,
444 &self.messages,
445 &self.code,
446 &self.span,
447 &self.children,
448 &self.suggestions,
449 self.args.iter().collect(),
450 &self.is_lint,
452 )
454 }
455}
456
457impl Hash for DiagInner {
458 fn hash<H>(&self, state: &mut H)
459 where
460 H: Hasher,
461 {
462 self.keys().hash(state);
463 }
464}
465
466impl PartialEq for DiagInner {
467 fn eq(&self, other: &Self) -> bool {
468 self.keys() == other.keys()
469 }
470}
471
472#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
475pub struct Subdiag {
476 pub level: Level,
477 pub messages: Vec<(DiagMessage, Style)>,
478 pub span: MultiSpan,
479}
480
481#[must_use]
494pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> {
495 pub dcx: DiagCtxtHandle<'a>,
496
497 diag: Option<Box<DiagInner>>,
507
508 _marker: PhantomData<G>,
509}
510
511impl<G> !Clone for Diag<'_, G> {}
514
515rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * size_of::<usize>());
516
517impl<G: EmissionGuarantee> Deref for Diag<'_, G> {
518 type Target = DiagInner;
519
520 fn deref(&self) -> &DiagInner {
521 self.diag.as_ref().unwrap()
522 }
523}
524
525impl<G: EmissionGuarantee> DerefMut for Diag<'_, G> {
526 fn deref_mut(&mut self) -> &mut DiagInner {
527 self.diag.as_mut().unwrap()
528 }
529}
530
531impl<G: EmissionGuarantee> Debug for Diag<'_, G> {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 self.diag.fmt(f)
534 }
535}
536
537macro_rules! with_fn {
556 {
557 $with_f:ident,
558 $(#[$attrs:meta])*
559 pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self {
560 $($body:tt)*
561 }
562 } => {
563 $(#[$attrs])*
565 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
566 pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self {
567 $($body)*
568 }
569
570 $(#[$attrs])*
572 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
573 pub fn $with_f(mut $self, $($name: $ty),*) -> Self {
574 $self.$f($($name),*);
575 $self
576 }
577 };
578}
579
580impl<'a, G: EmissionGuarantee> Diag<'a, G> {
581 #[rustc_lint_diagnostics]
582 #[track_caller]
583 pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into<DiagMessage>) -> Self {
584 Self::new_diagnostic(dcx, DiagInner::new(level, message))
585 }
586
587 pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> {
589 Diag { dcx, diag: self.diag.take(), _marker: PhantomData }
590 }
591
592 #[track_caller]
594 pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self {
595 debug!("Created new diagnostic");
596 Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
597 }
598
599 #[rustc_lint_diagnostics]
610 #[track_caller]
611 pub fn downgrade_to_delayed_bug(&mut self) {
612 assert!(
613 matches!(self.level, Level::Error | Level::DelayedBug),
614 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
615 self.level
616 );
617 self.level = Level::DelayedBug;
618 }
619
620 with_fn! { with_span_label,
621 #[rustc_lint_diagnostics]
634 pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagMessage>) -> &mut Self {
635 let msg = self.subdiagnostic_message_to_diagnostic_message(label);
636 self.span.push_span_label(span, msg);
637 self
638 } }
639
640 with_fn! { with_span_labels,
641 #[rustc_lint_diagnostics]
644 pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
645 for span in spans {
646 self.span_label(span, label.to_string());
647 }
648 self
649 } }
650
651 #[rustc_lint_diagnostics]
652 pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
653 let before = self.span.clone();
654 self.span(after);
655 for span_label in before.span_labels() {
656 if let Some(label) = span_label.label {
657 if span_label.is_primary && keep_label {
658 self.span.push_span_label(after, label);
659 } else {
660 self.span.push_span_label(span_label.span, label);
661 }
662 }
663 }
664 self
665 }
666
667 #[rustc_lint_diagnostics]
668 pub fn note_expected_found(
669 &mut self,
670 expected_label: &str,
671 expected: DiagStyledString,
672 found_label: &str,
673 found: DiagStyledString,
674 ) -> &mut Self {
675 self.note_expected_found_extra(
676 expected_label,
677 expected,
678 found_label,
679 found,
680 DiagStyledString::normal(""),
681 DiagStyledString::normal(""),
682 )
683 }
684
685 #[rustc_lint_diagnostics]
686 pub fn note_expected_found_extra(
687 &mut self,
688 expected_label: &str,
689 expected: DiagStyledString,
690 found_label: &str,
691 found: DiagStyledString,
692 expected_extra: DiagStyledString,
693 found_extra: DiagStyledString,
694 ) -> &mut Self {
695 let expected_label = expected_label.to_string();
696 let expected_label = if expected_label.is_empty() {
697 "expected".to_string()
698 } else {
699 format!("expected {expected_label}")
700 };
701 let found_label = found_label.to_string();
702 let found_label = if found_label.is_empty() {
703 "found".to_string()
704 } else {
705 format!("found {found_label}")
706 };
707 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
708 (expected_label.len() - found_label.len(), 0)
709 } else {
710 (0, found_label.len() - expected_label.len())
711 };
712 let mut msg = vec![StringPart::normal(format!(
713 "{}{} `",
714 " ".repeat(expected_padding),
715 expected_label
716 ))];
717 msg.extend(expected.0);
718 msg.push(StringPart::normal(format!("`")));
719 msg.extend(expected_extra.0);
720 msg.push(StringPart::normal(format!("\n")));
721 msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label)));
722 msg.extend(found.0);
723 msg.push(StringPart::normal(format!("`")));
724 msg.extend(found_extra.0);
725
726 self.highlighted_note(msg);
728 self
729 }
730
731 #[rustc_lint_diagnostics]
732 pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
733 self.highlighted_note(vec![
734 StringPart::normal(format!("`{name}` from trait: `")),
735 StringPart::highlighted(signature),
736 StringPart::normal("`"),
737 ]);
738 self
739 }
740
741 with_fn! { with_note,
742 #[rustc_lint_diagnostics]
744 pub fn note(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
745 self.sub(Level::Note, msg, MultiSpan::new());
746 self
747 } }
748
749 #[rustc_lint_diagnostics]
750 pub fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
751 self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
752 self
753 }
754
755 #[rustc_lint_diagnostics]
756 pub fn highlighted_span_note(
757 &mut self,
758 span: impl Into<MultiSpan>,
759 msg: Vec<StringPart>,
760 ) -> &mut Self {
761 self.sub_with_highlights(Level::Note, msg, span.into());
762 self
763 }
764
765 #[rustc_lint_diagnostics]
767 pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
768 self.sub(Level::OnceNote, msg, MultiSpan::new());
769 self
770 }
771
772 with_fn! { with_span_note,
773 #[rustc_lint_diagnostics]
776 pub fn span_note(
777 &mut self,
778 sp: impl Into<MultiSpan>,
779 msg: impl Into<SubdiagMessage>,
780 ) -> &mut Self {
781 self.sub(Level::Note, msg, sp.into());
782 self
783 } }
784
785 #[rustc_lint_diagnostics]
788 pub fn span_note_once<S: Into<MultiSpan>>(
789 &mut self,
790 sp: S,
791 msg: impl Into<SubdiagMessage>,
792 ) -> &mut Self {
793 self.sub(Level::OnceNote, msg, sp.into());
794 self
795 }
796
797 with_fn! { with_warn,
798 #[rustc_lint_diagnostics]
800 pub fn warn(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
801 self.sub(Level::Warning, msg, MultiSpan::new());
802 self
803 } }
804
805 #[rustc_lint_diagnostics]
808 pub fn span_warn<S: Into<MultiSpan>>(
809 &mut self,
810 sp: S,
811 msg: impl Into<SubdiagMessage>,
812 ) -> &mut Self {
813 self.sub(Level::Warning, msg, sp.into());
814 self
815 }
816
817 with_fn! { with_help,
818 #[rustc_lint_diagnostics]
820 pub fn help(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
821 self.sub(Level::Help, msg, MultiSpan::new());
822 self
823 } }
824
825 #[rustc_lint_diagnostics]
827 pub fn help_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
828 self.sub(Level::OnceHelp, msg, MultiSpan::new());
829 self
830 }
831
832 #[rustc_lint_diagnostics]
834 pub fn highlighted_help(&mut self, msg: Vec<StringPart>) -> &mut Self {
835 self.sub_with_highlights(Level::Help, msg, MultiSpan::new());
836 self
837 }
838
839 #[rustc_lint_diagnostics]
841 pub fn highlighted_span_help(
842 &mut self,
843 span: impl Into<MultiSpan>,
844 msg: Vec<StringPart>,
845 ) -> &mut Self {
846 self.sub_with_highlights(Level::Help, msg, span.into());
847 self
848 }
849
850 #[rustc_lint_diagnostics]
853 pub fn span_help<S: Into<MultiSpan>>(
854 &mut self,
855 sp: S,
856 msg: impl Into<SubdiagMessage>,
857 ) -> &mut Self {
858 self.sub(Level::Help, msg, sp.into());
859 self
860 }
861
862 #[rustc_lint_diagnostics]
866 pub fn disable_suggestions(&mut self) -> &mut Self {
867 self.suggestions = Suggestions::Disabled;
868 self
869 }
870
871 #[rustc_lint_diagnostics]
876 pub fn seal_suggestions(&mut self) -> &mut Self {
877 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
878 let suggestions_slice = std::mem::take(suggestions).into_boxed_slice();
879 self.suggestions = Suggestions::Sealed(suggestions_slice);
880 }
881 self
882 }
883
884 #[rustc_lint_diagnostics]
889 fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
890 for subst in &suggestion.substitutions {
891 for part in &subst.parts {
892 let span = part.span;
893 let call_site = span.ctxt().outer_expn_data().call_site;
894 if span.in_derive_expansion() && span.overlaps_or_adjacent(call_site) {
895 return;
897 }
898 }
899 }
900
901 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
902 suggestions.push(suggestion);
903 }
904 }
905
906 with_fn! { with_multipart_suggestion,
907 #[rustc_lint_diagnostics]
910 pub fn multipart_suggestion(
911 &mut self,
912 msg: impl Into<SubdiagMessage>,
913 suggestion: Vec<(Span, String)>,
914 applicability: Applicability,
915 ) -> &mut Self {
916 self.multipart_suggestion_with_style(
917 msg,
918 suggestion,
919 applicability,
920 SuggestionStyle::ShowCode,
921 )
922 } }
923
924 #[rustc_lint_diagnostics]
927 pub fn multipart_suggestion_verbose(
928 &mut self,
929 msg: impl Into<SubdiagMessage>,
930 suggestion: Vec<(Span, String)>,
931 applicability: Applicability,
932 ) -> &mut Self {
933 self.multipart_suggestion_with_style(
934 msg,
935 suggestion,
936 applicability,
937 SuggestionStyle::ShowAlways,
938 )
939 }
940
941 #[rustc_lint_diagnostics]
943 pub fn multipart_suggestion_with_style(
944 &mut self,
945 msg: impl Into<SubdiagMessage>,
946 mut suggestion: Vec<(Span, String)>,
947 applicability: Applicability,
948 style: SuggestionStyle,
949 ) -> &mut Self {
950 let mut seen = crate::FxHashSet::default();
951 suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone())));
952
953 let parts = suggestion
954 .into_iter()
955 .map(|(span, snippet)| SubstitutionPart { snippet, span })
956 .collect::<Vec<_>>();
957
958 assert!(!parts.is_empty());
959 debug_assert_eq!(
960 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
961 None,
962 "Span must not be empty and have no suggestion",
963 );
964 debug_assert_eq!(
965 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
966 None,
967 "suggestion must not have overlapping parts",
968 );
969
970 self.push_suggestion(CodeSuggestion {
971 substitutions: vec![Substitution { parts }],
972 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
973 style,
974 applicability,
975 });
976 self
977 }
978
979 #[rustc_lint_diagnostics]
986 pub fn tool_only_multipart_suggestion(
987 &mut self,
988 msg: impl Into<SubdiagMessage>,
989 suggestion: Vec<(Span, String)>,
990 applicability: Applicability,
991 ) -> &mut Self {
992 self.multipart_suggestion_with_style(
993 msg,
994 suggestion,
995 applicability,
996 SuggestionStyle::CompletelyHidden,
997 )
998 }
999
1000 with_fn! { with_span_suggestion,
1001 #[rustc_lint_diagnostics]
1019 pub fn span_suggestion(
1020 &mut self,
1021 sp: Span,
1022 msg: impl Into<SubdiagMessage>,
1023 suggestion: impl ToString,
1024 applicability: Applicability,
1025 ) -> &mut Self {
1026 self.span_suggestion_with_style(
1027 sp,
1028 msg,
1029 suggestion,
1030 applicability,
1031 SuggestionStyle::ShowCode,
1032 );
1033 self
1034 } }
1035
1036 #[rustc_lint_diagnostics]
1038 pub fn span_suggestion_with_style(
1039 &mut self,
1040 sp: Span,
1041 msg: impl Into<SubdiagMessage>,
1042 suggestion: impl ToString,
1043 applicability: Applicability,
1044 style: SuggestionStyle,
1045 ) -> &mut Self {
1046 debug_assert!(
1047 !(sp.is_empty() && suggestion.to_string().is_empty()),
1048 "Span must not be empty and have no suggestion"
1049 );
1050 self.push_suggestion(CodeSuggestion {
1051 substitutions: vec![Substitution {
1052 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
1053 }],
1054 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1055 style,
1056 applicability,
1057 });
1058 self
1059 }
1060
1061 with_fn! { with_span_suggestion_verbose,
1062 #[rustc_lint_diagnostics]
1064 pub fn span_suggestion_verbose(
1065 &mut self,
1066 sp: Span,
1067 msg: impl Into<SubdiagMessage>,
1068 suggestion: impl ToString,
1069 applicability: Applicability,
1070 ) -> &mut Self {
1071 self.span_suggestion_with_style(
1072 sp,
1073 msg,
1074 suggestion,
1075 applicability,
1076 SuggestionStyle::ShowAlways,
1077 );
1078 self
1079 } }
1080
1081 with_fn! { with_span_suggestions,
1082 #[rustc_lint_diagnostics]
1085 pub fn span_suggestions(
1086 &mut self,
1087 sp: Span,
1088 msg: impl Into<SubdiagMessage>,
1089 suggestions: impl IntoIterator<Item = String>,
1090 applicability: Applicability,
1091 ) -> &mut Self {
1092 self.span_suggestions_with_style(
1093 sp,
1094 msg,
1095 suggestions,
1096 applicability,
1097 SuggestionStyle::ShowCode,
1098 )
1099 } }
1100
1101 #[rustc_lint_diagnostics]
1102 pub fn span_suggestions_with_style(
1103 &mut self,
1104 sp: Span,
1105 msg: impl Into<SubdiagMessage>,
1106 suggestions: impl IntoIterator<Item = String>,
1107 applicability: Applicability,
1108 style: SuggestionStyle,
1109 ) -> &mut Self {
1110 let substitutions = suggestions
1111 .into_iter()
1112 .map(|snippet| {
1113 debug_assert!(
1114 !(sp.is_empty() && snippet.is_empty()),
1115 "Span must not be empty and have no suggestion"
1116 );
1117 Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
1118 })
1119 .collect();
1120 self.push_suggestion(CodeSuggestion {
1121 substitutions,
1122 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1123 style,
1124 applicability,
1125 });
1126 self
1127 }
1128
1129 #[rustc_lint_diagnostics]
1133 pub fn multipart_suggestions(
1134 &mut self,
1135 msg: impl Into<SubdiagMessage>,
1136 suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
1137 applicability: Applicability,
1138 ) -> &mut Self {
1139 let substitutions = suggestions
1140 .into_iter()
1141 .map(|sugg| {
1142 let mut parts = sugg
1143 .into_iter()
1144 .map(|(span, snippet)| SubstitutionPart { snippet, span })
1145 .collect::<Vec<_>>();
1146
1147 parts.sort_unstable_by_key(|part| part.span);
1148
1149 assert!(!parts.is_empty());
1150 debug_assert_eq!(
1151 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
1152 None,
1153 "Span must not be empty and have no suggestion",
1154 );
1155 debug_assert_eq!(
1156 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
1157 None,
1158 "suggestion must not have overlapping parts",
1159 );
1160
1161 Substitution { parts }
1162 })
1163 .collect();
1164
1165 self.push_suggestion(CodeSuggestion {
1166 substitutions,
1167 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1168 style: SuggestionStyle::ShowCode,
1169 applicability,
1170 });
1171 self
1172 }
1173
1174 with_fn! { with_span_suggestion_short,
1175 #[rustc_lint_diagnostics]
1180 pub fn span_suggestion_short(
1181 &mut self,
1182 sp: Span,
1183 msg: impl Into<SubdiagMessage>,
1184 suggestion: impl ToString,
1185 applicability: Applicability,
1186 ) -> &mut Self {
1187 self.span_suggestion_with_style(
1188 sp,
1189 msg,
1190 suggestion,
1191 applicability,
1192 SuggestionStyle::HideCodeInline,
1193 );
1194 self
1195 } }
1196
1197 #[rustc_lint_diagnostics]
1204 pub fn span_suggestion_hidden(
1205 &mut self,
1206 sp: Span,
1207 msg: impl Into<SubdiagMessage>,
1208 suggestion: impl ToString,
1209 applicability: Applicability,
1210 ) -> &mut Self {
1211 self.span_suggestion_with_style(
1212 sp,
1213 msg,
1214 suggestion,
1215 applicability,
1216 SuggestionStyle::HideCodeAlways,
1217 );
1218 self
1219 }
1220
1221 with_fn! { with_tool_only_span_suggestion,
1222 #[rustc_lint_diagnostics]
1227 pub fn tool_only_span_suggestion(
1228 &mut self,
1229 sp: Span,
1230 msg: impl Into<SubdiagMessage>,
1231 suggestion: impl ToString,
1232 applicability: Applicability,
1233 ) -> &mut Self {
1234 self.span_suggestion_with_style(
1235 sp,
1236 msg,
1237 suggestion,
1238 applicability,
1239 SuggestionStyle::CompletelyHidden,
1240 );
1241 self
1242 } }
1243
1244 #[rustc_lint_diagnostics]
1249 pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self {
1250 subdiagnostic.add_to_diag(self);
1251 self
1252 }
1253
1254 pub fn eagerly_translate(&self, msg: impl Into<SubdiagMessage>) -> SubdiagMessage {
1260 let args = self.args.iter();
1261 let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into());
1262 self.dcx.eagerly_translate(msg, args)
1263 }
1264
1265 with_fn! { with_span,
1266 #[rustc_lint_diagnostics]
1268 pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
1269 self.span = sp.into();
1270 if let Some(span) = self.span.primary_span() {
1271 self.sort_span = span;
1272 }
1273 self
1274 } }
1275
1276 #[rustc_lint_diagnostics]
1277 pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
1278 self.is_lint = Some(IsLint { name, has_future_breakage });
1279 self
1280 }
1281
1282 with_fn! { with_code,
1283 #[rustc_lint_diagnostics]
1285 pub fn code(&mut self, code: ErrCode) -> &mut Self {
1286 self.code = Some(code);
1287 self
1288 } }
1289
1290 with_fn! { with_lint_id,
1291 #[rustc_lint_diagnostics]
1293 pub fn lint_id(
1294 &mut self,
1295 id: LintExpectationId,
1296 ) -> &mut Self {
1297 self.lint_id = Some(id);
1298 self
1299 } }
1300
1301 with_fn! { with_primary_message,
1302 #[rustc_lint_diagnostics]
1304 pub fn primary_message(&mut self, msg: impl Into<DiagMessage>) -> &mut Self {
1305 self.messages[0] = (msg.into(), Style::NoStyle);
1306 self
1307 } }
1308
1309 with_fn! { with_arg,
1310 #[rustc_lint_diagnostics]
1312 pub fn arg(
1313 &mut self,
1314 name: impl Into<DiagArgName>,
1315 arg: impl IntoDiagArg,
1316 ) -> &mut Self {
1317 self.deref_mut().arg(name, arg);
1318 self
1319 } }
1320
1321 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
1325 &self,
1326 attr: impl Into<SubdiagMessage>,
1327 ) -> DiagMessage {
1328 self.deref().subdiagnostic_message_to_diagnostic_message(attr)
1329 }
1330
1331 pub fn sub(&mut self, level: Level, message: impl Into<SubdiagMessage>, span: MultiSpan) {
1336 self.deref_mut().sub(level, message, span);
1337 }
1338
1339 fn sub_with_highlights(&mut self, level: Level, messages: Vec<StringPart>, span: MultiSpan) {
1342 let messages = messages
1343 .into_iter()
1344 .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style))
1345 .collect();
1346 let sub = Subdiag { level, messages, span };
1347 self.children.push(sub);
1348 }
1349
1350 fn take_diag(&mut self) -> DiagInner {
1354 if let Some(path) = &self.long_ty_path {
1355 self.note(format!(
1356 "the full name for the type has been written to '{}'",
1357 path.display()
1358 ));
1359 self.note("consider using `--verbose` to print the full type name to the console");
1360 }
1361 *self.diag.take().unwrap()
1362 }
1363
1364 pub fn long_ty_path(&mut self) -> &mut Option<PathBuf> {
1382 &mut self.long_ty_path
1383 }
1384
1385 fn emit_producing_nothing(mut self) {
1387 let diag = self.take_diag();
1388 self.dcx.emit_diagnostic(diag);
1389 }
1390
1391 fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
1393 let diag = self.take_diag();
1394
1395 assert!(
1404 matches!(diag.level, Level::Error | Level::DelayedBug),
1405 "invalid diagnostic level ({:?})",
1406 diag.level,
1407 );
1408
1409 let guar = self.dcx.emit_diagnostic(diag);
1410 guar.unwrap()
1411 }
1412
1413 #[track_caller]
1415 pub fn emit(self) -> G::EmitResult {
1416 G::emit_producing_guarantee(self)
1417 }
1418
1419 #[track_caller]
1424 pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
1425 if delay {
1426 self.downgrade_to_delayed_bug();
1427 }
1428 self.emit()
1429 }
1430
1431 pub fn cancel(mut self) {
1434 self.diag = None;
1435 drop(self);
1436 }
1437
1438 pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
1440 let diag = self.take_diag();
1441 self.dcx.stash_diagnostic(span, key, diag)
1442 }
1443
1444 #[track_caller]
1455 pub fn delay_as_bug(mut self) -> G::EmitResult {
1456 self.downgrade_to_delayed_bug();
1457 self.emit()
1458 }
1459
1460 pub fn remove_arg(&mut self, name: &str) {
1461 if let Some(diag) = self.diag.as_mut() {
1462 diag.remove_arg(name);
1463 }
1464 }
1465}
1466
1467impl<G: EmissionGuarantee> Drop for Diag<'_, G> {
1470 fn drop(&mut self) {
1471 match self.diag.take() {
1472 Some(diag) if !panicking() => {
1473 self.dcx.emit_diagnostic(DiagInner::new(
1474 Level::Bug,
1475 DiagMessage::from("the following error was constructed but not emitted"),
1476 ));
1477 self.dcx.emit_diagnostic(*diag);
1478 panic!("error was constructed but not emitted");
1479 }
1480 _ => {}
1481 }
1482 }
1483}
1484
1485#[macro_export]
1486macro_rules! struct_span_code_err {
1487 ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
1488 $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
1489 })
1490}