use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use alloc::borrow::Cow;
use rustc_macros::{Decodable, Encodable, StableHash};
use crate::rustc_span::Span;
mod diagnostic_impls;
pub use diagnostic_impls::DiagArgFromDisplay;
use crate::rustc_data_structures::fx::FxIndexMap;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)]
pub enum DiagMessage {
Str(Cow<'static, str>),
Inline(Cow<'static, str>),
}
impl DiagMessage {
pub fn as_str(&self) -> Option<&str> {
match self {
DiagMessage::Str(s) => Some(s),
DiagMessage::Inline(_) => None,
}
}
}
impl From<String> for DiagMessage {
fn from(s: String) -> Self {
DiagMessage::Str(Cow::Owned(s))
}
}
impl From<&'static str> for DiagMessage {
fn from(s: &'static str) -> Self {
DiagMessage::Str(Cow::Borrowed(s))
}
}
impl From<Cow<'static, str>> for DiagMessage {
fn from(s: Cow<'static, str>) -> Self {
DiagMessage::Str(s)
}
}
#[derive(Clone, Debug)]
pub struct SpanLabel {
pub span: Span,
pub is_primary: bool,
pub label: Option<DiagMessage>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable, StableHash)]
pub struct MultiSpan {
primary_spans: Vec<Span>,
span_labels: Vec<(Span, DiagMessage)>,
}
impl MultiSpan {
#[inline]
pub fn new() -> MultiSpan {
MultiSpan { primary_spans: vec![], span_labels: vec![] }
}
pub fn from_span(primary_span: Span) -> MultiSpan {
MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] }
}
pub fn from_spans(mut vec: Vec<Span>) -> MultiSpan {
vec.sort();
MultiSpan { primary_spans: vec, span_labels: vec![] }
}
pub fn push_primary_span(&mut self, primary_span: Span) {
self.primary_spans.push(primary_span);
}
pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagMessage>) {
self.span_labels.push((span, label.into()));
}
pub fn push_span_diag(&mut self, span: Span, diag: DiagMessage) {
self.span_labels.push((span, diag));
}
pub fn primary_span(&self) -> Option<Span> {
self.primary_spans.first().cloned()
}
pub fn primary_spans(&self) -> &[Span] {
&self.primary_spans
}
pub fn has_primary_spans(&self) -> bool {
!self.is_dummy()
}
pub fn is_dummy(&self) -> bool {
self.primary_spans.iter().all(|sp| sp.is_dummy())
}
pub fn replace(&mut self, before: Span, after: Span) -> bool {
let mut replacements_occurred = false;
for primary_span in &mut self.primary_spans {
if *primary_span == before {
*primary_span = after;
replacements_occurred = true;
}
}
for span_label in &mut self.span_labels {
if span_label.0 == before {
span_label.0 = after;
replacements_occurred = true;
}
}
replacements_occurred
}
pub fn span_labels(&self) -> Vec<SpanLabel> {
let is_primary = |span| self.primary_spans.contains(&span);
let mut span_labels = self
.span_labels
.iter()
.map(|&(span, ref label)| SpanLabel {
span,
is_primary: is_primary(span),
label: Some(label.clone()),
})
.collect::<Vec<_>>();
for &span in &self.primary_spans {
if !span_labels.iter().any(|sl| sl.span == span) {
span_labels.push(SpanLabel { span, is_primary: true, label: None });
}
}
span_labels
}
pub fn span_labels_raw(&self) -> &[(Span, DiagMessage)] {
&self.span_labels
}
pub fn has_span_labels(&self) -> bool {
self.span_labels.iter().any(|(sp, _)| !sp.is_dummy())
}
pub fn clone_ignoring_labels(&self) -> Self {
Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() }
}
}
impl From<Span> for MultiSpan {
fn from(span: Span) -> MultiSpan {
MultiSpan::from_span(span)
}
}
impl From<Vec<Span>> for MultiSpan {
fn from(spans: Vec<Span>) -> MultiSpan {
MultiSpan::from_spans(spans)
}
}
pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue);
pub type DiagArgName = Cow<'static, str>;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagArgValue {
Str(Cow<'static, str>),
Number(i32),
StrListSepByAnd(Vec<Cow<'static, str>>),
}
pub type DiagArgMap = FxIndexMap<DiagArgName, DiagArgValue>;
pub trait IntoDiagArg {
fn into_diag_arg(self, path: &mut LongTyPath) -> DiagArgValue;
}
pub type LongTyPath = Option<String>;
pub use alloc::string::ToString;
impl IntoDiagArg for DiagArgValue {
fn into_diag_arg(self, _: &mut LongTyPath) -> DiagArgValue {
self
}
}
pub fn render_message(template: &str, args: &DiagArgMap) -> Cow<'static, str> {
let pattern = frontend_diag_template::parse(template).unwrap_or_else(|err| {
panic!(
"diagnostic message failed to parse at byte {}: {}\nmessage: {template:?}",
err.offset, err.message
)
});
let rendered = frontend_diag_template::render(&pattern, &TemplateArgs(args));
if !rendered.unresolved.is_empty() {
panic!(
"diagnostic message refers to arguments that were not set: {:?}\nmessage: {template:?}",
rendered.unresolved
);
}
Cow::Owned(rendered.text)
}
pub fn try_render_message(template: &str, args: &DiagArgMap) -> Option<String> {
let pattern = frontend_diag_template::parse(template).ok()?;
let rendered = frontend_diag_template::render(&pattern, &TemplateArgs(args));
if rendered.unresolved.is_empty() { Some(rendered.text) } else { None }
}
struct TemplateArgs<'a>(&'a DiagArgMap);
impl frontend_diag_template::Args for TemplateArgs<'_> {
fn get(&self, name: &str) -> Option<frontend_diag_template::Value<'_>> {
Some(match self.0.get(name)? {
DiagArgValue::Str(s) => frontend_diag_template::Value::Str(Cow::Borrowed(s)),
DiagArgValue::Number(n) => frontend_diag_template::Value::Number(*n),
DiagArgValue::StrListSepByAnd(list) => {
frontend_diag_template::Value::Str(Cow::Owned(join_with_and(list)))
}
})
}
}
fn join_with_and(list: &[Cow<'static, str>]) -> String {
let mut out = String::new();
for (i, item) in list.iter().enumerate() {
if i > 0 {
if list.len() == 2 {
out.push_str(" and ");
} else if i + 1 == list.len() {
out.push_str(", and ");
} else {
out.push_str(", ");
}
}
out.push_str(item);
}
out
}
pub use crate::into_diag_arg_using_display;