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