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 sort_span: Span,
296
297 pub is_lint: Option<IsLint>,
298
299 pub long_ty_path: Option<PathBuf>,
300 pub(crate) emitted_at: DiagLocation,
303}
304
305impl DiagInner {
306 #[track_caller]
307 pub fn new<M: Into<DiagMessage>>(level: Level, message: M) -> Self {
308 DiagInner::new_with_messages(level, vec![(message.into(), Style::NoStyle)])
309 }
310
311 #[track_caller]
312 pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
313 DiagInner {
314 level,
315 lint_id: None,
316 messages,
317 code: None,
318 span: MultiSpan::new(),
319 children: vec![],
320 suggestions: Suggestions::Enabled(vec![]),
321 args: Default::default(),
322 sort_span: DUMMY_SP,
323 is_lint: None,
324 long_ty_path: None,
325 emitted_at: DiagLocation::caller(),
326 }
327 }
328
329 #[inline(always)]
330 pub fn level(&self) -> Level {
331 self.level
332 }
333
334 pub fn is_error(&self) -> bool {
335 match self.level {
336 Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
337
338 Level::ForceWarning
339 | Level::Warning
340 | Level::Note
341 | Level::OnceNote
342 | Level::Help
343 | Level::OnceHelp
344 | Level::FailureNote
345 | Level::Allow
346 | Level::Expect => false,
347 }
348 }
349
350 pub(crate) fn has_future_breakage(&self) -> bool {
352 matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. }))
353 }
354
355 pub(crate) fn is_force_warn(&self) -> bool {
356 match self.level {
357 Level::ForceWarning => {
358 assert!(self.is_lint.is_some());
359 true
360 }
361 _ => false,
362 }
363 }
364
365 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
367 &self,
368 attr: impl Into<SubdiagMessage>,
369 ) -> DiagMessage {
370 let msg =
371 self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
372 msg.with_subdiagnostic_message(attr.into())
373 }
374
375 pub(crate) fn sub(
376 &mut self,
377 level: Level,
378 message: impl Into<SubdiagMessage>,
379 span: MultiSpan,
380 ) {
381 let sub = Subdiag {
382 level,
383 messages: vec![(
384 self.subdiagnostic_message_to_diagnostic_message(message),
385 Style::NoStyle,
386 )],
387 span,
388 };
389 self.children.push(sub);
390 }
391
392 pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) {
393 self.args.insert(name.into(), arg.into_diag_arg(&mut self.long_ty_path));
394 }
395
396 fn keys(
398 &self,
399 ) -> (
400 &Level,
401 &[(DiagMessage, Style)],
402 &Option<ErrCode>,
403 &MultiSpan,
404 &[Subdiag],
405 &Suggestions,
406 Vec<(&DiagArgName, &DiagArgValue)>,
407 &Option<IsLint>,
408 ) {
409 (
410 &self.level,
411 &self.messages,
412 &self.code,
413 &self.span,
414 &self.children,
415 &self.suggestions,
416 self.args.iter().collect(),
417 &self.is_lint,
419 )
421 }
422}
423
424impl Hash for DiagInner {
425 fn hash<H>(&self, state: &mut H)
426 where
427 H: Hasher,
428 {
429 self.keys().hash(state);
430 }
431}
432
433impl PartialEq for DiagInner {
434 fn eq(&self, other: &Self) -> bool {
435 self.keys() == other.keys()
436 }
437}
438
439#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
442pub struct Subdiag {
443 pub level: Level,
444 pub messages: Vec<(DiagMessage, Style)>,
445 pub span: MultiSpan,
446}
447
448#[must_use]
461pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> {
462 pub dcx: DiagCtxtHandle<'a>,
463
464 diag: Option<Box<DiagInner>>,
474
475 _marker: PhantomData<G>,
476}
477
478impl<G> !Clone for Diag<'_, G> {}
481
482rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * size_of::<usize>());
483
484impl<G: EmissionGuarantee> Deref for Diag<'_, G> {
485 type Target = DiagInner;
486
487 fn deref(&self) -> &DiagInner {
488 self.diag.as_ref().unwrap()
489 }
490}
491
492impl<G: EmissionGuarantee> DerefMut for Diag<'_, G> {
493 fn deref_mut(&mut self) -> &mut DiagInner {
494 self.diag.as_mut().unwrap()
495 }
496}
497
498impl<G: EmissionGuarantee> Debug for Diag<'_, G> {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 self.diag.fmt(f)
501 }
502}
503
504macro_rules! with_fn {
523 {
524 $with_f:ident,
525 $(#[$attrs:meta])*
526 pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self {
527 $($body:tt)*
528 }
529 } => {
530 $(#[$attrs])*
532 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
533 pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self {
534 $($body)*
535 }
536
537 $(#[$attrs])*
539 #[doc = concat!("See [`Diag::", stringify!($f), "()`].")]
540 pub fn $with_f(mut $self, $($name: $ty),*) -> Self {
541 $self.$f($($name),*);
542 $self
543 }
544 };
545}
546
547impl<'a, G: EmissionGuarantee> Diag<'a, G> {
548 #[rustc_lint_diagnostics]
549 #[track_caller]
550 pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into<DiagMessage>) -> Self {
551 Self::new_diagnostic(dcx, DiagInner::new(level, message))
552 }
553
554 pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> {
556 Diag { dcx, diag: self.diag.take(), _marker: PhantomData }
557 }
558
559 #[track_caller]
561 pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self {
562 debug!("Created new diagnostic");
563 Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
564 }
565
566 #[rustc_lint_diagnostics]
577 #[track_caller]
578 pub fn downgrade_to_delayed_bug(&mut self) {
579 assert!(
580 matches!(self.level, Level::Error | Level::DelayedBug),
581 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
582 self.level
583 );
584 self.level = Level::DelayedBug;
585 }
586
587 with_fn! { with_span_label,
588 #[rustc_lint_diagnostics]
601 pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagMessage>) -> &mut Self {
602 let msg = self.subdiagnostic_message_to_diagnostic_message(label);
603 self.span.push_span_label(span, msg);
604 self
605 } }
606
607 with_fn! { with_span_labels,
608 #[rustc_lint_diagnostics]
611 pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
612 for span in spans {
613 self.span_label(span, label.to_string());
614 }
615 self
616 } }
617
618 #[rustc_lint_diagnostics]
619 pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
620 let before = self.span.clone();
621 self.span(after);
622 for span_label in before.span_labels() {
623 if let Some(label) = span_label.label {
624 if span_label.is_primary && keep_label {
625 self.span.push_span_label(after, label);
626 } else {
627 self.span.push_span_label(span_label.span, label);
628 }
629 }
630 }
631 self
632 }
633
634 #[rustc_lint_diagnostics]
635 pub fn note_expected_found(
636 &mut self,
637 expected_label: &str,
638 expected: DiagStyledString,
639 found_label: &str,
640 found: DiagStyledString,
641 ) -> &mut Self {
642 self.note_expected_found_extra(
643 expected_label,
644 expected,
645 found_label,
646 found,
647 DiagStyledString::normal(""),
648 DiagStyledString::normal(""),
649 )
650 }
651
652 #[rustc_lint_diagnostics]
653 pub fn note_expected_found_extra(
654 &mut self,
655 expected_label: &str,
656 expected: DiagStyledString,
657 found_label: &str,
658 found: DiagStyledString,
659 expected_extra: DiagStyledString,
660 found_extra: DiagStyledString,
661 ) -> &mut Self {
662 let expected_label = expected_label.to_string();
663 let expected_label = if expected_label.is_empty() {
664 "expected".to_string()
665 } else {
666 format!("expected {expected_label}")
667 };
668 let found_label = found_label.to_string();
669 let found_label = if found_label.is_empty() {
670 "found".to_string()
671 } else {
672 format!("found {found_label}")
673 };
674 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
675 (expected_label.len() - found_label.len(), 0)
676 } else {
677 (0, found_label.len() - expected_label.len())
678 };
679 let mut msg = vec![StringPart::normal(format!(
680 "{}{} `",
681 " ".repeat(expected_padding),
682 expected_label
683 ))];
684 msg.extend(expected.0);
685 msg.push(StringPart::normal(format!("`")));
686 msg.extend(expected_extra.0);
687 msg.push(StringPart::normal(format!("\n")));
688 msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label)));
689 msg.extend(found.0);
690 msg.push(StringPart::normal(format!("`")));
691 msg.extend(found_extra.0);
692
693 self.highlighted_note(msg);
695 self
696 }
697
698 #[rustc_lint_diagnostics]
699 pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
700 self.highlighted_note(vec![
701 StringPart::normal(format!("`{name}` from trait: `")),
702 StringPart::highlighted(signature),
703 StringPart::normal("`"),
704 ]);
705 self
706 }
707
708 with_fn! { with_note,
709 #[rustc_lint_diagnostics]
711 pub fn note(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
712 self.sub(Level::Note, msg, MultiSpan::new());
713 self
714 } }
715
716 #[rustc_lint_diagnostics]
717 pub fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
718 self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
719 self
720 }
721
722 #[rustc_lint_diagnostics]
723 pub fn highlighted_span_note(
724 &mut self,
725 span: impl Into<MultiSpan>,
726 msg: Vec<StringPart>,
727 ) -> &mut Self {
728 self.sub_with_highlights(Level::Note, msg, span.into());
729 self
730 }
731
732 #[rustc_lint_diagnostics]
734 pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
735 self.sub(Level::OnceNote, msg, MultiSpan::new());
736 self
737 }
738
739 with_fn! { with_span_note,
740 #[rustc_lint_diagnostics]
743 pub fn span_note(
744 &mut self,
745 sp: impl Into<MultiSpan>,
746 msg: impl Into<SubdiagMessage>,
747 ) -> &mut Self {
748 self.sub(Level::Note, msg, sp.into());
749 self
750 } }
751
752 #[rustc_lint_diagnostics]
755 pub fn span_note_once<S: Into<MultiSpan>>(
756 &mut self,
757 sp: S,
758 msg: impl Into<SubdiagMessage>,
759 ) -> &mut Self {
760 self.sub(Level::OnceNote, msg, sp.into());
761 self
762 }
763
764 with_fn! { with_warn,
765 #[rustc_lint_diagnostics]
767 pub fn warn(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
768 self.sub(Level::Warning, msg, MultiSpan::new());
769 self
770 } }
771
772 #[rustc_lint_diagnostics]
775 pub fn span_warn<S: Into<MultiSpan>>(
776 &mut self,
777 sp: S,
778 msg: impl Into<SubdiagMessage>,
779 ) -> &mut Self {
780 self.sub(Level::Warning, msg, sp.into());
781 self
782 }
783
784 with_fn! { with_help,
785 #[rustc_lint_diagnostics]
787 pub fn help(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
788 self.sub(Level::Help, msg, MultiSpan::new());
789 self
790 } }
791
792 #[rustc_lint_diagnostics]
794 pub fn help_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
795 self.sub(Level::OnceHelp, msg, MultiSpan::new());
796 self
797 }
798
799 #[rustc_lint_diagnostics]
801 pub fn highlighted_help(&mut self, msg: Vec<StringPart>) -> &mut Self {
802 self.sub_with_highlights(Level::Help, msg, MultiSpan::new());
803 self
804 }
805
806 #[rustc_lint_diagnostics]
808 pub fn highlighted_span_help(
809 &mut self,
810 span: impl Into<MultiSpan>,
811 msg: Vec<StringPart>,
812 ) -> &mut Self {
813 self.sub_with_highlights(Level::Help, msg, span.into());
814 self
815 }
816
817 #[rustc_lint_diagnostics]
820 pub fn span_help<S: Into<MultiSpan>>(
821 &mut self,
822 sp: S,
823 msg: impl Into<SubdiagMessage>,
824 ) -> &mut Self {
825 self.sub(Level::Help, msg, sp.into());
826 self
827 }
828
829 #[rustc_lint_diagnostics]
833 pub fn disable_suggestions(&mut self) -> &mut Self {
834 self.suggestions = Suggestions::Disabled;
835 self
836 }
837
838 #[rustc_lint_diagnostics]
843 pub fn seal_suggestions(&mut self) -> &mut Self {
844 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
845 let suggestions_slice = std::mem::take(suggestions).into_boxed_slice();
846 self.suggestions = Suggestions::Sealed(suggestions_slice);
847 }
848 self
849 }
850
851 #[rustc_lint_diagnostics]
856 fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
857 for subst in &suggestion.substitutions {
858 for part in &subst.parts {
859 let span = part.span;
860 let call_site = span.ctxt().outer_expn_data().call_site;
861 if span.in_derive_expansion() && span.overlaps_or_adjacent(call_site) {
862 return;
864 }
865 }
866 }
867
868 if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
869 suggestions.push(suggestion);
870 }
871 }
872
873 with_fn! { with_multipart_suggestion,
874 #[rustc_lint_diagnostics]
877 pub fn multipart_suggestion(
878 &mut self,
879 msg: impl Into<SubdiagMessage>,
880 suggestion: Vec<(Span, String)>,
881 applicability: Applicability,
882 ) -> &mut Self {
883 self.multipart_suggestion_with_style(
884 msg,
885 suggestion,
886 applicability,
887 SuggestionStyle::ShowCode,
888 )
889 } }
890
891 #[rustc_lint_diagnostics]
894 pub fn multipart_suggestion_verbose(
895 &mut self,
896 msg: impl Into<SubdiagMessage>,
897 suggestion: Vec<(Span, String)>,
898 applicability: Applicability,
899 ) -> &mut Self {
900 self.multipart_suggestion_with_style(
901 msg,
902 suggestion,
903 applicability,
904 SuggestionStyle::ShowAlways,
905 )
906 }
907
908 #[rustc_lint_diagnostics]
910 pub fn multipart_suggestion_with_style(
911 &mut self,
912 msg: impl Into<SubdiagMessage>,
913 mut suggestion: Vec<(Span, String)>,
914 applicability: Applicability,
915 style: SuggestionStyle,
916 ) -> &mut Self {
917 let mut seen = crate::FxHashSet::default();
918 suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone())));
919
920 let parts = suggestion
921 .into_iter()
922 .map(|(span, snippet)| SubstitutionPart { snippet, span })
923 .collect::<Vec<_>>();
924
925 assert!(!parts.is_empty());
926 debug_assert_eq!(
927 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
928 None,
929 "Span must not be empty and have no suggestion",
930 );
931 debug_assert_eq!(
932 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
933 None,
934 "suggestion must not have overlapping parts",
935 );
936
937 self.push_suggestion(CodeSuggestion {
938 substitutions: vec![Substitution { parts }],
939 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
940 style,
941 applicability,
942 });
943 self
944 }
945
946 #[rustc_lint_diagnostics]
953 pub fn tool_only_multipart_suggestion(
954 &mut self,
955 msg: impl Into<SubdiagMessage>,
956 suggestion: Vec<(Span, String)>,
957 applicability: Applicability,
958 ) -> &mut Self {
959 self.multipart_suggestion_with_style(
960 msg,
961 suggestion,
962 applicability,
963 SuggestionStyle::CompletelyHidden,
964 )
965 }
966
967 with_fn! { with_span_suggestion,
968 #[rustc_lint_diagnostics]
986 pub fn span_suggestion(
987 &mut self,
988 sp: Span,
989 msg: impl Into<SubdiagMessage>,
990 suggestion: impl ToString,
991 applicability: Applicability,
992 ) -> &mut Self {
993 self.span_suggestion_with_style(
994 sp,
995 msg,
996 suggestion,
997 applicability,
998 SuggestionStyle::ShowCode,
999 );
1000 self
1001 } }
1002
1003 #[rustc_lint_diagnostics]
1005 pub fn span_suggestion_with_style(
1006 &mut self,
1007 sp: Span,
1008 msg: impl Into<SubdiagMessage>,
1009 suggestion: impl ToString,
1010 applicability: Applicability,
1011 style: SuggestionStyle,
1012 ) -> &mut Self {
1013 debug_assert!(
1014 !(sp.is_empty() && suggestion.to_string().is_empty()),
1015 "Span must not be empty and have no suggestion"
1016 );
1017 self.push_suggestion(CodeSuggestion {
1018 substitutions: vec![Substitution {
1019 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
1020 }],
1021 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1022 style,
1023 applicability,
1024 });
1025 self
1026 }
1027
1028 with_fn! { with_span_suggestion_verbose,
1029 #[rustc_lint_diagnostics]
1031 pub fn span_suggestion_verbose(
1032 &mut self,
1033 sp: Span,
1034 msg: impl Into<SubdiagMessage>,
1035 suggestion: impl ToString,
1036 applicability: Applicability,
1037 ) -> &mut Self {
1038 self.span_suggestion_with_style(
1039 sp,
1040 msg,
1041 suggestion,
1042 applicability,
1043 SuggestionStyle::ShowAlways,
1044 );
1045 self
1046 } }
1047
1048 with_fn! { with_span_suggestions,
1049 #[rustc_lint_diagnostics]
1052 pub fn span_suggestions(
1053 &mut self,
1054 sp: Span,
1055 msg: impl Into<SubdiagMessage>,
1056 suggestions: impl IntoIterator<Item = String>,
1057 applicability: Applicability,
1058 ) -> &mut Self {
1059 self.span_suggestions_with_style(
1060 sp,
1061 msg,
1062 suggestions,
1063 applicability,
1064 SuggestionStyle::ShowCode,
1065 )
1066 } }
1067
1068 #[rustc_lint_diagnostics]
1069 pub fn span_suggestions_with_style(
1070 &mut self,
1071 sp: Span,
1072 msg: impl Into<SubdiagMessage>,
1073 suggestions: impl IntoIterator<Item = String>,
1074 applicability: Applicability,
1075 style: SuggestionStyle,
1076 ) -> &mut Self {
1077 let substitutions = suggestions
1078 .into_iter()
1079 .map(|snippet| {
1080 debug_assert!(
1081 !(sp.is_empty() && snippet.is_empty()),
1082 "Span must not be empty and have no suggestion"
1083 );
1084 Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
1085 })
1086 .collect();
1087 self.push_suggestion(CodeSuggestion {
1088 substitutions,
1089 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1090 style,
1091 applicability,
1092 });
1093 self
1094 }
1095
1096 #[rustc_lint_diagnostics]
1100 pub fn multipart_suggestions(
1101 &mut self,
1102 msg: impl Into<SubdiagMessage>,
1103 suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
1104 applicability: Applicability,
1105 ) -> &mut Self {
1106 let substitutions = suggestions
1107 .into_iter()
1108 .map(|sugg| {
1109 let mut parts = sugg
1110 .into_iter()
1111 .map(|(span, snippet)| SubstitutionPart { snippet, span })
1112 .collect::<Vec<_>>();
1113
1114 parts.sort_unstable_by_key(|part| part.span);
1115
1116 assert!(!parts.is_empty());
1117 debug_assert_eq!(
1118 parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
1119 None,
1120 "Span must not be empty and have no suggestion",
1121 );
1122 debug_assert_eq!(
1123 parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
1124 None,
1125 "suggestion must not have overlapping parts",
1126 );
1127
1128 Substitution { parts }
1129 })
1130 .collect();
1131
1132 self.push_suggestion(CodeSuggestion {
1133 substitutions,
1134 msg: self.subdiagnostic_message_to_diagnostic_message(msg),
1135 style: SuggestionStyle::ShowCode,
1136 applicability,
1137 });
1138 self
1139 }
1140
1141 with_fn! { with_span_suggestion_short,
1142 #[rustc_lint_diagnostics]
1147 pub fn span_suggestion_short(
1148 &mut self,
1149 sp: Span,
1150 msg: impl Into<SubdiagMessage>,
1151 suggestion: impl ToString,
1152 applicability: Applicability,
1153 ) -> &mut Self {
1154 self.span_suggestion_with_style(
1155 sp,
1156 msg,
1157 suggestion,
1158 applicability,
1159 SuggestionStyle::HideCodeInline,
1160 );
1161 self
1162 } }
1163
1164 #[rustc_lint_diagnostics]
1171 pub fn span_suggestion_hidden(
1172 &mut self,
1173 sp: Span,
1174 msg: impl Into<SubdiagMessage>,
1175 suggestion: impl ToString,
1176 applicability: Applicability,
1177 ) -> &mut Self {
1178 self.span_suggestion_with_style(
1179 sp,
1180 msg,
1181 suggestion,
1182 applicability,
1183 SuggestionStyle::HideCodeAlways,
1184 );
1185 self
1186 }
1187
1188 with_fn! { with_tool_only_span_suggestion,
1189 #[rustc_lint_diagnostics]
1194 pub fn tool_only_span_suggestion(
1195 &mut self,
1196 sp: Span,
1197 msg: impl Into<SubdiagMessage>,
1198 suggestion: impl ToString,
1199 applicability: Applicability,
1200 ) -> &mut Self {
1201 self.span_suggestion_with_style(
1202 sp,
1203 msg,
1204 suggestion,
1205 applicability,
1206 SuggestionStyle::CompletelyHidden,
1207 );
1208 self
1209 } }
1210
1211 #[rustc_lint_diagnostics]
1216 pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self {
1217 subdiagnostic.add_to_diag(self);
1218 self
1219 }
1220
1221 pub fn eagerly_translate(&self, msg: impl Into<SubdiagMessage>) -> SubdiagMessage {
1227 let args = self.args.iter();
1228 let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into());
1229 self.dcx.eagerly_translate(msg, args)
1230 }
1231
1232 with_fn! { with_span,
1233 #[rustc_lint_diagnostics]
1235 pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
1236 self.span = sp.into();
1237 if let Some(span) = self.span.primary_span() {
1238 self.sort_span = span;
1239 }
1240 self
1241 } }
1242
1243 #[rustc_lint_diagnostics]
1244 pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
1245 self.is_lint = Some(IsLint { name, has_future_breakage });
1246 self
1247 }
1248
1249 with_fn! { with_code,
1250 #[rustc_lint_diagnostics]
1252 pub fn code(&mut self, code: ErrCode) -> &mut Self {
1253 self.code = Some(code);
1254 self
1255 } }
1256
1257 with_fn! { with_lint_id,
1258 #[rustc_lint_diagnostics]
1260 pub fn lint_id(
1261 &mut self,
1262 id: LintExpectationId,
1263 ) -> &mut Self {
1264 self.lint_id = Some(id);
1265 self
1266 } }
1267
1268 with_fn! { with_primary_message,
1269 #[rustc_lint_diagnostics]
1271 pub fn primary_message(&mut self, msg: impl Into<DiagMessage>) -> &mut Self {
1272 self.messages[0] = (msg.into(), Style::NoStyle);
1273 self
1274 } }
1275
1276 with_fn! { with_arg,
1277 #[rustc_lint_diagnostics]
1279 pub fn arg(
1280 &mut self,
1281 name: impl Into<DiagArgName>,
1282 arg: impl IntoDiagArg,
1283 ) -> &mut Self {
1284 self.deref_mut().arg(name, arg);
1285 self
1286 } }
1287
1288 pub(crate) fn subdiagnostic_message_to_diagnostic_message(
1292 &self,
1293 attr: impl Into<SubdiagMessage>,
1294 ) -> DiagMessage {
1295 self.deref().subdiagnostic_message_to_diagnostic_message(attr)
1296 }
1297
1298 pub fn sub(&mut self, level: Level, message: impl Into<SubdiagMessage>, span: MultiSpan) {
1303 self.deref_mut().sub(level, message, span);
1304 }
1305
1306 fn sub_with_highlights(&mut self, level: Level, messages: Vec<StringPart>, span: MultiSpan) {
1309 let messages = messages
1310 .into_iter()
1311 .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style))
1312 .collect();
1313 let sub = Subdiag { level, messages, span };
1314 self.children.push(sub);
1315 }
1316
1317 fn take_diag(&mut self) -> DiagInner {
1321 if let Some(path) = &self.long_ty_path {
1322 self.note(format!(
1323 "the full name for the type has been written to '{}'",
1324 path.display()
1325 ));
1326 self.note("consider using `--verbose` to print the full type name to the console");
1327 }
1328 Box::into_inner(self.diag.take().unwrap())
1329 }
1330
1331 pub fn long_ty_path(&mut self) -> &mut Option<PathBuf> {
1349 &mut self.long_ty_path
1350 }
1351
1352 fn emit_producing_nothing(mut self) {
1354 let diag = self.take_diag();
1355 self.dcx.emit_diagnostic(diag);
1356 }
1357
1358 fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
1360 let diag = self.take_diag();
1361
1362 assert!(
1371 matches!(diag.level, Level::Error | Level::DelayedBug),
1372 "invalid diagnostic level ({:?})",
1373 diag.level,
1374 );
1375
1376 let guar = self.dcx.emit_diagnostic(diag);
1377 guar.unwrap()
1378 }
1379
1380 #[track_caller]
1382 pub fn emit(self) -> G::EmitResult {
1383 G::emit_producing_guarantee(self)
1384 }
1385
1386 #[track_caller]
1391 pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
1392 if delay {
1393 self.downgrade_to_delayed_bug();
1394 }
1395 self.emit()
1396 }
1397
1398 pub fn cancel(mut self) {
1401 self.diag = None;
1402 drop(self);
1403 }
1404
1405 pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
1407 let diag = self.take_diag();
1408 self.dcx.stash_diagnostic(span, key, diag)
1409 }
1410
1411 #[track_caller]
1422 pub fn delay_as_bug(mut self) -> G::EmitResult {
1423 self.downgrade_to_delayed_bug();
1424 self.emit()
1425 }
1426}
1427
1428impl<G: EmissionGuarantee> Drop for Diag<'_, G> {
1431 fn drop(&mut self) {
1432 match self.diag.take() {
1433 Some(diag) if !panicking() => {
1434 self.dcx.emit_diagnostic(DiagInner::new(
1435 Level::Bug,
1436 DiagMessage::from("the following error was constructed but not emitted"),
1437 ));
1438 self.dcx.emit_diagnostic(*diag);
1439 panic!("error was constructed but not emitted");
1440 }
1441 _ => {}
1442 }
1443 }
1444}
1445
1446#[macro_export]
1447macro_rules! struct_span_code_err {
1448 ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
1449 $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
1450 })
1451}