solar_interface/diagnostics/
builder.rsuse super::{
BugAbort, DiagCtxt, Diagnostic, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, ExplicitBug,
FatalAbort, Level, MultiSpan, Style,
};
use crate::Span;
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 DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
}
impl EmissionGuarantee for ErrorGuaranteed {
type EmitResult = Self;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
let guar = db.emit_producing_error_guaranteed();
assert!(
db.diagnostic.is_error(),
"emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`",
db.diagnostic.level,
);
guar.unwrap_err()
}
}
impl EmissionGuarantee for () {
type EmitResult = Self;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
}
}
impl EmissionGuarantee for BugAbort {
type EmitResult = Never;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, 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 DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
std::panic::panic_any(Self);
}
}
#[must_use = "diagnostics must be emitted or cancelled"]
pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
dcx: &'a DiagCtxt,
diagnostic: Box<Diagnostic>,
_marker: PhantomData<G>,
}
impl<G: EmissionGuarantee> Clone for DiagnosticBuilder<'_, G> {
#[inline]
fn clone(&self) -> Self {
Self { dcx: self.dcx, diagnostic: self.diagnostic.clone(), _marker: PhantomData }
}
}
impl<G: EmissionGuarantee> fmt::Debug for DiagnosticBuilder<'_, G> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.diagnostic.fmt(f)
}
}
impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
type Target = Diagnostic;
#[inline]
fn deref(&self) -> &Self::Target {
&self.diagnostic
}
}
impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.diagnostic
}
}
impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
#[track_caller]
fn drop(&mut self) {
if std::thread::panicking() {
return;
}
let _ = self.dcx.emit_diagnostic(Diagnostic::new(
Level::Bug,
"the following error was constructed but not emitted",
));
let _ = self.dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
panic!("error was constructed but not emitted");
}
}
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
#[track_caller]
pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, msg: M) -> Self {
Self { dcx, diagnostic: Box::new(Diagnostic::new(level, msg)), _marker: PhantomData }
}
#[inline]
pub fn dcx(&self) -> &DiagCtxt {
self.dcx
}
#[track_caller]
pub fn emit(mut self) -> G::EmitResult {
if self.dcx.track_diagnostics() {
self.diagnostic.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> {
self.dcx.emit_diagnostic_without_consuming(&mut self.diagnostic)
}
#[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 [`Diagnostic::", stringify!($n), "()`].")]
$vis fn $n(mut self, $($name: $ty),*) -> Self {
self.diagnostic.$n($($name),*);
self
}
)*
};
}
impl<G: EmissionGuarantee> DiagnosticBuilder<'_, G> {
forward! {
pub fn span(span: impl Into<MultiSpan>);
pub fn code(code: impl Into<DiagnosticId>);
pub fn span_label(span: Span, label: impl Into<DiagnosticMessage>);
pub fn span_labels(spans: impl IntoIterator<Item = Span>, label: impl Into<DiagnosticMessage>);
pub fn warn(msg: impl Into<DiagnosticMessage>);
pub fn span_warn(span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>);
pub fn note(msg: impl Into<DiagnosticMessage>);
pub fn span_note(span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>);
pub fn highlighted_note(messages: Vec<(impl Into<DiagnosticMessage>, Style)>);
pub fn note_once(msg: impl Into<DiagnosticMessage>);
pub fn span_note_once(span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>);
pub fn help(msg: impl Into<DiagnosticMessage>);
pub fn help_once(msg: impl Into<DiagnosticMessage>);
pub fn highlighted_help(messages: Vec<(impl Into<DiagnosticMessage>, Style)>);
pub fn span_help(span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>);
}
}