Skip to main content

garbage_code_hunter/rules/
rust_patterns.rs

1use std::path::Path;
2use syn::File;
3
4use crate::analyzer::{CodeIssue, Severity};
5use crate::rules::Rule;
6use crate::utils::{count_non_import_matches, find_line_of_str_non_import};
7
8/// Detect using String everywhere instead of &str
9pub struct StringAbuseRule;
10
11impl Rule for StringAbuseRule {
12    fn name(&self) -> &'static str {
13        "string-abuse"
14    }
15
16    fn check(
17        &self,
18        file_path: &Path,
19        _syntax_tree: &File,
20        content: &str,
21        lang: &str,
22        is_test_file: bool,
23    ) -> Vec<CodeIssue> {
24        if is_test_file {
25            return Vec::new();
26        }
27
28        let mut issues = Vec::new();
29
30        // Check String usage patterns in content (excluding comments and imports)
31        let string_new_count = count_non_import_matches(content, "String::new()");
32        let string_from_count = count_non_import_matches(content, "String::from(");
33        let to_string_count = count_non_import_matches(content, ".to_string()");
34        let total = string_new_count + string_from_count + to_string_count;
35
36        // Increased threshold: 25+ to avoid false positives in rule definitions
37        // (each CodeIssue creation uses .to_string() for rule_name/message)
38        if total > 25 {
39            let messages = if lang == "zh-CN" {
40                vec![
41                    format!("{} 次 String 转换?你是在开字符串工厂吗?", total),
42                    format!("这么多 String 分配,内存都要哭了"),
43                    format!("{} 个 String 转换,考虑用 &str 吧", total),
44                    format!("String 用得比我换衣服还频繁"),
45                    format!("到处都是 String::from(),性能在哭泣"),
46                ]
47            } else {
48                vec![
49                    format!(
50                        "{} String conversions? Are you running a string factory?",
51                        total
52                    ),
53                    format!("So many String allocations, memory is crying"),
54                    format!("{} String conversions - consider using &str", total),
55                    format!("You use String more than I change clothes"),
56                    format!("String::from() everywhere - performance is weeping"),
57                ]
58            };
59
60            let candidates = [
61                find_line_of_str_non_import(content, "String::from("),
62                find_line_of_str_non_import(content, "String::new()"),
63                find_line_of_str_non_import(content, ".to_string()"),
64            ];
65            let line = candidates
66                .iter()
67                .copied()
68                .filter(|&l| l > 1)
69                .min()
70                .unwrap_or(1);
71
72            issues.push(CodeIssue {
73                file_path: file_path.to_path_buf(),
74                line,
75                column: 1,
76                rule_name: "string-abuse".to_string(),
77                message: messages[total % messages.len()].clone(),
78                severity: Severity::Spicy,
79            });
80        }
81
82        issues
83    }
84}
85
86/// Detect unnecessary Vec allocations
87pub struct VecAbuseRule;
88
89impl Rule for VecAbuseRule {
90    fn name(&self) -> &'static str {
91        "vec-abuse"
92    }
93
94    fn check(
95        &self,
96        file_path: &Path,
97        _syntax_tree: &File,
98        content: &str,
99        lang: &str,
100        is_test_file: bool,
101    ) -> Vec<CodeIssue> {
102        if is_test_file {
103            return Vec::new();
104        }
105
106        let mut issues = Vec::new();
107
108        // Check Vec usage patterns in content (excluding comments and imports)
109        let vec_new_count = count_non_import_matches(content, "Vec::new()");
110
111        if vec_new_count > 15 {
112            let messages = if lang == "zh-CN" {
113                vec![
114                    format!("{} 个 Vec::new()?你在收集什么?", vec_new_count),
115                    format!("这么多 Vec 分配,考虑用数组或切片"),
116                    format!("{} 次 Vec 分配,内存分配器很忙", vec_new_count),
117                    format!("Vec 用得这么多,确定都需要动态数组吗?"),
118                ]
119            } else {
120                vec![
121                    format!("{} Vec::new()s? What are you collecting?", vec_new_count),
122                    format!("So many Vec allocations - consider arrays or slices"),
123                    format!(
124                        "{} Vec allocations - memory allocator is busy",
125                        vec_new_count
126                    ),
127                    format!("So many Vecs - sure you need all these dynamic arrays?"),
128                ]
129            };
130
131            let line = find_line_of_str_non_import(content, "Vec::new()");
132
133            issues.push(CodeIssue {
134                file_path: file_path.to_path_buf(),
135                line,
136                column: 1,
137                rule_name: "vec-abuse".to_string(),
138                message: messages[vec_new_count % messages.len()].clone(),
139                severity: Severity::Mild,
140            });
141        }
142
143        issues
144    }
145}