solar_interface/diagnostics/emitter/
mod.rs1use super::{Diag, Level, MultiSpan, SuggestionStyle};
2use crate::{SourceMap, diagnostics::Suggestions};
3use std::{any::Any, borrow::Cow, 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 mem;
14pub use mem::InMemoryEmitter;
15
16mod rustc;
17
18pub type DynEmitter = dyn Emitter + Send;
20
21pub trait Emitter: Any {
23 fn emit_diagnostic(&mut self, diagnostic: &mut Diag);
25
26 #[inline]
28 fn source_map(&self) -> Option<&Arc<SourceMap>> {
29 None
30 }
31
32 #[inline]
34 fn supports_color(&self) -> bool {
35 false
36 }
37
38 fn primary_span_formatted<'a>(
49 &self,
50 primary_span: &mut Cow<'a, MultiSpan>,
51 suggestions: &mut Suggestions,
52 ) {
53 if let Some((sugg, rest)) = &suggestions.split_first()
54 && rest.is_empty()
57 && let [substitution] = sugg.substitutions.as_slice()
59 && let [part] = substitution.parts.as_slice()
61 && sugg.msg.as_str().split_whitespace().count() < 10
63 && !part.snippet.contains('\n')
65 && ![
66 SuggestionStyle::HideCodeAlways,
68 SuggestionStyle::CompletelyHidden,
70 SuggestionStyle::ShowAlways,
72 ].contains(&sugg.style)
73 {
74 let snippet = part.snippet.trim();
75 let msg = if snippet.is_empty() || sugg.style.hide_inline() {
76 format!("help: {}", sugg.msg.as_str())
79 } else {
80 format!("help: {}: `{}`", sugg.msg.as_str(), snippet)
81 };
82 primary_span.to_mut().push_span_label(part.span, msg);
83
84 *suggestions = Suggestions::Disabled;
86 } else {
87 }
89 }
90}
91
92impl DynEmitter {
93 pub(crate) fn local_buffer(&self) -> Option<&str> {
94 (self as &dyn Any).downcast_ref::<HumanBufferEmitter>().map(HumanBufferEmitter::buffer)
95 }
96}
97
98pub struct SilentEmitter {
102 fatal_emitter: Option<Box<DynEmitter>>,
103 note: Option<String>,
104}
105
106impl SilentEmitter {
107 pub fn new(fatal_emitter: impl Emitter + Send) -> Self {
109 Self::new_boxed(Some(Box::new(fatal_emitter)))
110 }
111
112 pub fn new_boxed(fatal_emitter: Option<Box<DynEmitter>>) -> Self {
114 Self { fatal_emitter, note: None }
115 }
116
117 pub fn new_silent() -> Self {
121 Self::new_boxed(None)
122 }
123
124 pub fn with_note(mut self, note: Option<String>) -> Self {
126 self.note = note;
127 self
128 }
129}
130
131impl Emitter for SilentEmitter {
132 fn emit_diagnostic(&mut self, diagnostic: &mut Diag) {
133 let Some(fatal_emitter) = self.fatal_emitter.as_deref_mut() else { return };
134 if diagnostic.level != Level::Fatal {
135 return;
136 }
137
138 if let Some(note) = &self.note {
139 let mut diagnostic = diagnostic.clone();
140 diagnostic.note(note.clone());
141 fatal_emitter.emit_diagnostic(&mut diagnostic);
142 } else {
143 fatal_emitter.emit_diagnostic(diagnostic);
144 }
145 }
146}
147
148#[derive(Clone, Debug)]
150pub struct LocalEmitter {
151 diagnostics: Vec<Diag>,
152}
153
154impl Default for LocalEmitter {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl LocalEmitter {
161 pub fn new() -> Self {
163 Self { diagnostics: Vec::new() }
164 }
165
166 pub fn diagnostics(&self) -> &[Diag] {
168 &self.diagnostics
169 }
170
171 pub fn into_diagnostics(self) -> Vec<Diag> {
173 self.diagnostics
174 }
175}
176
177impl Emitter for LocalEmitter {
178 fn emit_diagnostic(&mut self, diagnostic: &mut Diag) {
179 self.diagnostics.push(diagnostic.clone());
180 }
181}
182
183#[cold]
184#[inline(never)]
185fn io_panic(error: std::io::Error) -> ! {
186 panic!("failed to emit diagnostic: {error}");
187}