Skip to main content

cha_core/plugins/
data_clumps.rs

1use std::collections::HashMap;
2
3use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
4
5/// Detect groups of parameters that repeatedly appear together across functions.
6pub struct DataClumpsAnalyzer {
7    pub min_clump_size: usize,
8    pub min_occurrences: usize,
9}
10
11impl Default for DataClumpsAnalyzer {
12    fn default() -> Self {
13        Self {
14            min_clump_size: 3,
15            min_occurrences: 3,
16        }
17    }
18}
19
20impl Plugin for DataClumpsAnalyzer {
21    fn name(&self) -> &str {
22        "data_clumps"
23    }
24
25    fn description(&self) -> &str {
26        "Repeated parameter type signatures"
27    }
28
29    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
30        // Build sorted param-type signatures per function
31        let sigs: Vec<_> = ctx
32            .model
33            .functions
34            .iter()
35            .filter(|f| f.parameter_types.len() >= self.min_clump_size)
36            .map(|f| (f, f.parameter_types.join(",")))
37            .collect();
38
39        // Count how many functions share the same type signature
40        let mut sig_counts: HashMap<&str, usize> = HashMap::new();
41        for (_, sig) in &sigs {
42            *sig_counts.entry(sig.as_str()).or_default() += 1;
43        }
44
45        sigs.iter()
46            .filter(|(_, sig)| {
47                sig_counts.get(sig.as_str()).copied().unwrap_or(0) >= self.min_occurrences
48            })
49            .map(|(f, sig)| Finding {
50                smell_name: "data_clumps".into(),
51                category: SmellCategory::Bloaters,
52                severity: Severity::Hint,
53                location: Location {
54                    path: ctx.file.path.clone(),
55                    start_line: f.start_line,
56                    end_line: f.end_line,
57                    name: Some(f.name.clone()),
58                },
59                message: format!(
60                    "Function `{}` shares parameter signature [{}] with {} other functions",
61                    f.name,
62                    sig,
63                    sig_counts[sig.as_str()] - 1
64                ),
65                suggested_refactorings: vec![
66                    "Extract Class".into(),
67                    "Introduce Parameter Object".into(),
68                ],
69                ..Default::default()
70            })
71            .collect()
72    }
73}