garbage_code_hunter/rules/
rust_patterns.rs1use 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
8pub 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 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 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
86pub 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 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}