1 use crate::snippet
::Style
;
2 use crate::CodeSuggestion
;
4 use crate::Substitution
;
5 use crate::SubstitutionPart
;
6 use crate::SuggestionStyle
;
7 use crate::ToolMetadata
;
8 use rustc_data_structures
::stable_map
::FxHashMap
;
9 use rustc_lint_defs
::{Applicability, LintExpectationId}
;
10 use rustc_serialize
::json
::Json
;
11 use rustc_span
::edition
::LATEST_STABLE_EDITION
;
12 use rustc_span
::{MultiSpan, Span, DUMMY_SP}
;
14 use std
::hash
::{Hash, Hasher}
;
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
;
22 #[derive(Clone, Debug, Encodable, Decodable)]
23 pub struct Diagnostic
{
24 // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
25 // outside of what methods in this crate themselves allow.
28 pub message
: Vec
<(String
, Style
)>,
29 pub code
: Option
<DiagnosticId
>,
31 pub children
: Vec
<SubDiagnostic
>,
32 pub suggestions
: Result
<Vec
<CodeSuggestion
>, SuggestionsDisabled
>,
34 /// This is not used for highlighting or rendering any error message. Rather, it can be used
35 /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
36 /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
39 /// If diagnostic is from Lint, custom hash function ignores notes
40 /// otherwise hash is based on the all the fields
44 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
45 pub enum DiagnosticId
{
47 Lint { name: String, has_future_breakage: bool, is_force_warn: bool }
,
50 /// A "sub"-diagnostic attached to a parent diagnostic.
51 /// For example, a note attached to an error.
52 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
53 pub struct SubDiagnostic
{
55 pub message
: Vec
<(String
, Style
)>,
57 pub render_span
: Option
<MultiSpan
>,
60 #[derive(Debug, PartialEq, Eq)]
61 pub struct DiagnosticStyledString(pub Vec
<StringPart
>);
63 impl DiagnosticStyledString
{
64 pub fn new() -> DiagnosticStyledString
{
65 DiagnosticStyledString(vec
![])
67 pub fn push_normal
<S
: Into
<String
>>(&mut self, t
: S
) {
68 self.0.push(StringPart
::Normal(t
.into()));
70 pub fn push_highlighted
<S
: Into
<String
>>(&mut self, t
: S
) {
71 self.0.push(StringPart
::Highlighted(t
.into()));
73 pub fn push
<S
: Into
<String
>>(&mut self, t
: S
, highlight
: bool
) {
75 self.push_highlighted(t
);
80 pub fn normal
<S
: Into
<String
>>(t
: S
) -> DiagnosticStyledString
{
81 DiagnosticStyledString(vec
![StringPart
::Normal(t
.into())])
84 pub fn highlighted
<S
: Into
<String
>>(t
: S
) -> DiagnosticStyledString
{
85 DiagnosticStyledString(vec
![StringPart
::Highlighted(t
.into())])
88 pub fn content(&self) -> String
{
89 self.0.iter
().map(|x
| x
.content()).collect
::<String
>()
93 #[derive(Debug, PartialEq, Eq)]
100 pub fn content(&self) -> &str {
102 &StringPart
::Normal(ref s
) | &StringPart
::Highlighted(ref s
) => s
,
108 pub fn new(level
: Level
, message
: &str) -> Self {
109 Diagnostic
::new_with_code(level
, None
, message
)
112 pub fn new_with_code(level
: Level
, code
: Option
<DiagnosticId
>, message
: &str) -> Self {
115 message
: vec
![(message
.to_owned(), Style
::NoStyle
)],
117 span
: MultiSpan
::new(),
119 suggestions
: Ok(vec
![]),
126 pub fn level(&self) -> Level
{
130 pub fn is_error(&self) -> bool
{
135 | Level
::Error { .. }
136 | Level
::FailureNote
=> true,
143 | Level
::Expect(_
) => false,
147 pub fn update_unstable_expectation_id(
149 unstable_to_stable
: &FxHashMap
<LintExpectationId
, LintExpectationId
>,
151 if let Level
::Expect(expectation_id
) = &mut self.level
{
152 if expectation_id
.is_stable() {
156 // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index.
157 // The lint index inside the attribute is manually transferred here.
158 let lint_index
= expectation_id
.get_lint_index();
159 expectation_id
.set_lint_index(None
);
160 let mut stable_id
= *unstable_to_stable
161 .get(&expectation_id
)
162 .expect("each unstable `LintExpectationId` must have a matching stable id");
164 stable_id
.set_lint_index(lint_index
);
165 *expectation_id
= stable_id
;
169 pub fn has_future_breakage(&self) -> bool
{
171 Some(DiagnosticId
::Lint { has_future_breakage, .. }
) => has_future_breakage
,
176 pub fn is_force_warn(&self) -> bool
{
178 Some(DiagnosticId
::Lint { is_force_warn, .. }
) => is_force_warn
,
183 /// Delay emission of this diagnostic as a bug.
185 /// This can be useful in contexts where an error indicates a bug but
186 /// typically this only happens when other compilation errors have already
187 /// happened. In those cases this can be used to defer emission of this
188 /// diagnostic as a bug in the compiler only if no other errors have been
191 /// In the meantime, though, callsites are required to deal with the "bug"
192 /// locally in whichever way makes the most sense.
194 pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
197 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
200 self.level
= Level
::DelayedBug
;
205 /// Adds a span/label to be included in the resulting snippet.
207 /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
208 /// was first built. That means it will be shown together with the original
209 /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
211 /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
212 /// the `Span` supplied when creating the diagnostic is primary.
213 pub fn span_label
<T
: Into
<String
>>(&mut self, span
: Span
, label
: T
) -> &mut Self {
214 self.span
.push_span_label(span
, label
.into());
218 /// Labels all the given spans with the provided label.
219 /// See [`Self::span_label()`] for more information.
222 spans
: impl IntoIterator
<Item
= Span
>,
223 label
: impl AsRef
<str>,
225 let label
= label
.as_ref();
227 self.span_label(span
, label
);
232 pub fn replace_span_with(&mut self, after
: Span
) -> &mut Self {
233 let before
= self.span
.clone();
234 self.set_span(after
);
235 for span_label
in before
.span_labels() {
236 if let Some(label
) = span_label
.label
{
237 self.span_label(after
, label
);
243 pub fn note_expected_found(
245 expected_label
: &dyn fmt
::Display
,
246 expected
: DiagnosticStyledString
,
247 found_label
: &dyn fmt
::Display
,
248 found
: DiagnosticStyledString
,
250 self.note_expected_found_extra(expected_label
, expected
, found_label
, found
, &"", &"")
253 pub fn note_unsuccessful_coercion(
255 expected
: DiagnosticStyledString
,
256 found
: DiagnosticStyledString
,
258 let mut msg
: Vec
<_
> =
259 vec
![("required when trying to coerce from type `".to_string(), Style
::NoStyle
)];
260 msg
.extend(expected
.0.iter
().map(|x
| match *x
{
261 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
262 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
264 msg
.push(("` to type '".to_string(), Style
::NoStyle
));
265 msg
.extend(found
.0.iter
().map(|x
| match *x
{
266 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
267 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
269 msg
.push(("`".to_string(), Style
::NoStyle
));
271 // For now, just attach these as notes
272 self.highlighted_note(msg
);
276 pub fn note_expected_found_extra(
278 expected_label
: &dyn fmt
::Display
,
279 expected
: DiagnosticStyledString
,
280 found_label
: &dyn fmt
::Display
,
281 found
: DiagnosticStyledString
,
282 expected_extra
: &dyn fmt
::Display
,
283 found_extra
: &dyn fmt
::Display
,
285 let expected_label
= expected_label
.to_string();
286 let expected_label
= if expected_label
.is_empty() {
287 "expected".to_string()
289 format
!("expected {}", expected_label
)
291 let found_label
= found_label
.to_string();
292 let found_label
= if found_label
.is_empty() {
295 format
!("found {}", found_label
)
297 let (found_padding
, expected_padding
) = if expected_label
.len() > found_label
.len() {
298 (expected_label
.len() - found_label
.len(), 0)
300 (0, found_label
.len() - expected_label
.len())
302 let mut msg
: Vec
<_
> =
303 vec
![(format
!("{}{} `", " ".repeat(expected_padding
), expected_label
), Style
::NoStyle
)];
304 msg
.extend(expected
.0.iter
().map(|x
| match *x
{
305 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
306 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
308 msg
.push((format
!("`{}\n", expected_extra
), Style
::NoStyle
));
309 msg
.push((format
!("{}{} `", " ".repeat(found_padding
), found_label
), Style
::NoStyle
));
310 msg
.extend(found
.0.iter
().map(|x
| match *x
{
311 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
312 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
314 msg
.push((format
!("`{}", found_extra
), Style
::NoStyle
));
316 // For now, just attach these as notes.
317 self.highlighted_note(msg
);
321 pub fn note_trait_signature(&mut self, name
: String
, signature
: String
) -> &mut Self {
322 self.highlighted_note(vec
![
323 (format
!("`{}` from trait: `", name
), Style
::NoStyle
),
324 (signature
, Style
::Highlight
),
325 ("`".to_string(), Style
::NoStyle
),
330 /// Add a note attached to this diagnostic.
331 pub fn note(&mut self, msg
: &str) -> &mut Self {
332 self.sub(Level
::Note
, msg
, MultiSpan
::new(), None
);
336 pub fn highlighted_note(&mut self, msg
: Vec
<(String
, Style
)>) -> &mut Self {
337 self.sub_with_highlights(Level
::Note
, msg
, MultiSpan
::new(), None
);
341 /// Prints the span with a note above it.
342 /// This is like [`Diagnostic::note()`], but it gets its own span.
343 pub fn note_once(&mut self, msg
: &str) -> &mut Self {
344 self.sub(Level
::OnceNote
, msg
, MultiSpan
::new(), None
);
348 /// Prints the span with a note above it.
349 /// This is like [`Diagnostic::note()`], but it gets its own span.
350 pub fn span_note
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
351 self.sub(Level
::Note
, msg
, sp
.into(), None
);
355 /// Prints the span with a note above it.
356 /// This is like [`Diagnostic::note()`], but it gets its own span.
357 pub fn span_note_once
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
358 self.sub(Level
::OnceNote
, msg
, sp
.into(), None
);
362 /// Add a warning attached to this diagnostic.
363 pub fn warn(&mut self, msg
: &str) -> &mut Self {
364 self.sub(Level
::Warning
, msg
, MultiSpan
::new(), None
);
368 /// Prints the span with a warning above it.
369 /// This is like [`Diagnostic::warn()`], but it gets its own span.
370 pub fn span_warn
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
371 self.sub(Level
::Warning
, msg
, sp
.into(), None
);
375 /// Add a help message attached to this diagnostic.
376 pub fn help(&mut self, msg
: &str) -> &mut Self {
377 self.sub(Level
::Help
, msg
, MultiSpan
::new(), None
);
381 /// Prints the span with some help above it.
382 /// This is like [`Diagnostic::help()`], but it gets its own span.
383 pub fn span_help
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
384 self.sub(Level
::Help
, msg
, sp
.into(), None
);
388 /// Help the user upgrade to the latest edition.
389 /// This is factored out to make sure it does the right thing with `Cargo.toml`.
390 pub fn help_use_latest_edition(&mut self) -> &mut Self {
391 if std
::env
::var_os("CARGO").is_some() {
392 self.help(&format
!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION
));
394 self.help(&format
!("pass `--edition {}` to `rustc`", LATEST_STABLE_EDITION
));
396 self.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
400 /// Disallow attaching suggestions this diagnostic.
401 /// Any suggestions attached e.g. with the `span_suggestion_*` methods
402 /// (before and after the call to `disable_suggestions`) will be ignored.
403 pub fn disable_suggestions(&mut self) -> &mut Self {
404 self.suggestions
= Err(SuggestionsDisabled
);
408 /// Helper for pushing to `self.suggestions`, if available (not disable).
409 fn push_suggestion(&mut self, suggestion
: CodeSuggestion
) {
410 if let Ok(suggestions
) = &mut self.suggestions
{
411 suggestions
.push(suggestion
);
415 /// Show a suggestion that has multiple parts to it.
416 /// In other words, multiple changes need to be applied as part of this suggestion.
417 pub fn multipart_suggestion(
420 suggestion
: Vec
<(Span
, String
)>,
421 applicability
: Applicability
,
423 self.multipart_suggestion_with_style(
427 SuggestionStyle
::ShowCode
,
431 /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
432 /// In other words, multiple changes need to be applied as part of this suggestion.
433 pub fn multipart_suggestion_verbose(
436 suggestion
: Vec
<(Span
, String
)>,
437 applicability
: Applicability
,
439 self.multipart_suggestion_with_style(
443 SuggestionStyle
::ShowAlways
,
446 /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
447 pub fn multipart_suggestion_with_style(
450 suggestion
: Vec
<(Span
, String
)>,
451 applicability
: Applicability
,
452 style
: SuggestionStyle
,
454 assert
!(!suggestion
.is_empty());
455 self.push_suggestion(CodeSuggestion
{
456 substitutions
: vec
![Substitution
{
459 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
465 tool_metadata
: Default
::default(),
470 /// Prints out a message with for a multipart suggestion without showing the suggested code.
472 /// This is intended to be used for suggestions that are obvious in what the changes need to
473 /// be from the message, showing the span label inline would be visually unpleasant
474 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
475 /// improve understandability.
476 pub fn tool_only_multipart_suggestion(
479 suggestion
: Vec
<(Span
, String
)>,
480 applicability
: Applicability
,
482 assert
!(!suggestion
.is_empty());
483 self.push_suggestion(CodeSuggestion
{
484 substitutions
: vec
![Substitution
{
487 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
491 style
: SuggestionStyle
::CompletelyHidden
,
493 tool_metadata
: Default
::default(),
498 /// Prints out a message with a suggested edit of the code.
500 /// In case of short messages and a simple suggestion, rustc displays it as a label:
503 /// try adding parentheses: `(tup.0).1`
508 /// * should not end in any punctuation (a `:` is added automatically)
509 /// * should not be a question (avoid language like "did you mean")
510 /// * should not contain any phrases like "the following", "as shown", etc.
511 /// * may look like "to do xyz, use" or "to do xyz, use abc"
512 /// * may contain a name of a function, variable, or type, but not whole expressions
514 /// See `CodeSuggestion` for more information.
515 pub fn span_suggestion(
520 applicability
: Applicability
,
522 self.span_suggestion_with_style(
527 SuggestionStyle
::ShowCode
,
532 /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
533 pub fn span_suggestion_with_style(
538 applicability
: Applicability
,
539 style
: SuggestionStyle
,
541 self.push_suggestion(CodeSuggestion
{
542 substitutions
: vec
![Substitution
{
543 parts
: vec
![SubstitutionPart { snippet: suggestion, span: sp }
],
548 tool_metadata
: Default
::default(),
553 /// Always show the suggested change.
554 pub fn span_suggestion_verbose(
559 applicability
: Applicability
,
561 self.span_suggestion_with_style(
566 SuggestionStyle
::ShowAlways
,
571 /// Prints out a message with multiple suggested edits of the code.
572 /// See also [`Diagnostic::span_suggestion()`].
573 pub fn span_suggestions(
577 suggestions
: impl Iterator
<Item
= String
>,
578 applicability
: Applicability
,
580 let mut suggestions
: Vec
<_
> = suggestions
.collect();
582 let substitutions
= suggestions
584 .map(|snippet
| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }
] })
586 self.push_suggestion(CodeSuggestion
{
589 style
: SuggestionStyle
::ShowCode
,
591 tool_metadata
: Default
::default(),
596 /// Prints out a message with multiple suggested edits of the code.
597 /// See also [`Diagnostic::span_suggestion()`].
598 pub fn multipart_suggestions(
601 suggestions
: impl Iterator
<Item
= Vec
<(Span
, String
)>>,
602 applicability
: Applicability
,
604 self.push_suggestion(CodeSuggestion
{
605 substitutions
: suggestions
606 .map(|sugg
| Substitution
{
609 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
614 style
: SuggestionStyle
::ShowCode
,
616 tool_metadata
: Default
::default(),
620 /// Prints out a message with a suggested edit of the code. If the suggestion is presented
621 /// inline, it will only show the message and not the suggestion.
623 /// See `CodeSuggestion` for more information.
624 pub fn span_suggestion_short(
629 applicability
: Applicability
,
631 self.span_suggestion_with_style(
636 SuggestionStyle
::HideCodeInline
,
641 /// Prints out a message for a suggestion without showing the suggested code.
643 /// This is intended to be used for suggestions that are obvious in what the changes need to
644 /// be from the message, showing the span label inline would be visually unpleasant
645 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
646 /// improve understandability.
647 pub fn span_suggestion_hidden(
652 applicability
: Applicability
,
654 self.span_suggestion_with_style(
659 SuggestionStyle
::HideCodeAlways
,
664 /// Adds a suggestion to the JSON output that will not be shown in the CLI.
666 /// This is intended to be used for suggestions that are *very* obvious in what the changes
667 /// need to be from the message, but we still want other tools to be able to apply them.
668 pub fn tool_only_span_suggestion(
673 applicability
: Applicability
,
675 self.span_suggestion_with_style(
680 SuggestionStyle
::CompletelyHidden
,
685 /// Adds a suggestion intended only for a tool. The intent is that the metadata encodes
686 /// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
687 pub fn tool_only_suggestion_with_metadata(
690 applicability
: Applicability
,
693 self.push_suggestion(CodeSuggestion
{
694 substitutions
: vec
![],
696 style
: SuggestionStyle
::CompletelyHidden
,
698 tool_metadata
: ToolMetadata
::new(tool_metadata
),
702 pub fn set_span
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
) -> &mut Self {
703 self.span
= sp
.into();
704 if let Some(span
) = self.span
.primary_span() {
705 self.sort_span
= span
;
710 pub fn set_is_lint(&mut self) -> &mut Self {
715 pub fn code(&mut self, s
: DiagnosticId
) -> &mut Self {
720 pub fn clear_code(&mut self) -> &mut Self {
725 pub fn get_code(&self) -> Option
<DiagnosticId
> {
729 pub fn set_primary_message
<M
: Into
<String
>>(&mut self, msg
: M
) -> &mut Self {
730 self.message
[0] = (msg
.into(), Style
::NoStyle
);
734 pub fn message(&self) -> String
{
735 self.message
.iter().map(|i
| i
.0.as_str()).collect
::<String
>()
738 pub fn styled_message(&self) -> &Vec
<(String
, Style
)> {
742 /// Convenience function for internal use, clients should use one of the
743 /// public methods above.
745 /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
751 render_span
: Option
<MultiSpan
>,
753 let sub
= SubDiagnostic
{
755 message
: vec
![(message
.to_owned(), Style
::NoStyle
)],
759 self.children
.push(sub
);
762 /// Convenience function for internal use, clients should use one of the
763 /// public methods above.
764 fn sub_with_highlights(
767 message
: Vec
<(String
, Style
)>,
769 render_span
: Option
<MultiSpan
>,
771 let sub
= SubDiagnostic { level, message, span, render_span }
;
772 self.children
.push(sub
);
775 /// Fields used for Hash, and PartialEq trait
780 &Vec
<(String
, Style
)>,
781 &Option
<DiagnosticId
>,
783 &Result
<Vec
<CodeSuggestion
>, SuggestionsDisabled
>,
784 Option
<&Vec
<SubDiagnostic
>>,
792 (if self.is_lint { None }
else { Some(&self.children) }
),
797 impl Hash
for Diagnostic
{
798 fn hash
<H
>(&self, state
: &mut H
)
802 self.keys().hash(state
);
806 impl PartialEq
for Diagnostic
{
807 fn eq(&self, other
: &Self) -> bool
{
808 self.keys() == other
.keys()
813 pub fn message(&self) -> String
{
814 self.message
.iter().map(|i
| i
.0.as_str()).collect
::<String
>()
817 pub fn styled_message(&self) -> &Vec
<(String
, Style
)> {