#![cfg_attr(coverage_nightly, coverage(off))]
use syn::visit::Visit;
#[allow(dead_code)]
pub(crate) struct ComplexityVisitor {
pub(crate) complexity: u32,
pub(crate) cognitive: u32,
pub(crate) nesting_level: u32,
pub(crate) function_count: u32,
pub(crate) satd_count: u32,
pub(crate) content: String,
}
impl ComplexityVisitor {
pub(crate) fn new(content: String) -> Self {
let satd_count = Self::count_satd_in_content(&content);
Self {
complexity: 1, cognitive: 0,
nesting_level: 0,
function_count: 0,
satd_count,
content,
}
}
fn count_satd_in_content(content: &str) -> u32 {
let patterns = ["TODO", "FIXME", "HACK", "XXX", "BUG"];
patterns
.iter()
.map(|pattern| content.matches(pattern).count() as u32)
.sum()
}
}
impl<'ast> Visit<'ast> for ComplexityVisitor {
fn visit_item_fn(&mut self, node: &'ast syn::ItemFn) {
self.function_count += 1;
let old_complexity = self.complexity;
let old_cognitive = self.cognitive;
let old_nesting = self.nesting_level;
self.complexity = 1; self.cognitive = 0;
self.nesting_level = 0;
syn::visit::visit_item_fn(self, node);
let fn_complexity = self.complexity;
let fn_cognitive = self.cognitive;
self.complexity = old_complexity + fn_complexity;
self.cognitive = old_cognitive + fn_cognitive;
self.nesting_level = old_nesting;
}
fn visit_expr_if(&mut self, node: &'ast syn::ExprIf) {
self.complexity += 1;
self.cognitive += 1 + self.nesting_level;
self.nesting_level += 1;
syn::visit::visit_expr_if(self, node);
self.nesting_level -= 1;
}
fn visit_expr_while(&mut self, node: &'ast syn::ExprWhile) {
self.complexity += 1;
self.cognitive += 1 + self.nesting_level;
self.nesting_level += 1;
syn::visit::visit_expr_while(self, node);
self.nesting_level -= 1;
}
fn visit_expr_for_loop(&mut self, node: &'ast syn::ExprForLoop) {
self.complexity += 1;
self.cognitive += 1 + self.nesting_level;
self.nesting_level += 1;
syn::visit::visit_expr_for_loop(self, node);
self.nesting_level -= 1;
}
fn visit_expr_loop(&mut self, node: &'ast syn::ExprLoop) {
self.complexity += 1;
self.cognitive += 1 + self.nesting_level;
self.nesting_level += 1;
syn::visit::visit_expr_loop(self, node);
self.nesting_level -= 1;
}
fn visit_expr_match(&mut self, node: &'ast syn::ExprMatch) {
self.complexity += node.arms.len() as u32;
self.cognitive += 1 + self.nesting_level;
self.nesting_level += 1;
syn::visit::visit_expr_match(self, node);
self.nesting_level -= 1;
}
fn visit_expr_binary(&mut self, node: &'ast syn::ExprBinary) {
match node.op {
syn::BinOp::And(_) | syn::BinOp::Or(_) => {
self.complexity += 1;
}
_ => {}
}
syn::visit::visit_expr_binary(self, node);
}
fn visit_arm(&mut self, node: &'ast syn::Arm) {
if self.nesting_level > 0 {
self.cognitive += self.nesting_level;
}
syn::visit::visit_arm(self, node);
}
}