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.downcast_ref::<HumanBufferEmitter>().map(HumanBufferEmitter::buffer)
39    }
40
41    // TODO: Remove when dyn trait upcasting is stable.
42    fn downcast_ref<T: Any>(&self) -> Option<&T> {
43        if self.type_id() == std::any::TypeId::of::<T>() {
44            unsafe { Some(&*(self as *const dyn Emitter as *const T)) }
45        } else {
46            None
47        }
48    }
49}
50
51/// Diagnostic emitter.
52///
53/// Emits fatal diagnostics by default, with `note` if set.
54pub struct SilentEmitter {
55    fatal_emitter: Option<Box<DynEmitter>>,
56    note: Option<String>,
57}
58
59impl SilentEmitter {
60    /// Creates a new `SilentEmitter`. Emits fatal diagnostics with `fatal_emitter`.
61    pub fn new(fatal_emitter: impl Emitter + Send) -> Self {
62        Self::new_boxed(Some(Box::new(fatal_emitter)))
63    }
64
65    /// Creates a new `SilentEmitter`. Emits fatal diagnostics with `fatal_emitter` if `Some`.
66    pub fn new_boxed(fatal_emitter: Option<Box<DynEmitter>>) -> Self {
67        Self { fatal_emitter, note: None }
68    }
69
70    /// Creates a new `SilentEmitter` that does not emit any diagnostics at all.
71    ///
72    /// Same as `new_boxed(None)`.
73    pub fn new_silent() -> Self {
74        Self::new_boxed(None)
75    }
76
77    /// Sets the note to be emitted for fatal diagnostics.
78    pub fn with_note(mut self, note: Option<String>) -> Self {
79        self.note = note;
80        self
81    }
82}
83
84impl Emitter for SilentEmitter {
85    fn emit_diagnostic(&mut self, diagnostic: &Diag) {
86        let Some(fatal_emitter) = self.fatal_emitter.as_deref_mut() else { return };
87        if diagnostic.level != Level::Fatal {
88            return;
89        }
90
91        if let Some(note) = &self.note {
92            let mut diagnostic = diagnostic.clone();
93            diagnostic.note(note.clone());
94            fatal_emitter.emit_diagnostic(&diagnostic);
95        } else {
96            fatal_emitter.emit_diagnostic(diagnostic);
97        }
98    }
99}
100
101/// Diagnostic emitter that only stores emitted diagnostics.
102#[derive(Clone, Debug)]
103pub struct LocalEmitter {
104    diagnostics: Vec<Diag>,
105}
106
107impl Default for LocalEmitter {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113impl LocalEmitter {
114    /// Creates a new `LocalEmitter`.
115    pub fn new() -> Self {
116        Self { diagnostics: Vec::new() }
117    }
118
119    /// Returns a reference to the emitted diagnostics.
120    pub fn diagnostics(&self) -> &[Diag] {
121        &self.diagnostics
122    }
123
124    /// Consumes the emitter and returns the emitted diagnostics.
125    pub fn into_diagnostics(self) -> Vec<Diag> {
126        self.diagnostics
127    }
128}
129
130impl Emitter for LocalEmitter {
131    fn emit_diagnostic(&mut self, diagnostic: &Diag) {
132        self.diagnostics.push(diagnostic.clone());
133    }
134}
135
136#[cold]
137#[inline(never)]
138fn io_panic(error: std::io::Error) -> ! {
139    panic!("failed to emit diagnostic: {error}");
140}