cha_core/plugins/
temporary_field.rs1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3pub struct TemporaryFieldAnalyzer {
5 pub min_methods: usize,
6 pub max_usage_ratio: f64,
7}
8
9impl Default for TemporaryFieldAnalyzer {
10 fn default() -> Self {
11 Self {
12 min_methods: 3,
13 max_usage_ratio: 0.3,
14 }
15 }
16}
17
18impl Plugin for TemporaryFieldAnalyzer {
19 fn name(&self) -> &str {
20 "temporary_field"
21 }
22
23 fn smells(&self) -> Vec<String> {
24 vec!["temporary_field".into()]
25 }
26
27 fn description(&self) -> &str {
28 "Fields used in too few methods"
29 }
30
31 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
32 let mut findings = Vec::new();
33 for class in &ctx.model.classes {
34 if class.field_names.is_empty() || class.method_count < self.min_methods {
35 continue;
36 }
37 let methods: Vec<_> = ctx
38 .model
39 .functions
40 .iter()
41 .filter(|f| f.start_line >= class.start_line && f.end_line <= class.end_line)
42 .collect();
43 if methods.len() < self.min_methods {
44 continue;
45 }
46 self.check_fields(ctx, class, &methods, &mut findings);
47 }
48 findings
49 }
50}
51
52impl TemporaryFieldAnalyzer {
53 fn check_fields(
54 &self,
55 ctx: &AnalysisContext,
56 class: &crate::ClassInfo,
57 methods: &[&crate::FunctionInfo],
58 findings: &mut Vec<Finding>,
59 ) {
60 for field in &class.field_names {
61 let usage = methods
62 .iter()
63 .filter(|m| m.referenced_fields.contains(field))
64 .count();
65 let ratio = usage as f64 / methods.len() as f64;
66 if ratio > 0.0 && ratio <= self.max_usage_ratio {
67 findings.push(Finding {
68 smell_name: "temporary_field".into(),
69 category: SmellCategory::OoAbusers,
70 severity: Severity::Hint,
71 location: Location {
72 path: ctx.file.path.clone(),
73 start_line: class.start_line,
74 end_line: class.end_line,
75 name: Some(format!("{}.{}", class.name, field)),
76 ..Default::default()
77 },
78 message: format!(
79 "Field `{}` in `{}` is only used in {}/{} methods, consider Extract Class",
80 field,
81 class.name,
82 usage,
83 methods.len()
84 ),
85 suggested_refactorings: vec!["Extract Class".into()],
86 actual_value: Some(ratio),
87 threshold: Some(self.max_usage_ratio),
88 });
89 }
90 }
91 }
92}