use std::fmt;
use std::fmt::Display;
use std::path::Path;
use dupe::Dupe;
use serde::Serialize;
use crate::codemap::CodeMap;
use crate::codemap::FileSpan;
use crate::codemap::ResolvedSpan;
use crate::codemap::Span;
pub(crate) trait LintWarning: Display {
fn severity(&self) -> EvalSeverity;
fn short_name(&self) -> &'static str;
}
#[derive(Debug)]
pub(crate) struct LintT<T> {
pub location: FileSpan,
pub original: String,
pub problem: T,
}
#[derive(Debug)]
pub struct Lint {
pub location: FileSpan,
pub short_name: String,
pub severity: EvalSeverity,
pub problem: String,
pub original: String,
}
impl Display for Lint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.location, self.problem)
}
}
impl<T: Display> Display for LintT<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.location, self.problem)
}
}
impl<T: LintWarning> LintT<T> {
pub(crate) fn new(codemap: &CodeMap, span: Span, problem: T) -> Self {
let location = codemap.file_span(span);
Self {
original: location.file.source_span(span).to_owned(),
location,
problem,
}
}
pub(crate) fn erase(self) -> Lint {
Lint {
location: self.location,
short_name: self.problem.short_name().to_owned(),
severity: self.problem.severity(),
problem: self.problem.to_string(),
original: self.original,
}
}
}
#[derive(Debug, Serialize, Dupe, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum EvalSeverity {
Error,
Warning,
Advice,
Disabled,
}
impl Display for EvalSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
EvalSeverity::Error => "Error",
EvalSeverity::Warning => "Warning",
EvalSeverity::Advice => "Advice",
EvalSeverity::Disabled => "Disabled",
})
}
}
#[derive(Debug, Clone)]
pub struct EvalMessage {
pub path: String,
pub span: Option<ResolvedSpan>,
pub severity: EvalSeverity,
pub name: String,
pub description: String,
pub full_error_with_span: Option<String>,
pub original: Option<String>,
}
impl Display for EvalMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}:", self.severity, self.path)?;
if let Some(span) = self.span {
write!(f, "{}", span)?;
}
write!(f, " {}", self.description)
}
}
impl EvalMessage {
pub fn from_error(file: &Path, err: &crate::Error) -> Self {
if let Some(span) = err.span() {
return Self::from_diagnostic(span, err.without_diagnostic(), err);
}
Self::from_any_error(file, err)
}
pub fn from_any_error(file: &Path, x: &impl std::fmt::Display) -> Self {
Self {
path: file.display().to_string(),
span: None,
severity: EvalSeverity::Error,
name: "error".to_owned(),
description: format!("{:#}", x),
full_error_with_span: None,
original: None,
}
}
fn from_diagnostic(
span: &FileSpan,
message: impl std::fmt::Display,
full_error: impl std::fmt::Display,
) -> Self {
let original = span.source_span().to_owned();
let resolved_span = span.resolve_span();
Self {
path: span.filename().to_owned(),
span: Some(resolved_span),
severity: EvalSeverity::Error,
name: "error".to_owned(),
description: format!("{:#}", message),
full_error_with_span: Some(full_error.to_string()),
original: Some(original),
}
}
}
impl From<Lint> for EvalMessage {
fn from(x: Lint) -> Self {
Self {
path: x.location.filename().to_owned(),
span: Some(x.location.resolve_span()),
severity: x.severity,
name: x.short_name,
description: x.problem,
full_error_with_span: None,
original: Some(x.original),
}
}
}