cha_core/plugins/
refused_bequest.rs1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3pub 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}