use crate::adapters::analyzers::architecture::{MatchLocation, ViolationKind};
use syn::spanned::Spanned;
use syn::visit::{self, Visit};
pub fn find_function_call_matches(
file: &str,
ast: &syn::File,
paths: &[String],
) -> Vec<MatchLocation> {
let mut visitor = FunctionCallVisitor {
file,
paths,
hits: Vec::new(),
};
visitor.visit_file(ast);
visitor.hits
}
struct FunctionCallVisitor<'a> {
file: &'a str,
paths: &'a [String],
hits: Vec<MatchLocation>,
}
impl FunctionCallVisitor<'_> {
fn record(&mut self, rendered: &str, span: proc_macro2::Span) {
let start = span.start();
self.hits.push(MatchLocation {
file: self.file.to_string(),
line: start.line,
column: start.column,
kind: ViolationKind::FunctionCall {
rendered_path: rendered.to_string(),
},
});
}
}
impl<'ast> Visit<'ast> for FunctionCallVisitor<'_> {
fn visit_expr_call(&mut self, node: &'ast syn::ExprCall) {
if let syn::Expr::Path(ep) = &*node.func {
let rendered = render_path(&ep.path);
if self.paths.iter().any(|p| p == &rendered) {
self.record(&rendered, ep.path.span());
}
}
visit::visit_expr_call(self, node);
}
fn visit_macro(&mut self, node: &'ast syn::Macro) {
use syn::punctuated::Punctuated;
if let Ok(args) = syn::parse::Parser::parse2(
Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated,
node.tokens.clone(),
) {
args.iter().for_each(|expr| visit::visit_expr(self, expr));
}
visit::visit_macro(self, node);
}
}
use super::render_path;