use std::fmt::{self, Display};
use gazebo::variants::VariantName;
use crate::codemap::{CodeMap, FileSpan, Span};
pub(crate) trait LintWarning: Display + VariantName {
fn is_serious(&self) -> bool;
}
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 serious: bool,
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: kebab(self.problem.variant_name()),
serious: self.problem.is_serious(),
problem: self.problem.to_string(),
original: self.original,
}
}
}
fn kebab(xs: &str) -> String {
let mut res = String::new();
for x in xs.chars() {
if x.is_uppercase() {
if !res.is_empty() {
res.push('-');
}
for y in x.to_lowercase() {
res.push(y);
}
} else {
res.push(x);
}
}
res
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lint_kebab() {
assert_eq!(kebab("Unreachable"), "unreachable");
assert_eq!(kebab("UsingIgnored"), "using-ignored");
assert_eq!(
kebab("MissingReturnExpression"),
"missing-return-expression"
);
assert_eq!(
kebab("DuplicateTopLevelAssign"),
"duplicate-top-level-assign"
);
}
}