]> git.proxmox.com Git - rustc.git/blob - src/librustc_errors/diagnostic.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / librustc_errors / diagnostic.rs
1 use crate::snippet::Style;
2 use crate::Applicability;
3 use crate::CodeSuggestion;
4 use crate::Level;
5 use crate::Substitution;
6 use crate::SubstitutionPart;
7 use crate::SuggestionStyle;
8 use rustc_span::{MultiSpan, Span, DUMMY_SP};
9 use std::fmt;
10
11 #[must_use]
12 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
13 pub struct Diagnostic {
14 pub level: Level,
15 pub message: Vec<(String, Style)>,
16 pub code: Option<DiagnosticId>,
17 pub span: MultiSpan,
18 pub children: Vec<SubDiagnostic>,
19 pub suggestions: Vec<CodeSuggestion>,
20
21 /// This is not used for highlighting or rendering any error message. Rather, it can be used
22 /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
23 /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
24 pub sort_span: Span,
25 }
26
27 #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
28 pub enum DiagnosticId {
29 Error(String),
30 Lint(String),
31 }
32
33 /// For example a note attached to an error.
34 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
35 pub struct SubDiagnostic {
36 pub level: Level,
37 pub message: Vec<(String, Style)>,
38 pub span: MultiSpan,
39 pub render_span: Option<MultiSpan>,
40 }
41
42 #[derive(Debug, PartialEq, Eq)]
43 pub struct DiagnosticStyledString(pub Vec<StringPart>);
44
45 impl DiagnosticStyledString {
46 pub fn new() -> DiagnosticStyledString {
47 DiagnosticStyledString(vec![])
48 }
49 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
50 self.0.push(StringPart::Normal(t.into()));
51 }
52 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
53 self.0.push(StringPart::Highlighted(t.into()));
54 }
55 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
56 if highlight {
57 self.push_highlighted(t);
58 } else {
59 self.push_normal(t);
60 }
61 }
62 pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
63 DiagnosticStyledString(vec![StringPart::Normal(t.into())])
64 }
65
66 pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
67 DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
68 }
69
70 pub fn content(&self) -> String {
71 self.0.iter().map(|x| x.content()).collect::<String>()
72 }
73 }
74
75 #[derive(Debug, PartialEq, Eq)]
76 pub enum StringPart {
77 Normal(String),
78 Highlighted(String),
79 }
80
81 impl StringPart {
82 pub fn content(&self) -> &str {
83 match self {
84 &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
85 }
86 }
87 }
88
89 impl Diagnostic {
90 pub fn new(level: Level, message: &str) -> Self {
91 Diagnostic::new_with_code(level, None, message)
92 }
93
94 pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
95 Diagnostic {
96 level,
97 message: vec![(message.to_owned(), Style::NoStyle)],
98 code,
99 span: MultiSpan::new(),
100 children: vec![],
101 suggestions: vec![],
102 sort_span: DUMMY_SP,
103 }
104 }
105
106 pub fn is_error(&self) -> bool {
107 match self.level {
108 Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
109
110 Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
111 }
112 }
113
114 /// Cancel the diagnostic (a structured diagnostic must either be emitted or
115 /// canceled or it will panic when dropped).
116 pub fn cancel(&mut self) {
117 self.level = Level::Cancelled;
118 }
119
120 pub fn cancelled(&self) -> bool {
121 self.level == Level::Cancelled
122 }
123
124 /// Set the sorting span.
125 pub fn set_sort_span(&mut self, sp: Span) {
126 self.sort_span = sp;
127 }
128
129 /// Adds a span/label to be included in the resulting snippet.
130 /// This label will be shown together with the original span/label used when creating the
131 /// diagnostic, *not* a span added by one of the `span_*` methods.
132 ///
133 /// This is pushed onto the `MultiSpan` that was created when the
134 /// diagnostic was first built. If you don't call this function at
135 /// all, and you just supplied a `Span` to create the diagnostic,
136 /// then the snippet will just include that `Span`, which is
137 /// called the primary span.
138 pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
139 self.span.push_span_label(span, label.into());
140 self
141 }
142
143 pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
144 let before = self.span.clone();
145 self.set_span(after);
146 for span_label in before.span_labels() {
147 if let Some(label) = span_label.label {
148 self.span_label(after, label);
149 }
150 }
151 self
152 }
153
154 pub fn note_expected_found(
155 &mut self,
156 expected_label: &dyn fmt::Display,
157 expected: DiagnosticStyledString,
158 found_label: &dyn fmt::Display,
159 found: DiagnosticStyledString,
160 ) -> &mut Self {
161 self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
162 }
163
164 pub fn note_unsuccessfull_coercion(
165 &mut self,
166 expected: DiagnosticStyledString,
167 found: DiagnosticStyledString,
168 ) -> &mut Self {
169 let mut msg: Vec<_> =
170 vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
171 msg.extend(expected.0.iter().map(|x| match *x {
172 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
173 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
174 }));
175 msg.push(("` to type '".to_string(), Style::NoStyle));
176 msg.extend(found.0.iter().map(|x| match *x {
177 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
178 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
179 }));
180 msg.push(("`".to_string(), Style::NoStyle));
181
182 // For now, just attach these as notes
183 self.highlighted_note(msg);
184 self
185 }
186
187 pub fn note_expected_found_extra(
188 &mut self,
189 expected_label: &dyn fmt::Display,
190 expected: DiagnosticStyledString,
191 found_label: &dyn fmt::Display,
192 found: DiagnosticStyledString,
193 expected_extra: &dyn fmt::Display,
194 found_extra: &dyn fmt::Display,
195 ) -> &mut Self {
196 let expected_label = expected_label.to_string();
197 let expected_label = if expected_label.is_empty() {
198 "expected".to_string()
199 } else {
200 format!("expected {}", expected_label)
201 };
202 let found_label = found_label.to_string();
203 let found_label = if found_label.is_empty() {
204 "found".to_string()
205 } else {
206 format!("found {}", found_label)
207 };
208 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
209 (expected_label.len() - found_label.len(), 0)
210 } else {
211 (0, found_label.len() - expected_label.len())
212 };
213 let mut msg: Vec<_> =
214 vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
215 msg.extend(expected.0.iter().map(|x| match *x {
216 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
217 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
218 }));
219 msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
220 msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
221 msg.extend(found.0.iter().map(|x| match *x {
222 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
223 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
224 }));
225 msg.push((format!("`{}", found_extra), Style::NoStyle));
226
227 // For now, just attach these as notes.
228 self.highlighted_note(msg);
229 self
230 }
231
232 pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
233 self.highlighted_note(vec![
234 (format!("`{}` from trait: `", name), Style::NoStyle),
235 (signature, Style::Highlight),
236 ("`".to_string(), Style::NoStyle),
237 ]);
238 self
239 }
240
241 pub fn note(&mut self, msg: &str) -> &mut Self {
242 self.sub(Level::Note, msg, MultiSpan::new(), None);
243 self
244 }
245
246 pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
247 self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
248 self
249 }
250
251 /// Prints the span with a note above it.
252 pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
253 self.sub(Level::Note, msg, sp.into(), None);
254 self
255 }
256
257 pub fn warn(&mut self, msg: &str) -> &mut Self {
258 self.sub(Level::Warning, msg, MultiSpan::new(), None);
259 self
260 }
261
262 /// Prints the span with a warn above it.
263 pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
264 self.sub(Level::Warning, msg, sp.into(), None);
265 self
266 }
267
268 pub fn help(&mut self, msg: &str) -> &mut Self {
269 self.sub(Level::Help, msg, MultiSpan::new(), None);
270 self
271 }
272
273 /// Prints the span with some help above it.
274 pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
275 self.sub(Level::Help, msg, sp.into(), None);
276 self
277 }
278
279 pub fn multipart_suggestion(
280 &mut self,
281 msg: &str,
282 suggestion: Vec<(Span, String)>,
283 applicability: Applicability,
284 ) -> &mut Self {
285 self.suggestions.push(CodeSuggestion {
286 substitutions: vec![Substitution {
287 parts: suggestion
288 .into_iter()
289 .map(|(span, snippet)| SubstitutionPart { snippet, span })
290 .collect(),
291 }],
292 msg: msg.to_owned(),
293 style: SuggestionStyle::ShowCode,
294 applicability,
295 });
296 self
297 }
298
299 pub fn multipart_suggestions(
300 &mut self,
301 msg: &str,
302 suggestions: Vec<Vec<(Span, String)>>,
303 applicability: Applicability,
304 ) -> &mut Self {
305 self.suggestions.push(CodeSuggestion {
306 substitutions: suggestions
307 .into_iter()
308 .map(|suggestion| Substitution {
309 parts: suggestion
310 .into_iter()
311 .map(|(span, snippet)| SubstitutionPart { snippet, span })
312 .collect(),
313 })
314 .collect(),
315 msg: msg.to_owned(),
316 style: SuggestionStyle::ShowCode,
317 applicability,
318 });
319 self
320 }
321
322 /// Prints out a message with for a multipart suggestion without showing the suggested code.
323 ///
324 /// This is intended to be used for suggestions that are obvious in what the changes need to
325 /// be from the message, showing the span label inline would be visually unpleasant
326 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
327 /// improve understandability.
328 pub fn tool_only_multipart_suggestion(
329 &mut self,
330 msg: &str,
331 suggestion: Vec<(Span, String)>,
332 applicability: Applicability,
333 ) -> &mut Self {
334 self.suggestions.push(CodeSuggestion {
335 substitutions: vec![Substitution {
336 parts: suggestion
337 .into_iter()
338 .map(|(span, snippet)| SubstitutionPart { snippet, span })
339 .collect(),
340 }],
341 msg: msg.to_owned(),
342 style: SuggestionStyle::CompletelyHidden,
343 applicability,
344 });
345 self
346 }
347
348 /// Prints out a message with a suggested edit of the code.
349 ///
350 /// In case of short messages and a simple suggestion, rustc displays it as a label:
351 ///
352 /// ```text
353 /// try adding parentheses: `(tup.0).1`
354 /// ```
355 ///
356 /// The message
357 ///
358 /// * should not end in any punctuation (a `:` is added automatically)
359 /// * should not be a question (avoid language like "did you mean")
360 /// * should not contain any phrases like "the following", "as shown", etc.
361 /// * may look like "to do xyz, use" or "to do xyz, use abc"
362 /// * may contain a name of a function, variable, or type, but not whole expressions
363 ///
364 /// See `CodeSuggestion` for more information.
365 pub fn span_suggestion(
366 &mut self,
367 sp: Span,
368 msg: &str,
369 suggestion: String,
370 applicability: Applicability,
371 ) -> &mut Self {
372 self.span_suggestion_with_style(
373 sp,
374 msg,
375 suggestion,
376 applicability,
377 SuggestionStyle::ShowCode,
378 );
379 self
380 }
381
382 pub fn span_suggestion_with_style(
383 &mut self,
384 sp: Span,
385 msg: &str,
386 suggestion: String,
387 applicability: Applicability,
388 style: SuggestionStyle,
389 ) -> &mut Self {
390 self.suggestions.push(CodeSuggestion {
391 substitutions: vec![Substitution {
392 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
393 }],
394 msg: msg.to_owned(),
395 style,
396 applicability,
397 });
398 self
399 }
400
401 pub fn span_suggestion_verbose(
402 &mut self,
403 sp: Span,
404 msg: &str,
405 suggestion: String,
406 applicability: Applicability,
407 ) -> &mut Self {
408 self.span_suggestion_with_style(
409 sp,
410 msg,
411 suggestion,
412 applicability,
413 SuggestionStyle::ShowAlways,
414 );
415 self
416 }
417
418 /// Prints out a message with multiple suggested edits of the code.
419 pub fn span_suggestions(
420 &mut self,
421 sp: Span,
422 msg: &str,
423 suggestions: impl Iterator<Item = String>,
424 applicability: Applicability,
425 ) -> &mut Self {
426 self.suggestions.push(CodeSuggestion {
427 substitutions: suggestions
428 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
429 .collect(),
430 msg: msg.to_owned(),
431 style: SuggestionStyle::ShowCode,
432 applicability,
433 });
434 self
435 }
436
437 /// Prints out a message with a suggested edit of the code. If the suggestion is presented
438 /// inline, it will only show the message and not the suggestion.
439 ///
440 /// See `CodeSuggestion` for more information.
441 pub fn span_suggestion_short(
442 &mut self,
443 sp: Span,
444 msg: &str,
445 suggestion: String,
446 applicability: Applicability,
447 ) -> &mut Self {
448 self.span_suggestion_with_style(
449 sp,
450 msg,
451 suggestion,
452 applicability,
453 SuggestionStyle::HideCodeInline,
454 );
455 self
456 }
457
458 /// Prints out a message with for a suggestion without showing the suggested code.
459 ///
460 /// This is intended to be used for suggestions that are obvious in what the changes need to
461 /// be from the message, showing the span label inline would be visually unpleasant
462 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
463 /// improve understandability.
464 pub fn span_suggestion_hidden(
465 &mut self,
466 sp: Span,
467 msg: &str,
468 suggestion: String,
469 applicability: Applicability,
470 ) -> &mut Self {
471 self.span_suggestion_with_style(
472 sp,
473 msg,
474 suggestion,
475 applicability,
476 SuggestionStyle::HideCodeAlways,
477 );
478 self
479 }
480
481 /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
482 ///
483 /// This is intended to be used for suggestions that are *very* obvious in what the changes
484 /// need to be from the message, but we still want other tools to be able to apply them.
485 pub fn tool_only_span_suggestion(
486 &mut self,
487 sp: Span,
488 msg: &str,
489 suggestion: String,
490 applicability: Applicability,
491 ) -> &mut Self {
492 self.span_suggestion_with_style(
493 sp,
494 msg,
495 suggestion,
496 applicability,
497 SuggestionStyle::CompletelyHidden,
498 );
499 self
500 }
501
502 pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
503 self.span = sp.into();
504 if let Some(span) = self.span.primary_span() {
505 self.sort_span = span;
506 }
507 self
508 }
509
510 pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
511 self.code = Some(s);
512 self
513 }
514
515 pub fn clear_code(&mut self) -> &mut Self {
516 self.code = None;
517 self
518 }
519
520 pub fn get_code(&self) -> Option<DiagnosticId> {
521 self.code.clone()
522 }
523
524 pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
525 self.message[0] = (msg.into(), Style::NoStyle);
526 self
527 }
528
529 pub fn message(&self) -> String {
530 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
531 }
532
533 pub fn styled_message(&self) -> &Vec<(String, Style)> {
534 &self.message
535 }
536
537 /// Used by a lint. Copies over all details *but* the "main
538 /// message".
539 pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
540 self.span = from.span.clone();
541 self.code = from.code.clone();
542 self.children.extend(from.children.iter().cloned())
543 }
544
545 /// Convenience function for internal use, clients should use one of the
546 /// public methods above.
547 pub fn sub(
548 &mut self,
549 level: Level,
550 message: &str,
551 span: MultiSpan,
552 render_span: Option<MultiSpan>,
553 ) {
554 let sub = SubDiagnostic {
555 level,
556 message: vec![(message.to_owned(), Style::NoStyle)],
557 span,
558 render_span,
559 };
560 self.children.push(sub);
561 }
562
563 /// Convenience function for internal use, clients should use one of the
564 /// public methods above.
565 fn sub_with_highlights(
566 &mut self,
567 level: Level,
568 message: Vec<(String, Style)>,
569 span: MultiSpan,
570 render_span: Option<MultiSpan>,
571 ) {
572 let sub = SubDiagnostic { level, message, span, render_span };
573 self.children.push(sub);
574 }
575 }
576
577 impl SubDiagnostic {
578 pub fn message(&self) -> String {
579 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
580 }
581
582 pub fn styled_message(&self) -> &Vec<(String, Style)> {
583 &self.message
584 }
585 }