1use crate::Span;
6use anstyle::{AnsiColor, Color};
7use std::{borrow::Cow, fmt, panic::Location};
8
9mod builder;
10pub use builder::{DiagnosticBuilder, EmissionGuarantee};
11
12mod context;
13pub use context::{DiagCtxt, DiagCtxtFlags};
14
15mod emitter;
16#[cfg(feature = "json")]
17pub use emitter::JsonEmitter;
18pub use emitter::{
19 DynEmitter, Emitter, HumanBufferEmitter, HumanEmitter, LocalEmitter, SilentEmitter,
20};
21
22mod message;
23pub use message::{DiagnosticMessage, MultiSpan, SpanLabel};
24
25pub struct EmittedDiagnostics(pub(crate) String);
29
30impl fmt::Debug for EmittedDiagnostics {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 f.write_str(&self.0)
33 }
34}
35
36impl fmt::Display for EmittedDiagnostics {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_str(&self.0)
39 }
40}
41
42impl std::error::Error for EmittedDiagnostics {}
43
44impl EmittedDiagnostics {
45 pub fn is_empty(&self) -> bool {
47 self.0.is_empty()
48 }
49}
50
51#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
54pub struct ErrorGuaranteed(());
55
56impl fmt::Debug for ErrorGuaranteed {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.write_str("ErrorGuaranteed")
59 }
60}
61
62impl ErrorGuaranteed {
63 #[inline]
67 pub const fn new_unchecked() -> Self {
68 Self(())
69 }
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct BugAbort;
76
77pub struct ExplicitBug;
80
81pub struct FatalAbort;
83
84#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct DiagnosticId {
96 id: u32,
97}
98
99impl DiagnosticId {
100 #[doc(hidden)]
104 #[track_caller]
105 pub const fn new_from_macro(id: u32) -> Self {
106 assert!(id >= 1 && id <= 9999, "error code must be in range 0001-9999");
107 Self { id }
108 }
109
110 pub fn as_string(&self) -> String {
112 format!("{:04}", self.id)
113 }
114}
115
116#[macro_export]
125macro_rules! error_code {
126 ($id:literal) => {
127 const { $crate::diagnostics::DiagnosticId::new_from_macro($id) }
128 };
129}
130
131#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
133pub enum Level {
134 Bug,
138
139 Fatal,
144
145 Error,
150
151 Warning,
155
156 Note,
161
162 OnceNote,
166
167 Help,
172
173 OnceHelp,
177
178 FailureNote,
182
183 Allow,
187}
188
189impl Level {
190 pub fn to_str(self) -> &'static str {
192 match self {
193 Self::Bug => "error: internal compiler error",
194 Self::Fatal | Self::Error => "error",
195 Self::Warning => "warning",
196 Self::Note | Self::OnceNote => "note",
197 Self::Help | Self::OnceHelp => "help",
198 Self::FailureNote => "failure-note",
199 Self::Allow
200 => unreachable!(),
202 }
203 }
204
205 #[inline]
207 pub fn is_error(self) -> bool {
208 match self {
209 Self::Bug | Self::Fatal | Self::Error | Self::FailureNote => true,
210
211 Self::Warning
212 | Self::Note
213 | Self::OnceNote
214 | Self::Help
215 | Self::OnceHelp
216 | Self::Allow => false,
217 }
218 }
219
220 #[inline]
222 pub const fn style(self) -> anstyle::Style {
223 anstyle::Style::new().fg_color(self.color()).bold()
224 }
225
226 #[inline]
228 pub const fn color(self) -> Option<Color> {
229 match self.ansi_color() {
230 Some(c) => Some(Color::Ansi(c)),
231 None => None,
232 }
233 }
234
235 #[inline]
237 pub const fn ansi_color(self) -> Option<AnsiColor> {
238 match self {
240 Self::Bug | Self::Fatal | Self::Error => Some(AnsiColor::BrightRed),
241 Self::Warning => Some(AnsiColor::BrightYellow),
242 Self::Note | Self::OnceNote => Some(AnsiColor::BrightGreen),
243 Self::Help | Self::OnceHelp => Some(AnsiColor::BrightCyan),
244 Self::FailureNote | Self::Allow => None,
245 }
246 }
247}
248
249#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
250pub enum Style {
251 MainHeaderMsg,
252 HeaderMsg,
253 LineAndColumn,
254 LineNumber,
255 Quotation,
256 UnderlinePrimary,
257 UnderlineSecondary,
258 LabelPrimary,
259 LabelSecondary,
260 NoStyle,
261 Level(Level),
262 Highlight,
263 Addition,
264 Removal,
265}
266
267impl Style {
268 pub const fn to_color_spec(self, level: Level) -> anstyle::Style {
270 use AnsiColor::*;
271
272 const BRIGHT_BLUE: Color = Color::Ansi(if cfg!(windows) { BrightCyan } else { BrightBlue });
276 const GREEN: Color = Color::Ansi(BrightGreen);
277 const MAGENTA: Color = Color::Ansi(BrightMagenta);
278 const RED: Color = Color::Ansi(BrightRed);
279 const WHITE: Color = Color::Ansi(BrightWhite);
280
281 let s = anstyle::Style::new();
282 match self {
283 Self::Addition => s.fg_color(Some(GREEN)),
284 Self::Removal => s.fg_color(Some(RED)),
285 Self::LineAndColumn => s,
286 Self::LineNumber => s.fg_color(Some(BRIGHT_BLUE)).bold(),
287 Self::Quotation => s,
288 Self::MainHeaderMsg => if cfg!(windows) { s.fg_color(Some(WHITE)) } else { s }.bold(),
289 Self::UnderlinePrimary | Self::LabelPrimary => s.fg_color(level.color()).bold(),
290 Self::UnderlineSecondary | Self::LabelSecondary => s.fg_color(Some(BRIGHT_BLUE)).bold(),
291 Self::HeaderMsg | Self::NoStyle => s,
292 Self::Level(level2) => s.fg_color(level2.color()).bold(),
293 Self::Highlight => s.fg_color(Some(MAGENTA)).bold(),
294 }
295 }
296}
297
298#[derive(Clone, Debug, PartialEq, Hash)]
301pub struct SubDiagnostic {
302 pub level: Level,
303 pub messages: Vec<(DiagnosticMessage, Style)>,
304 pub span: MultiSpan,
305}
306
307impl SubDiagnostic {
308 pub fn label(&self) -> Cow<'_, str> {
310 flatten_messages(&self.messages)
311 }
312}
313
314#[must_use]
316#[derive(Clone, Debug)]
317pub struct Diagnostic {
318 pub(crate) level: Level,
319
320 pub messages: Vec<(DiagnosticMessage, Style)>,
321 pub span: MultiSpan,
322 pub children: Vec<SubDiagnostic>,
323 pub code: Option<DiagnosticId>,
324
325 pub created_at: &'static Location<'static>,
326}
327
328impl PartialEq for Diagnostic {
329 fn eq(&self, other: &Self) -> bool {
330 self.keys() == other.keys()
331 }
332}
333
334impl std::hash::Hash for Diagnostic {
335 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
336 self.keys().hash(state);
337 }
338}
339
340impl Diagnostic {
341 #[track_caller]
343 pub fn new<M: Into<DiagnosticMessage>>(level: Level, msg: M) -> Self {
344 Self::new_with_messages(level, vec![(msg.into(), Style::NoStyle)])
345 }
346
347 #[track_caller]
349 pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self {
350 Self {
351 level,
352 messages,
353 code: None,
354 span: MultiSpan::new(),
355 children: vec![],
356 created_at: Location::caller(),
361 }
362 }
363
364 #[inline]
366 pub fn is_error(&self) -> bool {
367 self.level.is_error()
368 }
369
370 pub fn label(&self) -> Cow<'_, str> {
372 flatten_messages(&self.messages)
373 }
374
375 pub fn messages(&self) -> &[(DiagnosticMessage, Style)] {
377 &self.messages
378 }
379
380 pub fn level(&self) -> Level {
382 self.level
383 }
384
385 pub fn id(&self) -> Option<String> {
387 self.code.as_ref().map(|code| code.as_string())
388 }
389
390 fn keys(&self) -> impl PartialEq + std::hash::Hash + '_ {
392 (
393 &self.level,
394 &self.messages,
395 &self.code,
397 &self.span,
398 &self.children,
401 )
402 }
403}
404
405impl Diagnostic {
407 pub fn span(&mut self, span: impl Into<MultiSpan>) -> &mut Self {
409 self.span = span.into();
410 self
411 }
412
413 pub fn code(&mut self, code: impl Into<DiagnosticId>) -> &mut Self {
415 self.code = Some(code.into());
416 self
417 }
418
419 pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self {
428 self.span.push_span_label(span, label);
429 self
430 }
431
432 pub fn span_labels(
435 &mut self,
436 spans: impl IntoIterator<Item = Span>,
437 label: impl Into<DiagnosticMessage>,
438 ) -> &mut Self {
439 let label = label.into();
440 for span in spans {
441 self.span_label(span, label.clone());
442 }
443 self
444 }
445
446 pub(crate) fn locations_note(&mut self, emitted_at: &Location<'_>) -> &mut Self {
448 let msg = format!(
449 "created at {},\n\
450 emitted at {}",
451 self.created_at, emitted_at
452 );
453 self.note(msg)
454 }
455}
456
457impl Diagnostic {
459 pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
461 self.sub(Level::Warning, msg, MultiSpan::new())
462 }
463
464 pub fn span_warn(
467 &mut self,
468 span: impl Into<MultiSpan>,
469 msg: impl Into<DiagnosticMessage>,
470 ) -> &mut Self {
471 self.sub(Level::Warning, msg, span)
472 }
473
474 pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
476 self.sub(Level::Note, msg, MultiSpan::new())
477 }
478
479 pub fn span_note(
482 &mut self,
483 span: impl Into<MultiSpan>,
484 msg: impl Into<DiagnosticMessage>,
485 ) -> &mut Self {
486 self.sub(Level::Note, msg, span)
487 }
488
489 pub fn highlighted_note(
490 &mut self,
491 messages: Vec<(impl Into<DiagnosticMessage>, Style)>,
492 ) -> &mut Self {
493 self.sub_with_highlights(Level::Note, messages, MultiSpan::new())
494 }
495
496 pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
499 self.sub(Level::OnceNote, msg, MultiSpan::new())
500 }
501
502 pub fn span_note_once(
505 &mut self,
506 span: impl Into<MultiSpan>,
507 msg: impl Into<DiagnosticMessage>,
508 ) -> &mut Self {
509 self.sub(Level::OnceNote, msg, span)
510 }
511
512 pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
514 self.sub(Level::Help, msg, MultiSpan::new())
515 }
516
517 pub fn help_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
520 self.sub(Level::OnceHelp, msg, MultiSpan::new())
521 }
522
523 pub fn highlighted_help(
525 &mut self,
526 msgs: Vec<(impl Into<DiagnosticMessage>, Style)>,
527 ) -> &mut Self {
528 self.sub_with_highlights(Level::Help, msgs, MultiSpan::new())
529 }
530
531 pub fn span_help(
534 &mut self,
535 span: impl Into<MultiSpan>,
536 msg: impl Into<DiagnosticMessage>,
537 ) -> &mut Self {
538 self.sub(Level::Help, msg, span)
539 }
540
541 fn sub(
542 &mut self,
543 level: Level,
544 msg: impl Into<DiagnosticMessage>,
545 span: impl Into<MultiSpan>,
546 ) -> &mut Self {
547 self.children.push(SubDiagnostic {
548 level,
549 messages: vec![(msg.into(), Style::NoStyle)],
550 span: span.into(),
551 });
552 self
553 }
554
555 fn sub_with_highlights(
556 &mut self,
557 level: Level,
558 messages: Vec<(impl Into<DiagnosticMessage>, Style)>,
559 span: MultiSpan,
560 ) -> &mut Self {
561 let messages = messages.into_iter().map(|(m, s)| (m.into(), s)).collect();
562 self.children.push(SubDiagnostic { level, messages, span });
563 self
564 }
565}
566
567fn flatten_messages(messages: &[(DiagnosticMessage, Style)]) -> Cow<'_, str> {
569 match messages {
570 [] => Cow::Borrowed(""),
571 [(message, _)] => Cow::Borrowed(message.as_str()),
572 messages => messages.iter().map(|(msg, _)| msg.as_str()).collect(),
573 }
574}