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