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 with_fn! { with_span_help,
851 #[rustc_lint_diagnostics]
854 pub fn span_help(
855 &mut self,
856 sp: impl Into<MultiSpan>,
857 msg: impl Into<SubdiagMessage>,
858 ) -> &mut Self {
859 self.sub(Level::Help, msg, sp.into());
860 self
861 } }
862
863 #[rustc_lint_diagnostics]
867 pub fn disable_suggestions(&mut self) -> &mut Self {
868 self.suggestions = Suggestions::Disabled;
869 self
870 }
871
872 #[rustc_lint_diagnostics]
877 pub fn seal_suggestions(&mut self) -> &mut Self {
878 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
879 let suggestions_slice = std::mem::take(suggestions).into_boxed_slice();
880 self.suggestions = Suggestions::Sealed(suggestions_slice);
881 }
882 self
883 }
884
885 #[rustc_lint_diagnostics]
890 fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
891 for subst in &suggestion.substitutions {
892 for part in &subst.parts {
893 let span = part.span;
894 let call_site = span.ctxt().outer_expn_data().call_site;
895 if span.in_derive_expansion() && span.overlaps_or_adjacent(call_site) {
896 return;
898 }
899 }
900 }
901
902 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
903 suggestions.push(suggestion);
904 }
905 }
906
907 with_fn! { with_multipart_suggestion,
908 #[rustc_lint_diagnostics]
911 pub fn multipart_suggestion(
912 &mut self,
913 msg: impl Into<SubdiagMessage>,
914 suggestion: Vec<(Span, String)>,
915 applicability: Applicability,
916 ) -> &mut Self {
917 self.multipart_suggestion_with_style(
918 msg,
919 suggestion,
920 applicability,
921 SuggestionStyle::ShowCode,
922 )
923 } }
924
925 #[rustc_lint_diagnostics]
928 pub fn multipart_suggestion_verbose(
929 &mut self,
930 msg: impl Into<SubdiagMessage>,
931 suggestion: Vec<(Span, String)>,
932 applicability: Applicability,
933 ) -> &mut Self {
934 self.multipart_suggestion_with_style(
935 msg,
936 suggestion,
937 applicability,
938 SuggestionStyle::ShowAlways,
939 )
940 }
941
942 #[rustc_lint_diagnostics]
944 pub fn multipart_suggestion_with_style(
945 &mut self,
946 msg: impl Into<SubdiagMessage>,
947 mut suggestion: Vec<(Span, String)>,
948 applicability: Applicability,
949 style: SuggestionStyle,
950 ) -> &mut Self {
951 let mut seen = crate::FxHashSet::default();
952 suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone())));
953
954 let parts = suggestion
955 .into_iter()
956 .map(|(span, snippet)| SubstitutionPart { snippet, span })
957 .collect::<Vec<_>>();
958
959 assert!(!parts.is_empty());
960 debug_assert_eq!(
961 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
962 None,
963 "Span must not be empty and have no suggestion",
964 );
965 debug_assert_eq!(
966 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
967 None,
968 "suggestion must not have overlapping parts",
969 );
970
971 self.push_suggestion(CodeSuggestion {
972 substitutions: vec![Substitution { parts }],
973 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
974 style,
975 applicability,
976 });
977 self
978 }
979
980 #[rustc_lint_diagnostics]
987 pub fn tool_only_multipart_suggestion(
988 &mut self,
989 msg: impl Into<SubdiagMessage>,
990 suggestion: Vec<(Span, String)>,
991 applicability: Applicability,
992 ) -> &mut Self {
993 self.multipart_suggestion_with_style(
994 msg,
995 suggestion,
996 applicability,
997 SuggestionStyle::CompletelyHidden,
998 )
999 }
1000
1001 with_fn! { with_span_suggestion,
1002 #[rustc_lint_diagnostics]
1020 pub fn span_suggestion(
1021 &mut self,
1022 sp: Span,
1023 msg: impl Into<SubdiagMessage>,
1024 suggestion: impl ToString,
1025 applicability: Applicability,
1026 ) -> &mut Self {
1027 self.span_suggestion_with_style(
1028 sp,
1029 msg,
1030 suggestion,
1031 applicability,
1032 SuggestionStyle::ShowCode,
1033 );
1034 self
1035 } }
1036
1037 #[rustc_lint_diagnostics]
1039 pub fn span_suggestion_with_style(
1040 &mut self,
1041 sp: Span,
1042 msg: impl Into<SubdiagMessage>,
1043 suggestion: impl ToString,
1044 applicability: Applicability,
1045 style: SuggestionStyle,
1046 ) -> &mut Self {
1047 debug_assert!(
1048 !(sp.is_empty() && suggestion.to_string().is_empty()),
1049 "Span must not be empty and have no suggestion"
1050 );
1051 self.push_suggestion(CodeSuggestion {
1052 substitutions: vec![Substitution {
1053 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
1054 }],
1055 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1056 style,
1057 applicability,
1058 });
1059 self
1060 }
1061
1062 with_fn! { with_span_suggestion_verbose,
1063 #[rustc_lint_diagnostics]
1065 pub fn span_suggestion_verbose(
1066 &mut self,
1067 sp: Span,
1068 msg: impl Into<SubdiagMessage>,
1069 suggestion: impl ToString,
1070 applicability: Applicability,
1071 ) -> &mut Self {
1072 self.span_suggestion_with_style(
1073 sp,
1074 msg,
1075 suggestion,
1076 applicability,
1077 SuggestionStyle::ShowAlways,
1078 );
1079 self
1080 } }
1081
1082 with_fn! { with_span_suggestions,
1083 #[rustc_lint_diagnostics]
1086 pub fn span_suggestions(
1087 &mut self,
1088 sp: Span,
1089 msg: impl Into<SubdiagMessage>,
1090 suggestions: impl IntoIterator<Item = String>,
1091 applicability: Applicability,
1092 ) -> &mut Self {
1093 self.span_suggestions_with_style(
1094 sp,
1095 msg,
1096 suggestions,
1097 applicability,
1098 SuggestionStyle::ShowCode,
1099 )
1100 } }
1101
1102 #[rustc_lint_diagnostics]
1103 pub fn span_suggestions_with_style(
1104 &mut self,
1105 sp: Span,
1106 msg: impl Into<SubdiagMessage>,
1107 suggestions: impl IntoIterator<Item = String>,
1108 applicability: Applicability,
1109 style: SuggestionStyle,
1110 ) -> &mut Self {
1111 let substitutions = suggestions
1112 .into_iter()
1113 .map(|snippet| {
1114 debug_assert!(
1115 !(sp.is_empty() && snippet.is_empty()),
1116 "Span must not be empty and have no suggestion"
1117 );
1118 Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
1119 })
1120 .collect();
1121 self.push_suggestion(CodeSuggestion {
1122 substitutions,
1123 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1124 style,
1125 applicability,
1126 });
1127 self
1128 }
1129
1130 #[rustc_lint_diagnostics]
1134 pub fn multipart_suggestions(
1135 &mut self,
1136 msg: impl Into<SubdiagMessage>,
1137 suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
1138 applicability: Applicability,
1139 ) -> &mut Self {
1140 let substitutions = suggestions
1141 .into_iter()
1142 .map(|sugg| {
1143 let mut parts = sugg
1144 .into_iter()
1145 .map(|(span, snippet)| SubstitutionPart { snippet, span })
1146 .collect::<Vec<_>>();
1147
1148 parts.sort_unstable_by_key(|part| part.span);
1149
1150 assert!(!parts.is_empty());
1151 debug_assert_eq!(
1152 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
1153 None,
1154 "Span must not be empty and have no suggestion",
1155 );
1156 debug_assert_eq!(
1157 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
1158 None,
1159 "suggestion must not have overlapping parts",
1160 );
1161
1162 Substitution { parts }
1163 })
1164 .collect();
1165
1166 self.push_suggestion(CodeSuggestion {
1167 substitutions,
1168 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1169 style: SuggestionStyle::ShowAlways,
1170 applicability,
1171 });
1172 self
1173 }
1174
1175 with_fn! { with_span_suggestion_short,
1176 #[rustc_lint_diagnostics]
1181 pub fn span_suggestion_short(
1182 &mut self,
1183 sp: Span,
1184 msg: impl Into<SubdiagMessage>,
1185 suggestion: impl ToString,
1186 applicability: Applicability,
1187 ) -> &mut Self {
1188 self.span_suggestion_with_style(
1189 sp,
1190 msg,
1191 suggestion,
1192 applicability,
1193 SuggestionStyle::HideCodeInline,
1194 );
1195 self
1196 } }
1197
1198 #[rustc_lint_diagnostics]
1205 pub fn span_suggestion_hidden(
1206 &mut self,
1207 sp: Span,
1208 msg: impl Into<SubdiagMessage>,
1209 suggestion: impl ToString,
1210 applicability: Applicability,
1211 ) -> &mut Self {
1212 self.span_suggestion_with_style(
1213 sp,
1214 msg,
1215 suggestion,
1216 applicability,
1217 SuggestionStyle::HideCodeAlways,
1218 );
1219 self
1220 }
1221
1222 with_fn! { with_tool_only_span_suggestion,
1223 #[rustc_lint_diagnostics]
1228 pub fn tool_only_span_suggestion(
1229 &mut self,
1230 sp: Span,
1231 msg: impl Into<SubdiagMessage>,
1232 suggestion: impl ToString,
1233 applicability: Applicability,
1234 ) -> &mut Self {
1235 self.span_suggestion_with_style(
1236 sp,
1237 msg,
1238 suggestion,
1239 applicability,
1240 SuggestionStyle::CompletelyHidden,
1241 );
1242 self
1243 } }
1244
1245 #[rustc_lint_diagnostics]
1250 pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self {
1251 subdiagnostic.add_to_diag(self);
1252 self
1253 }
1254
1255 pub fn eagerly_translate(&self, msg: impl Into<SubdiagMessage>) -> SubdiagMessage {
1261 let args = self.args.iter();
1262 let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into());
1263 self.dcx.eagerly_translate(msg, args)
1264 }
1265
1266 with_fn! { with_span,
1267 #[rustc_lint_diagnostics]
1269 pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
1270 self.span = sp.into();
1271 if let Some(span) = self.span.primary_span() {
1272 self.sort_span = span;
1273 }
1274 self
1275 } }
1276
1277 #[rustc_lint_diagnostics]
1278 pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
1279 self.is_lint = Some(IsLint { name, has_future_breakage });
1280 self
1281 }
1282
1283 with_fn! { with_code,
1284 #[rustc_lint_diagnostics]
1286 pub fn code(&mut self, code: ErrCode) -> &mut Self {
1287 self.code = Some(code);
1288 self
1289 } }
1290
1291 with_fn! { with_lint_id,
1292 #[rustc_lint_diagnostics]
1294 pub fn lint_id(
1295 &mut self,
1296 id: LintExpectationId,
1297 ) -> &mut Self {
1298 self.lint_id = Some(id);
1299 self
1300 } }
1301
1302 with_fn! { with_primary_message,
1303 #[rustc_lint_diagnostics]
1305 pub fn primary_message(&mut self, msg: impl Into<DiagMessage>) -> &mut Self {
1306 self.messages[0] = (msg.into(), Style::NoStyle);
1307 self
1308 } }
1309
1310 with_fn! { with_arg,
1311 #[rustc_lint_diagnostics]
1313 pub fn arg(
1314 &mut self,
1315 name: impl Into<DiagArgName>,
1316 arg: impl IntoDiagArg,
1317 ) -> &mut Self {
1318 self.deref_mut().arg(name, arg);
1319 self
1320 } }
1321
1322 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
1326 &self,
1327 attr: impl Into<SubdiagMessage>,
1328 ) -> DiagMessage {
1329 self.deref().subdiagnostic_message_to_diagnostic_message(attr)
1330 }
1331
1332 pub fn sub(&mut self, level: Level, message: impl Into<SubdiagMessage>, span: MultiSpan) {
1337 self.deref_mut().sub(level, message, span);
1338 }
1339
1340 fn sub_with_highlights(&mut self, level: Level, messages: Vec<StringPart>, span: MultiSpan) {
1343 let messages = messages
1344 .into_iter()
1345 .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style))
1346 .collect();
1347 let sub = Subdiag { level, messages, span };
1348 self.children.push(sub);
1349 }
1350
1351 fn take_diag(&mut self) -> DiagInner {
1355 if let Some(path) = &self.long_ty_path {
1356 self.note(format!(
1357 "the full name for the type has been written to '{}'",
1358 path.display()
1359 ));
1360 self.note("consider using `--verbose` to print the full type name to the console");
1361 }
1362 *self.diag.take().unwrap()
1363 }
1364
1365 pub fn long_ty_path(&mut self) -> &mut Option<PathBuf> {
1383 &mut self.long_ty_path
1384 }
1385
1386 pub fn with_long_ty_path(mut self, long_ty_path: Option<PathBuf>) -> Self {
1387 self.long_ty_path = long_ty_path;
1388 self
1389 }
1390
1391 fn emit_producing_nothing(mut self) {
1393 let diag = self.take_diag();
1394 self.dcx.emit_diagnostic(diag);
1395 }
1396
1397 fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
1399 let diag = self.take_diag();
1400
1401 assert!(
1410 matches!(diag.level, Level::Error | Level::DelayedBug),
1411 "invalid diagnostic level ({:?})",
1412 diag.level,
1413 );
1414
1415 let guar = self.dcx.emit_diagnostic(diag);
1416 guar.unwrap()
1417 }
1418
1419 #[track_caller]
1421 pub fn emit(self) -> G::EmitResult {
1422 G::emit_producing_guarantee(self)
1423 }
1424
1425 #[track_caller]
1430 pub fn emit_unless_delay(mut self, delay: bool) -> G::EmitResult {
1431 if delay {
1432 self.downgrade_to_delayed_bug();
1433 }
1434 self.emit()
1435 }
1436
1437 pub fn cancel(mut self) {
1440 self.diag = None;
1441 drop(self);
1442 }
1443
1444 pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
1446 let diag = self.take_diag();
1447 self.dcx.stash_diagnostic(span, key, diag)
1448 }
1449
1450 #[track_caller]
1461 pub fn delay_as_bug(mut self) -> G::EmitResult {
1462 self.downgrade_to_delayed_bug();
1463 self.emit()
1464 }
1465
1466 pub fn remove_arg(&mut self, name: &str) {
1467 if let Some(diag) = self.diag.as_mut() {
1468 diag.remove_arg(name);
1469 }
1470 }
1471}
1472
1473impl<G: EmissionGuarantee> Drop for Diag<'_, G> {
1476 fn drop(&mut self) {
1477 match self.diag.take() {
1478 Some(diag) if !panicking() => {
1479 self.dcx.emit_diagnostic(DiagInner::new(
1480 Level::Bug,
1481 DiagMessage::from("the following error was constructed but not emitted"),
1482 ));
1483 self.dcx.emit_diagnostic(*diag);
1484 panic!("error was constructed but not emitted");
1485 }
1486 _ => {}
1487 }
1488 }
1489}
1490
1491#[macro_export]
1492macro_rules! struct_span_code_err {
1493 ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
1494 $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
1495 })
1496}