use super::{
file_forbids_unsafe, has_unsafe_attributes, is_test_fn, is_test_mod,
IncludeTests, RsFileMetrics,
};
use syn::{
visit, Expr, ExprUnsafe, ImplItemFn, ItemFn, ItemImpl, ItemMod, ItemTrait,
};
pub struct GeigerSynVisitor {
include_tests: IncludeTests,
pub metrics: RsFileMetrics,
unsafe_scopes: u32,
}
impl GeigerSynVisitor {
pub fn new(include_tests: IncludeTests) -> Self {
GeigerSynVisitor {
include_tests,
metrics: Default::default(),
unsafe_scopes: 0,
}
}
pub fn enter_unsafe_scope(&mut self) {
self.unsafe_scopes += 1;
}
pub fn exit_unsafe_scope(&mut self) {
self.unsafe_scopes -= 1;
}
}
impl<'ast> visit::Visit<'ast> for GeigerSynVisitor {
fn visit_file(&mut self, i: &'ast syn::File) {
self.metrics.forbids_unsafe = file_forbids_unsafe(i);
visit::visit_file(self, i);
}
fn visit_item_fn(&mut self, item_fn: &ItemFn) {
if IncludeTests::No == self.include_tests && is_test_fn(item_fn) {
return;
}
let unsafe_fn =
item_fn.sig.unsafety.is_some() || has_unsafe_attributes(item_fn);
if unsafe_fn {
self.enter_unsafe_scope()
}
self.metrics.counters.functions.count(unsafe_fn);
visit::visit_item_fn(self, item_fn);
if item_fn.sig.unsafety.is_some() {
self.exit_unsafe_scope()
}
}
fn visit_expr(&mut self, i: &Expr) {
match i {
Expr::Lit(..) | Expr::Path(..) | Expr::Unsafe(..) => {
}
_ => {
self.metrics.counters.exprs.count(self.unsafe_scopes > 0);
}
}
visit::visit_expr(self, i);
}
fn visit_expr_unsafe(&mut self, i: &ExprUnsafe) {
self.enter_unsafe_scope();
visit::visit_expr_unsafe(self, i);
self.exit_unsafe_scope();
}
fn visit_item_mod(&mut self, i: &ItemMod) {
if IncludeTests::No == self.include_tests && is_test_mod(i) {
return;
}
visit::visit_item_mod(self, i);
}
fn visit_item_impl(&mut self, i: &ItemImpl) {
self.metrics.counters.item_impls.count(i.unsafety.is_some());
visit::visit_item_impl(self, i);
}
fn visit_item_trait(&mut self, i: &ItemTrait) {
self.metrics
.counters
.item_traits
.count(i.unsafety.is_some());
visit::visit_item_trait(self, i);
}
fn visit_impl_item_fn(&mut self, i: &ImplItemFn) {
if i.sig.unsafety.is_some() {
self.enter_unsafe_scope()
}
self.metrics
.counters
.methods
.count(i.sig.unsafety.is_some());
visit::visit_impl_item_fn(self, i);
if i.sig.unsafety.is_some() {
self.exit_unsafe_scope()
}
}
}