1use std::fmt;
12
13use super::{snippet::Style, Applicability, CodeSuggestion, Level, Substitution, SubstitutionPart};
14use crate::syntax_pos::{MultiSpan, Span};
15
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17#[cfg_attr(
18 feature = "diagnostic-serde",
19 derive(serde::Serialize, serde::Deserialize)
20)]
21#[cfg_attr(
22 any(feature = "rkyv-impl"),
23 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
24)]
25#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
26#[cfg_attr(feature = "rkyv-impl", repr(C))]
27pub struct Message(pub String, pub Style);
28
29#[must_use]
30#[derive(Clone, Debug, PartialEq, Eq, Hash)]
31#[cfg_attr(
32 feature = "diagnostic-serde",
33 derive(serde::Serialize, serde::Deserialize)
34)]
35#[cfg_attr(
36 any(feature = "rkyv-impl"),
37 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
38)]
39#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
40#[cfg_attr(feature = "rkyv-impl", repr(C))]
41pub struct Diagnostic {
42 pub level: Level,
43 pub message: Vec<Message>,
44 pub code: Option<DiagnosticId>,
45 pub span: MultiSpan,
46 pub children: Vec<SubDiagnostic>,
47 pub suggestions: Vec<CodeSuggestion>,
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[cfg_attr(
52 feature = "diagnostic-serde",
53 derive(serde::Serialize, serde::Deserialize)
54)]
55#[cfg_attr(
56 any(feature = "rkyv-impl"),
57 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
58)]
59#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
60#[cfg_attr(feature = "rkyv-impl", repr(u32))]
61pub enum DiagnosticId {
62 Error(String),
63 Lint(String),
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, Hash)]
68#[cfg_attr(
69 feature = "diagnostic-serde",
70 derive(serde::Serialize, serde::Deserialize)
71)]
72#[cfg_attr(
73 any(feature = "rkyv-impl"),
74 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
75)]
76#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
77#[cfg_attr(feature = "rkyv-impl", repr(C))]
78pub struct SubDiagnostic {
79 pub level: Level,
80 pub message: Vec<Message>,
81 pub span: MultiSpan,
82 pub render_span: Option<MultiSpan>,
83}
84
85#[derive(PartialEq, Eq, Default)]
86pub struct DiagnosticStyledString(pub Vec<StringPart>);
87
88impl DiagnosticStyledString {
89 pub fn new() -> DiagnosticStyledString {
90 Default::default()
91 }
92
93 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
94 self.0.push(StringPart::Normal(t.into()));
95 }
96
97 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
98 self.0.push(StringPart::Highlighted(t.into()));
99 }
100
101 pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
102 DiagnosticStyledString(vec![StringPart::Normal(t.into())])
103 }
104
105 pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
106 DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
107 }
108
109 pub fn content(&self) -> String {
110 self.0.iter().map(|x| x.content()).collect::<String>()
111 }
112}
113
114#[derive(PartialEq, Eq)]
115pub enum StringPart {
116 Normal(String),
117 Highlighted(String),
118}
119
120impl StringPart {
121 pub fn content(&self) -> &str {
122 match self {
123 &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
124 }
125 }
126}
127
128impl Diagnostic {
129 pub fn new(level: Level, message: &str) -> Self {
130 Diagnostic::new_with_code(level, None, message)
131 }
132
133 pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
134 Diagnostic {
135 level,
136 message: vec![Message(message.to_owned(), Style::NoStyle)],
137 code,
138 span: MultiSpan::new(),
139 children: Vec::new(),
140 suggestions: Vec::new(),
141 }
142 }
143
144 pub fn is_error(&self) -> bool {
145 match self.level {
146 Level::Bug | Level::Fatal | Level::PhaseFatal | Level::Error | Level::FailureNote => {
147 true
148 }
149
150 Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
151 }
152 }
153
154 pub fn cancel(&mut self) {
157 self.level = Level::Cancelled;
158 }
159
160 pub fn cancelled(&self) -> bool {
161 self.level == Level::Cancelled
162 }
163
164 pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
171 self.span.push_span_label(span, label.into());
172 self
173 }
174
175 pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
176 let before = self.span.clone();
177 self.set_span(after);
178 for span_label in before.span_labels() {
179 if let Some(label) = span_label.label {
180 self.span_label(after, label);
181 }
182 }
183 self
184 }
185
186 pub fn note_expected_found(
187 &mut self,
188 label: &dyn fmt::Display,
189 expected: DiagnosticStyledString,
190 found: DiagnosticStyledString,
191 ) -> &mut Self {
192 self.note_expected_found_extra(label, expected, found, &"", &"")
193 }
194
195 pub fn note_expected_found_extra(
196 &mut self,
197 label: &dyn fmt::Display,
198 expected: DiagnosticStyledString,
199 found: DiagnosticStyledString,
200 expected_extra: &dyn fmt::Display,
201 found_extra: &dyn fmt::Display,
202 ) -> &mut Self {
203 let mut msg: Vec<_> = vec![Message(format!("expected {} `", label), Style::NoStyle)];
204 msg.extend(expected.0.iter().map(|x| match *x {
205 StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
206 StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
207 }));
208 msg.push(Message(format!("`{}\n", expected_extra), Style::NoStyle));
209 msg.push(Message(format!(" found {} `", label), Style::NoStyle));
210 msg.extend(found.0.iter().map(|x| match *x {
211 StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
212 StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
213 }));
214 msg.push(Message(format!("`{}", found_extra), Style::NoStyle));
215
216 self.highlighted_note(msg);
218 self
219 }
220
221 pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
222 self.highlighted_note(vec![
223 Message(format!("`{}` from trait: `", name), Style::NoStyle),
224 Message(signature, Style::Highlight),
225 Message("`".to_string(), Style::NoStyle),
226 ]);
227 self
228 }
229
230 pub fn note(&mut self, msg: &str) -> &mut Self {
231 self.sub(Level::Note, msg, MultiSpan::new(), None);
232 self
233 }
234
235 pub fn highlighted_note(&mut self, msg: Vec<Message>) -> &mut Self {
236 self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
237 self
238 }
239
240 pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
241 self.sub(Level::Note, msg, sp.into(), None);
242 self
243 }
244
245 pub fn warn(&mut self, msg: &str) -> &mut Self {
246 self.sub(Level::Warning, msg, MultiSpan::new(), None);
247 self
248 }
249
250 pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
251 self.sub(Level::Warning, msg, sp.into(), None);
252 self
253 }
254
255 pub fn help(&mut self, msg: &str) -> &mut Self {
256 self.sub(Level::Help, msg, MultiSpan::new(), None);
257 self
258 }
259
260 pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
261 self.sub(Level::Help, msg, sp.into(), None);
262 self
263 }
264
265 #[deprecated(note = "Use `span_suggestion_short_with_applicability`")]
271 pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
272 self.suggestions.push(CodeSuggestion {
273 substitutions: vec![Substitution {
274 parts: vec![SubstitutionPart {
275 snippet: suggestion,
276 span: sp,
277 }],
278 }],
279 msg: msg.to_owned(),
280 show_code_when_inline: false,
281 applicability: Applicability::Unspecified,
282 });
283 self
284 }
285
286 #[deprecated(note = "Use `span_suggestion_with_applicability`")]
304 pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
305 self.suggestions.push(CodeSuggestion {
306 substitutions: vec![Substitution {
307 parts: vec![SubstitutionPart {
308 snippet: suggestion,
309 span: sp,
310 }],
311 }],
312 msg: msg.to_owned(),
313 show_code_when_inline: true,
314 applicability: Applicability::Unspecified,
315 });
316 self
317 }
318
319 pub fn multipart_suggestion_with_applicability(
320 &mut self,
321 msg: &str,
322 suggestion: Vec<(Span, String)>,
323 applicability: Applicability,
324 ) -> &mut Self {
325 self.suggestions.push(CodeSuggestion {
326 substitutions: vec![Substitution {
327 parts: suggestion
328 .into_iter()
329 .map(|(span, snippet)| SubstitutionPart { snippet, span })
330 .collect(),
331 }],
332 msg: msg.to_owned(),
333 show_code_when_inline: true,
334 applicability,
335 });
336 self
337 }
338
339 #[deprecated(note = "Use `multipart_suggestion_with_applicability`")]
340 pub fn multipart_suggestion(
341 &mut self,
342 msg: &str,
343 suggestion: Vec<(Span, String)>,
344 ) -> &mut Self {
345 self.multipart_suggestion_with_applicability(msg, suggestion, Applicability::Unspecified)
346 }
347
348 #[deprecated(note = "Use `span_suggestions_with_applicability`")]
350 pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self {
351 self.suggestions.push(CodeSuggestion {
352 substitutions: suggestions
353 .into_iter()
354 .map(|snippet| Substitution {
355 parts: vec![SubstitutionPart { snippet, span: sp }],
356 })
357 .collect(),
358 msg: msg.to_owned(),
359 show_code_when_inline: true,
360 applicability: Applicability::Unspecified,
361 });
362 self
363 }
364
365 pub fn span_suggestion_with_applicability(
368 &mut self,
369 sp: Span,
370 msg: &str,
371 suggestion: String,
372 applicability: Applicability,
373 ) -> &mut Self {
374 self.suggestions.push(CodeSuggestion {
375 substitutions: vec![Substitution {
376 parts: vec![SubstitutionPart {
377 snippet: suggestion,
378 span: sp,
379 }],
380 }],
381 msg: msg.to_owned(),
382 show_code_when_inline: true,
383 applicability,
384 });
385 self
386 }
387
388 pub fn span_suggestions_with_applicability(
389 &mut self,
390 sp: Span,
391 msg: &str,
392 suggestions: impl Iterator<Item = String>,
393 applicability: Applicability,
394 ) -> &mut Self {
395 self.suggestions.push(CodeSuggestion {
396 substitutions: suggestions
397 .map(|snippet| Substitution {
398 parts: vec![SubstitutionPart { snippet, span: sp }],
399 })
400 .collect(),
401 msg: msg.to_owned(),
402 show_code_when_inline: true,
403 applicability,
404 });
405 self
406 }
407
408 pub fn span_suggestion_short_with_applicability(
409 &mut self,
410 sp: Span,
411 msg: &str,
412 suggestion: String,
413 applicability: Applicability,
414 ) -> &mut Self {
415 self.suggestions.push(CodeSuggestion {
416 substitutions: vec![Substitution {
417 parts: vec![SubstitutionPart {
418 snippet: suggestion,
419 span: sp,
420 }],
421 }],
422 msg: msg.to_owned(),
423 show_code_when_inline: false,
424 applicability,
425 });
426 self
427 }
428
429 pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
430 self.span = sp.into();
431 self
432 }
433
434 pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
435 self.code = Some(s);
436 self
437 }
438
439 pub fn get_code(&self) -> Option<DiagnosticId> {
440 self.code.clone()
441 }
442
443 pub fn message(&self) -> String {
444 self.message
445 .iter()
446 .map(|i| i.0.as_str())
447 .collect::<String>()
448 }
449
450 pub fn styled_message(&self) -> &Vec<Message> {
451 &self.message
452 }
453
454 pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
457 self.span = from.span.clone();
458 self.code.clone_from(&from.code);
459 self.children.extend(from.children.iter().cloned())
460 }
461
462 pub fn sub(
465 &mut self,
466 level: Level,
467 message: &str,
468 span: MultiSpan,
469 render_span: Option<MultiSpan>,
470 ) {
471 let sub = SubDiagnostic {
472 level,
473 message: vec![Message(message.to_owned(), Style::NoStyle)],
474 span,
475 render_span,
476 };
477 self.children.push(sub);
478 }
479
480 fn sub_with_highlights(
483 &mut self,
484 level: Level,
485 message: Vec<Message>,
486 span: MultiSpan,
487 render_span: Option<MultiSpan>,
488 ) {
489 let sub = SubDiagnostic {
490 level,
491 message,
492 span,
493 render_span,
494 };
495 self.children.push(sub);
496 }
497}
498
499impl SubDiagnostic {
500 pub fn message(&self) -> String {
501 self.message
502 .iter()
503 .map(|i| i.0.as_str())
504 .collect::<String>()
505 }
506
507 pub fn styled_message(&self) -> &Vec<Message> {
508 &self.message
509 }
510}