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