Skip to main content

cha_core/plugins/
cognitive_complexity.rs

1use crate::{AnalysisContext, Finding, Plugin, Severity, SmellCategory, func_location};
2
3/// Detect functions with high cognitive complexity.
4///
5/// Cognitive complexity measures how hard code is to *understand*, unlike
6/// cyclomatic complexity which measures testability. It penalizes nesting
7/// and rewards linear, readable structures like `switch`.
8///
9/// ## References
10///
11/// [1] G. A. Campbell, "Cognitive Complexity: A new way of measuring
12///     understandability," SonarSource, 2017.
13///     https://www.sonarsource.com/resources/white-papers/cognitive-complexity/
14pub struct CognitiveComplexityAnalyzer {
15    pub threshold: usize,
16}
17
18impl Default for CognitiveComplexityAnalyzer {
19    fn default() -> Self {
20        Self { threshold: 15 }
21    }
22}
23
24impl Plugin for CognitiveComplexityAnalyzer {
25    fn name(&self) -> &str {
26        "cognitive_complexity"
27    }
28
29    fn smells(&self) -> Vec<String> {
30        vec!["cognitive_complexity".into()]
31    }
32
33    fn description(&self) -> &str {
34        "Cognitive complexity exceeds threshold"
35    }
36
37    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
38        ctx.model
39            .functions
40            .iter()
41            .filter(|f| f.cognitive_complexity > self.threshold)
42            .map(|f| {
43                let severity = if f.cognitive_complexity > self.threshold * 2 {
44                    Severity::Error
45                } else {
46                    Severity::Warning
47                };
48                Finding {
49                    smell_name: "cognitive_complexity".into(),
50                    category: SmellCategory::Bloaters,
51                    severity,
52                    location: func_location(&ctx.file.path, f),
53                    message: format!(
54                        "Function `{}` has cognitive complexity {} (threshold: {})",
55                        f.name, f.cognitive_complexity, self.threshold
56                    ),
57                    suggested_refactorings: vec![
58                        "Extract Method".into(),
59                        "Replace Nested Conditional with Guard Clauses".into(),
60                    ],
61                    actual_value: Some(f.cognitive_complexity as f64),
62                    threshold: Some(self.threshold as f64),
63                }
64            })
65            .collect()
66    }
67}