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