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