use crate::diagnostic::IntoDiagnosticArg;
use crate::{DiagCtxt, Level, MultiSpan, StashKey};
use crate::{
Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed,
ExplicitBug, SubdiagnosticMessage,
};
use rustc_lint_defs::Applicability;
use rustc_span::source_map::Spanned;
use rustc_span::Span;
use std::borrow::Cow;
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::panic;
use std::thread::panicking;
#[rustc_diagnostic_item = "IntoDiagnostic"]
pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
#[must_use]
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>;
}
impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
where
T: IntoDiagnostic<'a, G>,
G: EmissionGuarantee,
{
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
self.node.into_diagnostic(dcx, level).span_mv(self.span)
}
}
#[must_use]
pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
pub dcx: &'a DiagCtxt,
diag: Option<Box<Diagnostic>>,
_marker: PhantomData<G>,
}
impl<G> !Clone for DiagnosticBuilder<'_, G> {}
rustc_data_structures::static_assert_size!(
DiagnosticBuilder<'_, ()>,
2 * std::mem::size_of::<usize>()
);
pub trait EmissionGuarantee: Sized {
type EmitResult = Self;
#[track_caller]
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
}
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
fn take_diag(&mut self) -> Diagnostic {
Box::into_inner(self.diag.take().unwrap())
}
fn emit_producing_nothing(mut self) {
let diag = self.take_diag();
self.dcx.emit_diagnostic(diag);
}
fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
let diag = self.take_diag();
assert!(
diag.is_error(),
"emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`",
diag.level,
);
let guar = self.dcx.emit_diagnostic(diag);
guar.unwrap()
}
}
impl EmissionGuarantee for ErrorGuaranteed {
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_error_guaranteed()
}
}
impl EmissionGuarantee for () {
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
}
}
#[derive(Copy, Clone)]
pub struct BugAbort;
impl EmissionGuarantee for BugAbort {
type EmitResult = !;
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
panic::panic_any(ExplicitBug);
}
}
#[derive(Copy, Clone)]
pub struct FatalAbort;
impl EmissionGuarantee for FatalAbort {
type EmitResult = !;
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
crate::FatalError.raise()
}
}
impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
db.emit_producing_nothing();
rustc_span::fatal_error::FatalError
}
}
macro_rules! forward {
(
($n:ident, $n_mv:ident)($($name:ident: $ty:ty),* $(,)?)
) => {
#[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
self.diag.as_mut().unwrap().$n($($name),*);
self
}
#[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
pub fn $n_mv(mut self, $($name: $ty),*) -> Self {
self.diag.as_mut().unwrap().$n($($name),*);
self
}
};
}
impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
type Target = Diagnostic;
fn deref(&self) -> &Diagnostic {
self.diag.as_ref().unwrap()
}
}
impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
fn deref_mut(&mut self) -> &mut Diagnostic {
self.diag.as_mut().unwrap()
}
}
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
#[rustc_lint_diagnostics]
#[track_caller]
pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self {
Self::new_diagnostic(dcx, Diagnostic::new(level, message))
}
#[track_caller]
pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self {
debug!("Created new diagnostic");
Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
}
#[track_caller]
pub fn emit(self) -> G::EmitResult {
G::emit_producing_guarantee(self)
}
#[track_caller]
pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
if delay {
self.downgrade_to_delayed_bug();
}
self.emit()
}
pub fn cancel(mut self) {
self.diag = None;
drop(self);
}
pub fn stash(self, span: Span, key: StashKey) {
if let Some((diag, dcx)) = self.into_diagnostic() {
dcx.stash_diagnostic(span, key, diag);
}
}
pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() {
self.emit();
return None;
}
let diag = self.take_diag();
debug!("buffer: diag={:?}", diag);
Some((diag, self.dcx))
}
pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
}
#[track_caller]
pub fn delay_as_bug(mut self) -> G::EmitResult {
self.downgrade_to_delayed_bug();
self.emit()
}
forward!((span_label, span_label_mv)(
span: Span,
label: impl Into<SubdiagnosticMessage>,
));
forward!((span_labels, span_labels_mv)(
spans: impl IntoIterator<Item = Span>,
label: &str,
));
forward!((note_expected_found, note_expected_found_mv)(
expected_label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found_label: &dyn fmt::Display,
found: DiagnosticStyledString,
));
forward!((note_expected_found_extra, note_expected_found_extra_mv)(
expected_label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found_label: &dyn fmt::Display,
found: DiagnosticStyledString,
expected_extra: &dyn fmt::Display,
found_extra: &dyn fmt::Display,
));
forward!((note, note_mv)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((note_once, note_once_mv)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_note, span_note_mv)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_note_once, span_note_once_mv)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((warn, warn_mv)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_warn, span_warn_mv)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((help, help_mv)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((help_once, help_once_mv)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_help, span_help_once_mv)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((multipart_suggestion, multipart_suggestion_mv)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((multipart_suggestion_verbose, multipart_suggestion_verbose_mv)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((tool_only_multipart_suggestion, tool_only_multipart_suggestion_mv)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((span_suggestion, span_suggestion_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestions, span_suggestions_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestions: impl IntoIterator<Item = String>,
applicability: Applicability,
));
forward!((multipart_suggestions, multipart_suggestions_mv)(
msg: impl Into<SubdiagnosticMessage>,
suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
));
forward!((span_suggestion_short, span_suggestion_short_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestion_verbose, span_suggestion_verbose_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestion_hidden, span_suggestion_hidden_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((tool_only_span_suggestion, tool_only_span_suggestion_mv)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((primary_message, primary_message_mv)(
msg: impl Into<DiagnosticMessage>,
));
forward!((span, span_mv)(
sp: impl Into<MultiSpan>,
));
forward!((code, code_mv)(
s: DiagnosticId,
));
forward!((arg, arg_mv)(
name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
));
forward!((subdiagnostic, subdiagnostic_mv)(
subdiagnostic: impl crate::AddToDiagnostic,
));
}
impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.diag.fmt(f)
}
}
impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
fn drop(&mut self) {
match self.diag.take() {
Some(diag) if !panicking() => {
self.dcx.emit_diagnostic(Diagnostic::new(
Level::Bug,
DiagnosticMessage::from("the following error was constructed but not emitted"),
));
self.dcx.emit_diagnostic(*diag);
panic!("error was constructed but not emitted");
}
_ => {}
}
}
}
#[macro_export]
macro_rules! struct_span_err {
($dcx:expr, $span:expr, $code:ident, $($message:tt)*) => ({
$dcx.struct_span_err(
$span,
format!($($message)*),
)
.code_mv($crate::error_code!($code))
})
}
#[macro_export]
macro_rules! error_code {
($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }};
}