Skip to main content

cha_core/plugins/
refused_bequest.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect classes that inherit but override most parent methods (refused bequest).
4pub struct RefusedBequestAnalyzer {
5    pub min_override_ratio: f64,
6    pub min_methods: usize,
7}
8
9impl Default for RefusedBequestAnalyzer {
10    fn default() -> Self {
11        Self {
12            min_override_ratio: 0.5,
13            min_methods: 3,
14        }
15    }
16}
17
18impl Plugin for RefusedBequestAnalyzer {
19    fn name(&self) -> &str {
20        "refused_bequest"
21    }
22
23    fn smells(&self) -> Vec<String> {
24        vec!["refused_bequest".into()]
25    }
26
27    fn description(&self) -> &str {
28        "Subclass overrides most parent methods"
29    }
30
31    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
32        ctx.model
33            .classes
34            .iter()
35            .filter_map(|c| {
36                let parent = c.parent_name.as_ref()?;
37                if c.method_count < self.min_methods || c.override_count == 0 {
38                    return None;
39                }
40                let ratio = c.override_count as f64 / c.method_count as f64;
41                if ratio < self.min_override_ratio {
42                    return None;
43                }
44                Some(Finding {
45                    smell_name: "refused_bequest".into(),
46                    category: SmellCategory::OoAbusers,
47                    severity: Severity::Hint,
48                    location: Location {
49                        path: ctx.file.path.clone(),
50                        start_line: c.start_line,
51                        start_col: c.name_col,
52                        end_line: c.start_line,
53                        end_col: c.name_end_col,
54                        name: Some(c.name.clone()),
55                    },
56                    message: format!(
57                        "Class `{}` overrides {}/{} methods from `{}`, consider Replace Inheritance with Delegation",
58                        c.name, c.override_count, c.method_count, parent
59                    ),
60                    suggested_refactorings: vec![
61                        "Replace Inheritance with Delegation".into(),
62                        "Push Down Method".into(),
63                    ],
64                    actual_value: Some(ratio),
65                    threshold: Some(self.min_override_ratio),
66                    risk_score: None,
67                })
68            })
69            .collect()
70    }
71}