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 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 })
67 })
68 .collect()
69 }
70}