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 description(&self) -> &str {
24        "Subclass overrides most parent methods"
25    }
26
27    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
28        ctx.model
29            .classes
30            .iter()
31            .filter_map(|c| {
32                let parent = c.parent_name.as_ref()?;
33                if c.method_count < self.min_methods || c.override_count == 0 {
34                    return None;
35                }
36                let ratio = c.override_count as f64 / c.method_count as f64;
37                if ratio < self.min_override_ratio {
38                    return None;
39                }
40                Some(Finding {
41                    smell_name: "refused_bequest".into(),
42                    category: SmellCategory::OoAbusers,
43                    severity: Severity::Hint,
44                    location: Location {
45                        path: ctx.file.path.clone(),
46                        start_line: c.start_line,
47                        end_line: c.end_line,
48                        name: Some(c.name.clone()),
49                    },
50                    message: format!(
51                        "Class `{}` overrides {}/{} methods from `{}`, consider Replace Inheritance with Delegation",
52                        c.name, c.override_count, c.method_count, parent
53                    ),
54                    suggested_refactorings: vec![
55                        "Replace Inheritance with Delegation".into(),
56                        "Push Down Method".into(),
57                    ],
58                    actual_value: Some(ratio),
59                    threshold: Some(self.min_override_ratio),
60                })
61            })
62            .collect()
63    }
64}