Archived
77 lines
2.2 KiB
Rust
77 lines
2.2 KiB
Rust
//! Core linter — orchestrates arch rules, style rules, and format check.
|
|
|
|
use el_arch::{ArchChecker, Severity as ArchSeverity};
|
|
|
|
use crate::{
|
|
error::LintError,
|
|
report::{LintDiagnostic, LintReport, LintSeverity},
|
|
rules,
|
|
};
|
|
|
|
pub struct Linter {
|
|
arch_checker: ArchChecker,
|
|
}
|
|
|
|
impl Linter {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
arch_checker: ArchChecker::new(),
|
|
}
|
|
}
|
|
|
|
pub fn lint(&self, source: &str) -> Result<LintReport, LintError> {
|
|
let tokens = el_lexer::tokenize(source)
|
|
.map_err(|e| LintError::Lex(e.to_string()))?;
|
|
let program = el_parser::parse(tokens, source.to_string())
|
|
.map_err(|e| LintError::Parse(e.to_string()))?;
|
|
|
|
let mut diagnostics = Vec::new();
|
|
|
|
// 1. Run el-arch architectural rules.
|
|
let arch_diags = self.arch_checker.check(&program);
|
|
for d in arch_diags {
|
|
diagnostics.push(LintDiagnostic {
|
|
severity: match d.severity {
|
|
ArchSeverity::Error => LintSeverity::Error,
|
|
ArchSeverity::Warning => LintSeverity::Warning,
|
|
},
|
|
code: d.rule,
|
|
message: d.message,
|
|
location: d.location.unwrap_or_else(|| "unknown".into()),
|
|
suggestion: None,
|
|
});
|
|
}
|
|
|
|
// 2. Run style rules.
|
|
let style_diags = rules::check_style(&program);
|
|
diagnostics.extend(style_diags);
|
|
|
|
// 3. Check whether the source is in canonical format.
|
|
match el_fmt::is_canonical(source) {
|
|
Ok(false) => {
|
|
diagnostics.push(LintDiagnostic {
|
|
severity: LintSeverity::Info,
|
|
code: "I001".into(),
|
|
message: "source is not in canonical format — run `el fmt` to fix".into(),
|
|
location: "file".into(),
|
|
suggestion: Some("el fmt --in-place <file.el>".into()),
|
|
});
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let source_lines = source.lines().count();
|
|
Ok(LintReport {
|
|
diagnostics,
|
|
file_path: None,
|
|
source_lines,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Default for Linter {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|