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