use std::fmt::Debug;
use crate::{
common::{HasNodeId, NodeId},
context::{with_cx, MarkerContext},
ffi::{FfiSlice, FfiStr},
lint::Lint,
prelude::{HasSpan, Span},
};
pub struct DiagnosticBuilder<'ast> {
inner: Option<DiagnosticBuilderInner<'ast>>,
}
struct DiagnosticBuilderInner<'ast> {
lint: &'static Lint,
node: NodeId,
msg: String,
span: Span<'ast>,
parts: Vec<DiagnosticPart<String, Span<'ast>>>,
}
impl<'ast> DiagnosticBuilder<'ast> {
pub(crate) fn dummy() -> Self {
Self { inner: None }
}
pub(crate) fn new(lint: &'static Lint, node: NodeId, msg: String, span: Span<'ast>) -> Self {
Self {
inner: Some(DiagnosticBuilderInner {
lint,
msg,
node,
span,
parts: vec![],
}),
}
}
pub fn span(&mut self, span: impl HasSpan<'ast>) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.span = span.span().clone();
}
self
}
pub fn note(&mut self, msg: impl Into<String>) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.parts.push(DiagnosticPart::Note { msg: msg.into() });
}
self
}
pub fn span_note(&mut self, msg: impl Into<String>, span: impl HasSpan<'ast>) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.parts.push(DiagnosticPart::NoteSpan {
msg: msg.into(),
span: span.span().clone(),
});
}
self
}
pub fn help(&mut self, msg: impl Into<String>) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.parts.push(DiagnosticPart::Help { msg: msg.into() });
}
self
}
pub fn span_help(&mut self, msg: impl Into<String>, span: impl HasSpan<'ast>) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.parts.push(DiagnosticPart::HelpSpan {
msg: msg.into(),
span: span.span().clone(),
});
}
self
}
pub fn span_suggestion(
&mut self,
msg: impl Into<String>,
span: impl HasSpan<'ast>,
suggestion: impl Into<String>,
app: Applicability,
) -> &mut Self {
if let Some(inner) = self.inner.as_mut() {
inner.parts.push(DiagnosticPart::Suggestion {
msg: msg.into(),
span: span.span().clone(),
sugg: suggestion.into(),
app,
});
}
self
}
pub fn decorate<F>(&mut self, decorate: F) -> &mut Self
where
F: FnOnce(&mut DiagnosticBuilder<'ast>),
{
if self.inner.is_some() {
decorate(self);
}
self
}
pub fn done(&self) {}
pub(crate) fn emit<'builder>(&'builder self, cx: &MarkerContext<'ast>) {
if let Some(inner) = &self.inner {
let parts: Vec<_> = inner.parts.iter().map(DiagnosticPart::to_ffi_part).collect();
let diag = Diagnostic {
lint: inner.lint,
msg: inner.msg.as_str().into(),
node: inner.node,
span: &inner.span,
parts: parts.as_slice().into(),
};
cx.emit_diagnostic(&diag);
}
}
}
impl<'ast> Drop for DiagnosticBuilder<'ast> {
fn drop(&mut self) {
with_cx(self, |cx| self.emit(cx));
}
}
pub trait EmissionNode<'ast>: Debug + HasSpan<'ast> + HasNodeId {}
impl<'ast, N: Debug + HasSpan<'ast> + HasNodeId> EmissionNode<'ast> for N {}
#[repr(C)]
#[non_exhaustive]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "driver-api", visibility::make(pub))]
pub(crate) enum DiagnosticPart<St, Sp> {
Help {
msg: St,
},
HelpSpan {
msg: St,
span: Sp,
},
Note {
msg: St,
},
NoteSpan {
msg: St,
span: Sp,
},
Suggestion {
msg: St,
span: Sp,
sugg: St,
app: Applicability,
},
}
impl<'ast> DiagnosticPart<String, Span<'ast>> {
fn to_ffi_part<'part>(&'part self) -> DiagnosticPart<FfiStr<'part>, &'part Span<'ast>> {
match self {
DiagnosticPart::Help { msg } => DiagnosticPart::Help { msg: msg.into() },
DiagnosticPart::HelpSpan { msg, span } => DiagnosticPart::HelpSpan { msg: msg.into(), span },
DiagnosticPart::Note { msg } => DiagnosticPart::Note { msg: msg.into() },
DiagnosticPart::NoteSpan { msg, span } => DiagnosticPart::NoteSpan { msg: msg.into(), span },
DiagnosticPart::Suggestion { msg, span, sugg, app } => DiagnosticPart::Suggestion {
msg: msg.into(),
span,
sugg: sugg.into(),
app: *app,
},
}
}
}
#[repr(C)]
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Applicability {
MachineApplicable,
MaybeIncorrect,
HasPlaceholders,
Unspecified,
}
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "driver-api", visibility::make(pub))]
pub(crate) struct Diagnostic<'builder, 'ast> {
pub lint: &'static Lint,
pub msg: FfiStr<'builder>,
pub node: NodeId,
pub span: &'builder Span<'ast>,
pub parts: FfiSlice<'builder, DiagnosticPart<FfiStr<'builder>, &'builder Span<'ast>>>,
}
impl<'builder, 'ast> Diagnostic<'builder, 'ast> {
pub fn msg(&self) -> &str {
self.msg.get()
}
}