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_lint_defs
::Applicability
;
9 use rustc_serialize
::json
::Json
;
10 use rustc_span
::{MultiSpan, Span, DUMMY_SP}
;
12 use std
::hash
::{Hash, Hasher}
;
14 /// Error type for `Diagnostic`'s `suggestions` field, indicating that
15 /// `.disable_suggestions()` was called on the `Diagnostic`.
16 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
17 pub struct SuggestionsDisabled
;
20 #[derive(Clone, Debug, Encodable, Decodable)]
21 pub struct Diagnostic
{
23 pub message
: Vec
<(String
, Style
)>,
24 pub code
: Option
<DiagnosticId
>,
26 pub children
: Vec
<SubDiagnostic
>,
27 pub suggestions
: Result
<Vec
<CodeSuggestion
>, SuggestionsDisabled
>,
29 /// This is not used for highlighting or rendering any error message. Rather, it can be used
30 /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
31 /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
34 /// If diagnostic is from Lint, custom hash function ignores notes
35 /// otherwise hash is based on the all the fields
39 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
40 pub enum DiagnosticId
{
42 Lint { name: String, has_future_breakage: bool, is_force_warn: bool }
,
45 /// A "sub"-diagnostic attached to a parent diagnostic.
46 /// For example, a note attached to an error.
47 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
48 pub struct SubDiagnostic
{
50 pub message
: Vec
<(String
, Style
)>,
52 pub render_span
: Option
<MultiSpan
>,
55 #[derive(Debug, PartialEq, Eq)]
56 pub struct DiagnosticStyledString(pub Vec
<StringPart
>);
58 impl DiagnosticStyledString
{
59 pub fn new() -> DiagnosticStyledString
{
60 DiagnosticStyledString(vec
![])
62 pub fn push_normal
<S
: Into
<String
>>(&mut self, t
: S
) {
63 self.0.push(StringPart
::Normal(t
.into()));
65 pub fn push_highlighted
<S
: Into
<String
>>(&mut self, t
: S
) {
66 self.0.push(StringPart
::Highlighted(t
.into()));
68 pub fn push
<S
: Into
<String
>>(&mut self, t
: S
, highlight
: bool
) {
70 self.push_highlighted(t
);
75 pub fn normal
<S
: Into
<String
>>(t
: S
) -> DiagnosticStyledString
{
76 DiagnosticStyledString(vec
![StringPart
::Normal(t
.into())])
79 pub fn highlighted
<S
: Into
<String
>>(t
: S
) -> DiagnosticStyledString
{
80 DiagnosticStyledString(vec
![StringPart
::Highlighted(t
.into())])
83 pub fn content(&self) -> String
{
84 self.0.iter
().map(|x
| x
.content()).collect
::<String
>()
88 #[derive(Debug, PartialEq, Eq)]
95 pub fn content(&self) -> &str {
97 &StringPart
::Normal(ref s
) | &StringPart
::Highlighted(ref s
) => s
,
103 pub fn new(level
: Level
, message
: &str) -> Self {
104 Diagnostic
::new_with_code(level
, None
, message
)
107 pub fn new_with_code(level
: Level
, code
: Option
<DiagnosticId
>, message
: &str) -> Self {
110 message
: vec
![(message
.to_owned(), Style
::NoStyle
)],
112 span
: MultiSpan
::new(),
114 suggestions
: Ok(vec
![]),
120 pub fn is_error(&self) -> bool
{
122 Level
::Bug
| Level
::Fatal
| Level
::Error { .. }
| Level
::FailureNote
=> true,
124 Level
::Warning
| Level
::Note
| Level
::Help
| Level
::Cancelled
| Level
::Allow
=> false,
128 pub fn has_future_breakage(&self) -> bool
{
130 Some(DiagnosticId
::Lint { has_future_breakage, .. }
) => has_future_breakage
,
135 pub fn is_force_warn(&self) -> bool
{
137 Some(DiagnosticId
::Lint { is_force_warn, .. }
) => is_force_warn
,
142 /// Cancel the diagnostic (a structured diagnostic must either be emitted or
143 /// canceled or it will panic when dropped).
144 pub fn cancel(&mut self) {
145 self.level
= Level
::Cancelled
;
148 /// Check if this diagnostic [was cancelled][Self::cancel()].
149 pub fn cancelled(&self) -> bool
{
150 self.level
== Level
::Cancelled
153 /// Adds a span/label to be included in the resulting snippet.
155 /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
156 /// was first built. That means it will be shown together with the original
157 /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
159 /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
160 /// the `Span` supplied when creating the diagnostic is primary.
161 pub fn span_label
<T
: Into
<String
>>(&mut self, span
: Span
, label
: T
) -> &mut Self {
162 self.span
.push_span_label(span
, label
.into());
166 pub fn replace_span_with(&mut self, after
: Span
) -> &mut Self {
167 let before
= self.span
.clone();
168 self.set_span(after
);
169 for span_label
in before
.span_labels() {
170 if let Some(label
) = span_label
.label
{
171 self.span_label(after
, label
);
177 crate fn note_expected_found(
179 expected_label
: &dyn fmt
::Display
,
180 expected
: DiagnosticStyledString
,
181 found_label
: &dyn fmt
::Display
,
182 found
: DiagnosticStyledString
,
184 self.note_expected_found_extra(expected_label
, expected
, found_label
, found
, &"", &"")
187 crate fn note_unsuccessful_coercion(
189 expected
: DiagnosticStyledString
,
190 found
: DiagnosticStyledString
,
192 let mut msg
: Vec
<_
> =
193 vec
![("required when trying to coerce from type `".to_string(), Style
::NoStyle
)];
194 msg
.extend(expected
.0.iter
().map(|x
| match *x
{
195 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
196 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
198 msg
.push(("` to type '".to_string(), Style
::NoStyle
));
199 msg
.extend(found
.0.iter
().map(|x
| match *x
{
200 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
201 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
203 msg
.push(("`".to_string(), Style
::NoStyle
));
205 // For now, just attach these as notes
206 self.highlighted_note(msg
);
210 pub fn note_expected_found_extra(
212 expected_label
: &dyn fmt
::Display
,
213 expected
: DiagnosticStyledString
,
214 found_label
: &dyn fmt
::Display
,
215 found
: DiagnosticStyledString
,
216 expected_extra
: &dyn fmt
::Display
,
217 found_extra
: &dyn fmt
::Display
,
219 let expected_label
= expected_label
.to_string();
220 let expected_label
= if expected_label
.is_empty() {
221 "expected".to_string()
223 format
!("expected {}", expected_label
)
225 let found_label
= found_label
.to_string();
226 let found_label
= if found_label
.is_empty() {
229 format
!("found {}", found_label
)
231 let (found_padding
, expected_padding
) = if expected_label
.len() > found_label
.len() {
232 (expected_label
.len() - found_label
.len(), 0)
234 (0, found_label
.len() - expected_label
.len())
236 let mut msg
: Vec
<_
> =
237 vec
![(format
!("{}{} `", " ".repeat(expected_padding
), expected_label
), Style
::NoStyle
)];
238 msg
.extend(expected
.0.iter
().map(|x
| match *x
{
239 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
240 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
242 msg
.push((format
!("`{}\n", expected_extra
), Style
::NoStyle
));
243 msg
.push((format
!("{}{} `", " ".repeat(found_padding
), found_label
), Style
::NoStyle
));
244 msg
.extend(found
.0.iter
().map(|x
| match *x
{
245 StringPart
::Normal(ref s
) => (s
.to_owned(), Style
::NoStyle
),
246 StringPart
::Highlighted(ref s
) => (s
.to_owned(), Style
::Highlight
),
248 msg
.push((format
!("`{}", found_extra
), Style
::NoStyle
));
250 // For now, just attach these as notes.
251 self.highlighted_note(msg
);
255 pub fn note_trait_signature(&mut self, name
: String
, signature
: String
) -> &mut Self {
256 self.highlighted_note(vec
![
257 (format
!("`{}` from trait: `", name
), Style
::NoStyle
),
258 (signature
, Style
::Highlight
),
259 ("`".to_string(), Style
::NoStyle
),
264 /// Add a note attached to this diagnostic.
265 pub fn note(&mut self, msg
: &str) -> &mut Self {
266 self.sub(Level
::Note
, msg
, MultiSpan
::new(), None
);
270 pub fn highlighted_note(&mut self, msg
: Vec
<(String
, Style
)>) -> &mut Self {
271 self.sub_with_highlights(Level
::Note
, msg
, MultiSpan
::new(), None
);
275 /// Prints the span with a note above it.
276 /// This is like [`Diagnostic::note()`], but it gets its own span.
277 crate fn span_note
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
278 self.sub(Level
::Note
, msg
, sp
.into(), None
);
282 /// Add a warning attached to this diagnostic.
283 crate fn warn(&mut self, msg
: &str) -> &mut Self {
284 self.sub(Level
::Warning
, msg
, MultiSpan
::new(), None
);
288 /// Prints the span with a warning above it.
289 /// This is like [`Diagnostic::warn()`], but it gets its own span.
290 crate fn span_warn
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
291 self.sub(Level
::Warning
, msg
, sp
.into(), None
);
295 /// Add a help message attached to this diagnostic.
296 crate fn help(&mut self, msg
: &str) -> &mut Self {
297 self.sub(Level
::Help
, msg
, MultiSpan
::new(), None
);
301 /// Prints the span with some help above it.
302 /// This is like [`Diagnostic::help()`], but it gets its own span.
303 crate fn span_help
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
, msg
: &str) -> &mut Self {
304 self.sub(Level
::Help
, msg
, sp
.into(), None
);
308 /// Disallow attaching suggestions this diagnostic.
309 /// Any suggestions attached e.g. with the `span_suggestion_*` methods
310 /// (before and after the call to `disable_suggestions`) will be ignored.
311 pub fn disable_suggestions(&mut self) -> &mut Self {
312 self.suggestions
= Err(SuggestionsDisabled
);
316 /// Helper for pushing to `self.suggestions`, if available (not disable).
317 fn push_suggestion(&mut self, suggestion
: CodeSuggestion
) {
318 if let Ok(suggestions
) = &mut self.suggestions
{
319 suggestions
.push(suggestion
);
323 /// Show a suggestion that has multiple parts to it.
324 /// In other words, multiple changes need to be applied as part of this suggestion.
325 pub fn multipart_suggestion(
328 suggestion
: Vec
<(Span
, String
)>,
329 applicability
: Applicability
,
331 self.multipart_suggestion_with_style(
335 SuggestionStyle
::ShowCode
,
339 /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
340 /// In other words, multiple changes need to be applied as part of this suggestion.
341 pub fn multipart_suggestion_verbose(
344 suggestion
: Vec
<(Span
, String
)>,
345 applicability
: Applicability
,
347 self.multipart_suggestion_with_style(
351 SuggestionStyle
::ShowAlways
,
354 /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
355 pub fn multipart_suggestion_with_style(
358 suggestion
: Vec
<(Span
, String
)>,
359 applicability
: Applicability
,
360 style
: SuggestionStyle
,
362 assert
!(!suggestion
.is_empty());
363 self.push_suggestion(CodeSuggestion
{
364 substitutions
: vec
![Substitution
{
367 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
373 tool_metadata
: Default
::default(),
378 /// Prints out a message with for a multipart suggestion without showing the suggested code.
380 /// This is intended to be used for suggestions that are obvious in what the changes need to
381 /// be from the message, showing the span label inline would be visually unpleasant
382 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
383 /// improve understandability.
384 pub fn tool_only_multipart_suggestion(
387 suggestion
: Vec
<(Span
, String
)>,
388 applicability
: Applicability
,
390 assert
!(!suggestion
.is_empty());
391 self.push_suggestion(CodeSuggestion
{
392 substitutions
: vec
![Substitution
{
395 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
399 style
: SuggestionStyle
::CompletelyHidden
,
401 tool_metadata
: Default
::default(),
406 /// Prints out a message with a suggested edit of the code.
408 /// In case of short messages and a simple suggestion, rustc displays it as a label:
411 /// try adding parentheses: `(tup.0).1`
416 /// * should not end in any punctuation (a `:` is added automatically)
417 /// * should not be a question (avoid language like "did you mean")
418 /// * should not contain any phrases like "the following", "as shown", etc.
419 /// * may look like "to do xyz, use" or "to do xyz, use abc"
420 /// * may contain a name of a function, variable, or type, but not whole expressions
422 /// See `CodeSuggestion` for more information.
423 pub fn span_suggestion(
428 applicability
: Applicability
,
430 self.span_suggestion_with_style(
435 SuggestionStyle
::ShowCode
,
440 /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
441 pub fn span_suggestion_with_style(
446 applicability
: Applicability
,
447 style
: SuggestionStyle
,
449 self.push_suggestion(CodeSuggestion
{
450 substitutions
: vec
![Substitution
{
451 parts
: vec
![SubstitutionPart { snippet: suggestion, span: sp }
],
456 tool_metadata
: Default
::default(),
461 /// Always show the suggested change.
462 pub fn span_suggestion_verbose(
467 applicability
: Applicability
,
469 self.span_suggestion_with_style(
474 SuggestionStyle
::ShowAlways
,
479 /// Prints out a message with multiple suggested edits of the code.
480 /// See also [`Diagnostic::span_suggestion()`].
481 pub fn span_suggestions(
485 suggestions
: impl Iterator
<Item
= String
>,
486 applicability
: Applicability
,
488 let mut suggestions
: Vec
<_
> = suggestions
.collect();
490 let substitutions
= suggestions
492 .map(|snippet
| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }
] })
494 self.push_suggestion(CodeSuggestion
{
497 style
: SuggestionStyle
::ShowCode
,
499 tool_metadata
: Default
::default(),
504 /// Prints out a message with multiple suggested edits of the code.
505 /// See also [`Diagnostic::span_suggestion()`].
506 pub fn multipart_suggestions(
509 suggestions
: impl Iterator
<Item
= Vec
<(Span
, String
)>>,
510 applicability
: Applicability
,
512 self.push_suggestion(CodeSuggestion
{
513 substitutions
: suggestions
514 .map(|sugg
| Substitution
{
517 .map(|(span
, snippet
)| SubstitutionPart { snippet, span }
)
522 style
: SuggestionStyle
::ShowCode
,
524 tool_metadata
: Default
::default(),
528 /// Prints out a message with a suggested edit of the code. If the suggestion is presented
529 /// inline, it will only show the message and not the suggestion.
531 /// See `CodeSuggestion` for more information.
532 pub fn span_suggestion_short(
537 applicability
: Applicability
,
539 self.span_suggestion_with_style(
544 SuggestionStyle
::HideCodeInline
,
549 /// Prints out a message for a suggestion without showing the suggested code.
551 /// This is intended to be used for suggestions that are obvious in what the changes need to
552 /// be from the message, showing the span label inline would be visually unpleasant
553 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
554 /// improve understandability.
555 pub fn span_suggestion_hidden(
560 applicability
: Applicability
,
562 self.span_suggestion_with_style(
567 SuggestionStyle
::HideCodeAlways
,
572 /// Adds a suggestion to the JSON output that will not be shown in the CLI.
574 /// This is intended to be used for suggestions that are *very* obvious in what the changes
575 /// need to be from the message, but we still want other tools to be able to apply them.
576 pub fn tool_only_span_suggestion(
581 applicability
: Applicability
,
583 self.span_suggestion_with_style(
588 SuggestionStyle
::CompletelyHidden
,
593 /// Adds a suggestion intended only for a tool. The intent is that the metadata encodes
594 /// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
595 pub fn tool_only_suggestion_with_metadata(
598 applicability
: Applicability
,
601 self.push_suggestion(CodeSuggestion
{
602 substitutions
: vec
![],
604 style
: SuggestionStyle
::CompletelyHidden
,
606 tool_metadata
: ToolMetadata
::new(tool_metadata
),
610 pub fn set_span
<S
: Into
<MultiSpan
>>(&mut self, sp
: S
) -> &mut Self {
611 self.span
= sp
.into();
612 if let Some(span
) = self.span
.primary_span() {
613 self.sort_span
= span
;
618 pub fn set_is_lint(&mut self) -> &mut Self {
623 pub fn code(&mut self, s
: DiagnosticId
) -> &mut Self {
628 pub fn clear_code(&mut self) -> &mut Self {
633 pub fn get_code(&self) -> Option
<DiagnosticId
> {
637 crate fn set_primary_message
<M
: Into
<String
>>(&mut self, msg
: M
) -> &mut Self {
638 self.message
[0] = (msg
.into(), Style
::NoStyle
);
642 pub fn message(&self) -> String
{
643 self.message
.iter().map(|i
| i
.0.as_str()).collect
::<String
>()
646 pub fn styled_message(&self) -> &Vec
<(String
, Style
)> {
650 /// Convenience function for internal use, clients should use one of the
651 /// public methods above.
653 /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
659 render_span
: Option
<MultiSpan
>,
661 let sub
= SubDiagnostic
{
663 message
: vec
![(message
.to_owned(), Style
::NoStyle
)],
667 self.children
.push(sub
);
670 /// Convenience function for internal use, clients should use one of the
671 /// public methods above.
672 fn sub_with_highlights(
675 message
: Vec
<(String
, Style
)>,
677 render_span
: Option
<MultiSpan
>,
679 let sub
= SubDiagnostic { level, message, span, render_span }
;
680 self.children
.push(sub
);
683 /// Fields used for Hash, and PartialEq trait
688 &Vec
<(String
, Style
)>,
689 &Option
<DiagnosticId
>,
691 &Result
<Vec
<CodeSuggestion
>, SuggestionsDisabled
>,
692 Option
<&Vec
<SubDiagnostic
>>,
700 (if self.is_lint { None }
else { Some(&self.children) }
),
705 impl Hash
for Diagnostic
{
706 fn hash
<H
>(&self, state
: &mut H
)
710 self.keys().hash(state
);
714 impl PartialEq
for Diagnostic
{
715 fn eq(&self, other
: &Self) -> bool
{
716 self.keys() == other
.keys()
721 pub fn message(&self) -> String
{
722 self.message
.iter().map(|i
| i
.0.as_str()).collect
::<String
>()
725 pub fn styled_message(&self) -> &Vec
<(String
, Style
)> {