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