use crate::config::Config;
use crate::diagnostics::{self, Diagnostic, DiagnosticBuilder};
use crate::cli::Severity;
use crate::lints::extract_tag_from_with_tag;
use crate::parser::span_to_location;
use std::path::Path;
use syn::visit::Visit;
use syn::spanned::Spanned;
pub fn check(ast: &syn::File, path: &Path, config: &Config) -> Vec<Diagnostic> {
let mut visitor = ArchitectureVisitor::new(path, config);
visitor.visit_file(ast);
visitor.diagnostics
}
struct ArchitectureVisitor<'a> {
path: &'a Path,
config: &'a Config,
diagnostics: Vec<Diagnostic>,
current_module: Option<String>,
}
impl<'a> ArchitectureVisitor<'a> {
fn new(path: &'a Path, config: &'a Config) -> Self {
let current_module = path
.file_stem()
.and_then(|s| s.to_str())
.map(|s| s.to_string());
Self {
path,
config,
diagnostics: Vec::new(),
current_module,
}
}
fn check_tag(&mut self, tag: &str, span: proc_macro2::Span) {
if self.config.tags.warn_unknown_tags
&& !self.config.tags.known_tags.contains(&tag.to_string())
&& self.config.is_lint_enabled("FA802")
{
self.diagnostics.push(diagnostics::fa802(
span_to_location(span, self.path),
tag,
));
}
if let Some(ref module) = self.current_module {
if let Some(expected_tags) = self.config.tags.module_tags.get(module) {
if !expected_tags.contains(&tag.to_string())
&& self.config.is_lint_enabled("FA801")
{
self.diagnostics.push(diagnostics::fa801(
span_to_location(span, self.path),
&expected_tags.join(", "),
tag,
module,
));
}
}
}
}
}
impl<'a> Visit<'a> for ArchitectureVisitor<'a> {
fn visit_item_mod(&mut self, module: &'a syn::ItemMod) {
let prev_module = self.current_module.take();
self.current_module = Some(module.ident.to_string());
syn::visit::visit_item_mod(self, module);
self.current_module = prev_module;
}
fn visit_expr(&mut self, expr: &'a syn::Expr) {
if let syn::Expr::MethodCall(call) = expr {
if let Some(tag) = extract_tag_from_with_tag(call) {
self.check_tag(&tag, call.span());
}
}
syn::visit::visit_expr(self, expr);
}
}