use super::{
Applicability, BugAbort, Diag, DiagCtxt, DiagId, DiagMsg, ErrorGuaranteed, ExplicitBug,
FatalAbort, Level, MultiSpan, Span, Style, SuggestionStyle,
};
use solar_data_structures::Never;
use std::{
fmt,
marker::PhantomData,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
panic::Location,
};
pub trait EmissionGuarantee: Sized {
type EmitResult;
#[track_caller]
fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult;
}
impl EmissionGuarantee for ErrorGuaranteed {
type EmitResult = Self;
fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
let guar = db.emit_producing_error_guaranteed();
assert!(
db.is_error(),
"emitted non-error ({:?}) diagnostic from `DiagBuilder<ErrorGuaranteed>`",
db.level,
);
guar.unwrap_err()
}
}
impl EmissionGuarantee for () {
type EmitResult = Self;
fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
}
}
impl EmissionGuarantee for BugAbort {
type EmitResult = Never;
fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
std::panic::panic_any(ExplicitBug);
}
}
impl EmissionGuarantee for FatalAbort {
type EmitResult = Never;
fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
std::panic::panic_any(Self);
}
}
#[must_use = "diagnostics must be emitted or cancelled"]
pub struct DiagBuilder<'a, G: EmissionGuarantee> {
diagnostic: Box<(Diag, &'a DiagCtxt)>,
_marker: PhantomData<G>,
}
impl<G: EmissionGuarantee> Clone for DiagBuilder<'_, G> {
#[inline]
fn clone(&self) -> Self {
Self { diagnostic: self.diagnostic.clone(), _marker: PhantomData }
}
}
impl<G: EmissionGuarantee> fmt::Debug for DiagBuilder<'_, G> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.diagnostic.0.fmt(f)
}
}
impl<G: EmissionGuarantee> Deref for DiagBuilder<'_, G> {
type Target = Diag;
#[inline]
fn deref(&self) -> &Self::Target {
&self.diagnostic.0
}
}
impl<G: EmissionGuarantee> DerefMut for DiagBuilder<'_, G> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.diagnostic.0
}
}
impl<G: EmissionGuarantee> Drop for DiagBuilder<'_, G> {
#[track_caller]
fn drop(&mut self) {
if std::thread::panicking() {
return;
}
let (diag, dcx) = &mut *self.diagnostic;
let _ = dcx.emit_diagnostic(Diag::new(
Level::Bug,
"the following error was constructed but not emitted",
));
let _ = dcx.emit_diagnostic_without_consuming(diag);
panic!("error was constructed but not emitted");
}
}
impl<'a, G: EmissionGuarantee> DiagBuilder<'a, G> {
#[inline(never)]
#[track_caller]
pub fn new<M: Into<DiagMsg>>(dcx: &'a DiagCtxt, level: Level, msg: M) -> Self {
Self { diagnostic: Box::new((Diag::new(level, msg), dcx)), _marker: PhantomData }
}
#[inline]
pub fn dcx(&self) -> &DiagCtxt {
self.diagnostic.1
}
#[track_caller]
#[inline(never)]
pub fn emit(mut self) -> G::EmitResult {
if self.dcx().track_diagnostics() {
self.locations_note(Location::caller());
}
self.consume_no_panic(G::emit_producing_guarantee)
}
fn emit_producing_nothing(&mut self) {
let _ = self.emit_producing_error_guaranteed();
}
fn emit_producing_error_guaranteed(&mut self) -> Result<(), ErrorGuaranteed> {
let (diag, dcx) = &mut *self.diagnostic;
dcx.emit_diagnostic_without_consuming(diag)
}
#[inline]
pub fn cancel(self) {
self.consume_no_panic(|_| {});
}
fn consume_no_panic<R>(self, f: impl FnOnce(&mut Self) -> R) -> R {
let mut this = ManuallyDrop::new(self);
let r = f(&mut *this);
unsafe { std::ptr::drop_in_place(&mut this.diagnostic) };
r
}
}
macro_rules! forward {
(
$(
$(#[$attrs:meta])*
$vis:vis fn $n:ident($($name:ident: $ty:ty),* $(,)?);
)*
) => {
$(
$(#[$attrs])*
#[doc = concat!("See [`Diag::", stringify!($n), "()`].")]
#[inline(never)]
$vis fn $n(mut self, $($name: $ty),*) -> Self {
self.diagnostic.0.$n($($name),*);
self
}
)*
};
}
impl<G: EmissionGuarantee> DiagBuilder<'_, G> {
forward! {
pub fn span(span: impl Into<MultiSpan>);
pub fn code(code: impl Into<DiagId>);
pub fn span_label(span: Span, label: impl Into<DiagMsg>);
pub fn span_labels(spans: impl IntoIterator<Item = Span>, label: impl Into<DiagMsg>);
pub fn warn(msg: impl Into<DiagMsg>);
pub fn span_warn(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
pub fn note(msg: impl Into<DiagMsg>);
pub fn span_note(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
pub fn highlighted_note(messages: Vec<(impl Into<DiagMsg>, Style)>);
pub fn note_once(msg: impl Into<DiagMsg>);
pub fn span_note_once(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
pub fn help(msg: impl Into<DiagMsg>);
pub fn help_once(msg: impl Into<DiagMsg>);
pub fn highlighted_help(messages: Vec<(impl Into<DiagMsg>, Style)>);
pub fn span_help(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
pub fn span_suggestion(
span: Span,
msg: impl Into<DiagMsg>,
suggestion: impl Into<DiagMsg>,
applicability: Applicability,
);
pub fn span_suggestion_with_style(
span: Span,
msg: impl Into<DiagMsg>,
suggestion: impl Into<DiagMsg>,
applicability: Applicability,
style: SuggestionStyle
);
pub fn multipart_suggestion(
msg: impl Into<DiagMsg>,
substitutions: Vec<(Span, DiagMsg)>,
applicability: Applicability,
);
pub fn multipart_suggestion_with_style(
msg: impl Into<DiagMsg>,
substitutions: Vec<(Span, DiagMsg)>,
applicability: Applicability,
style: SuggestionStyle
);
}
}