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