Skip to main content

cha_core/plugins/
message_chain.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect deep method chain calls (e.g. a.b().c().d()).
4pub struct MessageChainAnalyzer {
5    pub max_depth: usize,
6}
7
8impl Default for MessageChainAnalyzer {
9    fn default() -> Self {
10        Self { max_depth: 3 }
11    }
12}
13
14impl Plugin for MessageChainAnalyzer {
15    fn name(&self) -> &str {
16        "message_chain"
17    }
18
19    fn smells(&self) -> Vec<String> {
20        vec!["message_chain".into()]
21    }
22
23    fn description(&self) -> &str {
24        "Deep field access chains (a.b.c.d)"
25    }
26
27    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
28        ctx.model
29            .functions
30            .iter()
31            .filter(|f| f.chain_depth > self.max_depth)
32            .map(|f| Finding {
33                smell_name: "message_chain".into(),
34                category: SmellCategory::Couplers,
35                severity: Severity::Warning,
36                location: Location {
37                    path: ctx.file.path.clone(),
38                    start_line: f.start_line,
39                    start_col: f.name_col,
40                    end_line: f.start_line,
41                    end_col: f.name_end_col,
42                    name: Some(f.name.clone()),
43                },
44                message: format!(
45                    "Function `{}` has chain depth {} (threshold: {})",
46                    f.name, f.chain_depth, self.max_depth
47                ),
48                suggested_refactorings: vec!["Hide Delegate".into()],
49                actual_value: Some(f.chain_depth as f64),
50                threshold: Some(self.max_depth as f64),
51            })
52            .collect()
53    }
54}