Skip to main content

cha_core/plugins/
speculative_generality.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect interfaces/traits with only one implementation (over-abstraction).
4pub struct SpeculativeGeneralityAnalyzer;
5
6impl Default for SpeculativeGeneralityAnalyzer {
7    fn default() -> Self {
8        Self
9    }
10}
11
12impl Plugin for SpeculativeGeneralityAnalyzer {
13    fn name(&self) -> &str {
14        "speculative_generality"
15    }
16
17    fn description(&self) -> &str {
18        "Interface with too few implementations"
19    }
20
21    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
22        // Count how many classes extend/implement each interface in this file
23        let interfaces: Vec<_> = ctx
24            .model
25            .classes
26            .iter()
27            .filter(|c| c.is_interface)
28            .collect();
29        if interfaces.is_empty() {
30            return vec![];
31        }
32        let mut findings = Vec::new();
33        for iface in &interfaces {
34            let impl_count = ctx
35                .model
36                .classes
37                .iter()
38                .filter(|c| c.parent_name.as_deref() == Some(&iface.name))
39                .count();
40            // Only flag if exactly 0 or 1 implementations in the same file
41            if impl_count <= 1 {
42                findings.push(Finding {
43                    smell_name: "speculative_generality".into(),
44                    category: SmellCategory::Dispensables,
45                    severity: Severity::Hint,
46                    location: Location {
47                        path: ctx.file.path.clone(),
48                        start_line: iface.start_line,
49                        end_line: iface.end_line,
50                        name: Some(iface.name.clone()),
51                    },
52                    message: format!(
53                        "Interface `{}` has only {} implementation(s) in this file, consider Collapse Hierarchy",
54                        iface.name, impl_count
55                    ),
56                    suggested_refactorings: vec![
57                        "Collapse Hierarchy".into(),
58                        "Inline Class".into(),
59                    ],
60                    ..Default::default()
61                });
62            }
63        }
64        findings
65    }
66}