use super::{Diag, Level, MultiSpan, SuggestionStyle};
use crate::{SourceMap, diagnostics::Suggestions};
use std::{any::Any, borrow::Cow, sync::Arc};
mod human;
pub use human::{HumanBufferEmitter, HumanEmitter};
#[cfg(feature = "json")]
mod json;
#[cfg(feature = "json")]
pub use json::JsonEmitter;
mod mem;
pub use mem::InMemoryEmitter;
mod rustc;
pub type DynEmitter = dyn Emitter + Send;
pub trait Emitter: Any {
fn emit_diagnostic(&mut self, diagnostic: &mut Diag);
#[inline]
fn source_map(&self) -> Option<&Arc<SourceMap>> {
None
}
#[inline]
fn supports_color(&self) -> bool {
false
}
fn primary_span_formatted<'a>(
&self,
primary_span: &mut Cow<'a, MultiSpan>,
suggestions: &mut Suggestions,
) {
if let Some((sugg, rest)) = &suggestions.split_first()
&& rest.is_empty()
&& let [substitution] = sugg.substitutions.as_slice()
&& let [part] = substitution.parts.as_slice()
&& sugg.msg.as_str().split_whitespace().count() < 10
&& !part.snippet.contains('\n')
&& ![
SuggestionStyle::HideCodeAlways,
SuggestionStyle::CompletelyHidden,
SuggestionStyle::ShowAlways,
].contains(&sugg.style)
{
let snippet = part.snippet.trim();
let msg = if snippet.is_empty() || sugg.style.hide_inline() {
format!("help: {}", sugg.msg.as_str())
} else {
format!("help: {}: `{}`", sugg.msg.as_str(), snippet)
};
primary_span.to_mut().push_span_label(part.span, msg);
*suggestions = Suggestions::Disabled;
} else {
}
}
}
impl DynEmitter {
pub(crate) fn local_buffer(&self) -> Option<&str> {
(self as &dyn Any).downcast_ref::<HumanBufferEmitter>().map(HumanBufferEmitter::buffer)
}
}
pub struct SilentEmitter {
fatal_emitter: Option<Box<DynEmitter>>,
note: Option<String>,
}
impl SilentEmitter {
pub fn new(fatal_emitter: impl Emitter + Send) -> Self {
Self::new_boxed(Some(Box::new(fatal_emitter)))
}
pub fn new_boxed(fatal_emitter: Option<Box<DynEmitter>>) -> Self {
Self { fatal_emitter, note: None }
}
pub fn new_silent() -> Self {
Self::new_boxed(None)
}
pub fn with_note(mut self, note: Option<String>) -> Self {
self.note = note;
self
}
}
impl Emitter for SilentEmitter {
fn emit_diagnostic(&mut self, diagnostic: &mut Diag) {
let Some(fatal_emitter) = self.fatal_emitter.as_deref_mut() else { return };
if diagnostic.level != Level::Fatal {
return;
}
if let Some(note) = &self.note {
let mut diagnostic = diagnostic.clone();
diagnostic.note(note.clone());
fatal_emitter.emit_diagnostic(&mut diagnostic);
} else {
fatal_emitter.emit_diagnostic(diagnostic);
}
}
}
#[derive(Clone, Debug)]
pub struct LocalEmitter {
diagnostics: Vec<Diag>,
}
impl Default for LocalEmitter {
fn default() -> Self {
Self::new()
}
}
impl LocalEmitter {
pub fn new() -> Self {
Self { diagnostics: Vec::new() }
}
pub fn diagnostics(&self) -> &[Diag] {
&self.diagnostics
}
pub fn into_diagnostics(self) -> Vec<Diag> {
self.diagnostics
}
}
impl Emitter for LocalEmitter {
fn emit_diagnostic(&mut self, diagnostic: &mut Diag) {
self.diagnostics.push(diagnostic.clone());
}
}
#[cold]
#[inline(never)]
fn io_panic(error: std::io::Error) -> ! {
panic!("failed to emit diagnostic: {error}");
}