solar_interface/diagnostics/emitter/
mod.rs

1use super::{Diag, Level};
2use crate::SourceMap;
3use std::{any::Any, sync::Arc};
4
5mod human;
6pub use human::{HumanBufferEmitter, HumanEmitter};
7
8#[cfg(feature = "json")]
9mod json;
10#[cfg(feature = "json")]
11pub use json::JsonEmitter;
12
13mod rustc;
14
15/// Dynamic diagnostic emitter. See [`Emitter`].
16pub type DynEmitter = dyn Emitter + Send;
17
18/// Diagnostic emitter.
19pub trait Emitter: Any {
20    /// Emits a diagnostic.
21    fn emit_diagnostic(&mut self, diagnostic: &Diag);
22
23    /// Returns a reference to the source map, if any.
24    #[inline]
25    fn source_map(&self) -> Option<&Arc<SourceMap>> {
26        None
27    }
28
29    /// Returns `true` if we can use colors in the current output stream.
30    #[inline]
31    fn supports_color(&self) -> bool {
32        false
33    }
34}
35
36impl DynEmitter {
37    pub(crate) fn local_buffer(&self) -> Option<&str> {
38        (self as &dyn Any).downcast_ref::<HumanBufferEmitter>().map(HumanBufferEmitter::buffer)
39    }
40}
41
42/// Diagnostic emitter.
43///
44/// Emits fatal diagnostics by default, with `note` if set.
45pub struct SilentEmitter {
46    fatal_emitter: Option<Box<DynEmitter>>,
47    note: Option<String>,
48}
49
50impl SilentEmitter {
51    /// Creates a new `SilentEmitter`. Emits fatal diagnostics with `fatal_emitter`.
52    pub fn new(fatal_emitter: impl Emitter + Send) -> Self {
53        Self::new_boxed(Some(Box::new(fatal_emitter)))
54    }
55
56    /// Creates a new `SilentEmitter`. Emits fatal diagnostics with `fatal_emitter` if `Some`.
57    pub fn new_boxed(fatal_emitter: Option<Box<DynEmitter>>) -> Self {
58        Self { fatal_emitter, note: None }
59    }
60
61    /// Creates a new `SilentEmitter` that does not emit any diagnostics at all.
62    ///
63    /// Same as `new_boxed(None)`.
64    pub fn new_silent() -> Self {
65        Self::new_boxed(None)
66    }
67
68    /// Sets the note to be emitted for fatal diagnostics.
69    pub fn with_note(mut self, note: Option<String>) -> Self {
70        self.note = note;
71        self
72    }
73}
74
75impl Emitter for SilentEmitter {
76    fn emit_diagnostic(&mut self, diagnostic: &Diag) {
77        let Some(fatal_emitter) = self.fatal_emitter.as_deref_mut() else { return };
78        if diagnostic.level != Level::Fatal {
79            return;
80        }
81
82        if let Some(note) = &self.note {
83            let mut diagnostic = diagnostic.clone();
84            diagnostic.note(note.clone());
85            fatal_emitter.emit_diagnostic(&diagnostic);
86        } else {
87            fatal_emitter.emit_diagnostic(diagnostic);
88        }
89    }
90}
91
92/// Diagnostic emitter that only stores emitted diagnostics.
93#[derive(Clone, Debug)]
94pub struct LocalEmitter {
95    diagnostics: Vec<Diag>,
96}
97
98impl Default for LocalEmitter {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl LocalEmitter {
105    /// Creates a new `LocalEmitter`.
106    pub fn new() -> Self {
107        Self { diagnostics: Vec::new() }
108    }
109
110    /// Returns a reference to the emitted diagnostics.
111    pub fn diagnostics(&self) -> &[Diag] {
112        &self.diagnostics
113    }
114
115    /// Consumes the emitter and returns the emitted diagnostics.
116    pub fn into_diagnostics(self) -> Vec<Diag> {
117        self.diagnostics
118    }
119}
120
121impl Emitter for LocalEmitter {
122    fn emit_diagnostic(&mut self, diagnostic: &Diag) {
123        self.diagnostics.push(diagnostic.clone());
124    }
125}
126
127#[cold]
128#[inline(never)]
129fn io_panic(error: std::io::Error) -> ! {
130    panic!("failed to emit diagnostic: {error}");
131}