1use std::path::Path;
2use syn::{visit::Visit, File, Ident};
3
4use crate::analyzer::{CodeIssue, RoastLevel, Severity};
5use crate::rules::Rule;
6use crate::utils::get_position;
7
8pub struct MeaninglessNamingRule;
10
11impl Rule for MeaninglessNamingRule {
12 fn name(&self) -> &'static str {
13 "meaningless-naming"
14 }
15
16 fn check(
17 &self,
18 file_path: &Path,
19 syntax_tree: &File,
20 _content: &str,
21 lang: &str,
22 ) -> Vec<CodeIssue> {
23 let mut visitor = MeaninglessNamingVisitor::new(file_path.to_path_buf(), lang);
24 visitor.visit_file(syntax_tree);
25 visitor.issues
26 }
27}
28
29pub struct HungarianNotationRule;
31
32impl Rule for HungarianNotationRule {
33 fn name(&self) -> &'static str {
34 "hungarian-notation"
35 }
36
37 fn check(
38 &self,
39 file_path: &Path,
40 syntax_tree: &File,
41 _content: &str,
42 lang: &str,
43 ) -> Vec<CodeIssue> {
44 let mut visitor = HungarianNotationVisitor::new(file_path.to_path_buf(), lang);
45 visitor.visit_file(syntax_tree);
46 visitor.issues
47 }
48}
49
50pub struct AbbreviationAbuseRule;
52
53impl Rule for AbbreviationAbuseRule {
54 fn name(&self) -> &'static str {
55 "abbreviation-abuse"
56 }
57
58 fn check(
59 &self,
60 file_path: &Path,
61 syntax_tree: &File,
62 _content: &str,
63 lang: &str,
64 ) -> Vec<CodeIssue> {
65 let mut visitor = AbbreviationAbuseVisitor::new(file_path.to_path_buf(), lang);
66 visitor.visit_file(syntax_tree);
67 visitor.issues
68 }
69}
70
71struct MeaninglessNamingVisitor {
76 file_path: std::path::PathBuf,
77 issues: Vec<CodeIssue>,
78 lang: String,
79}
80
81impl MeaninglessNamingVisitor {
82 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
83 Self {
84 file_path,
85 issues: Vec::new(),
86 lang: lang.to_string(),
87 }
88 }
89
90 fn is_meaningless_name(&self, name: &str) -> bool {
91 let meaningless_names = [
92 "foo",
94 "bar",
95 "baz",
96 "qux",
97 "quux",
98 "quuz",
99 "data",
101 "info",
102 "obj",
103 "item",
104 "thing",
105 "stuff",
106 "value",
107 "temp",
108 "tmp",
109 "test",
110 "example",
111 "sample",
112 "manager",
114 "handler",
115 "processor",
116 "controller",
117 "yonghu",
119 "mima",
120 "denglu",
121 "zhuce",
122 "shuju",
123 ];
124
125 let name_lower = name.to_lowercase();
126 meaningless_names
127 .iter()
128 .any(|&bad_name| name_lower == bad_name)
129 }
130
131 fn create_issue(&self, name: &str, line: usize, column: usize) -> CodeIssue {
132 let messages = if self.lang == "zh-CN" {
133 vec![
134 format!("变量名 '{}' 比我的网名还随意", name),
135 format!("'{}' 这个名字,是从字典里随机选的吗?", name),
136 format!("用 '{}' 做变量名?你是想让下一个维护代码的人猜谜吗?", name),
137 format!("'{}' 这个名字毫无意义,就像我的人生一样", name),
138 format!("看到 '{}' 这个变量名,我的智商受到了侮辱", name),
139 ]
140 } else {
141 vec![
142 format!("Variable name '{}' is more meaningless than my existence", name),
143 format!("'{}' - did you pick this name with your eyes closed?", name),
144 format!("Using '{}' as a variable name? Are you playing charades with future developers?", name),
145 format!("'{}' tells me nothing about what this variable does", name),
146 format!("The name '{}' is as helpful as a chocolate teapot", name),
147 ]
148 };
149
150 let severity = if ["foo", "bar", "baz", "data", "temp"].contains(&name) {
151 Severity::Spicy
152 } else {
153 Severity::Mild
154 };
155
156 CodeIssue {
157 file_path: self.file_path.clone(),
158 line,
159 column,
160 rule_name: "meaningless-naming".to_string(),
161 message: messages[self.issues.len() % messages.len()].clone(),
162 severity,
163 roast_level: RoastLevel::Sarcastic,
164 }
165 }
166}
167
168impl<'ast> Visit<'ast> for MeaninglessNamingVisitor {
169 fn visit_ident(&mut self, ident: &'ast Ident) {
170 let name = ident.to_string();
171 if self.is_meaningless_name(&name) {
172 let (line, column) = get_position(ident);
173 self.issues.push(self.create_issue(&name, line, column));
174 }
175 syn::visit::visit_ident(self, ident);
176 }
177}
178
179struct HungarianNotationVisitor {
184 file_path: std::path::PathBuf,
185 issues: Vec<CodeIssue>,
186 lang: String,
187}
188
189impl HungarianNotationVisitor {
190 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
191 Self {
192 file_path,
193 issues: Vec::new(),
194 lang: lang.to_string(),
195 }
196 }
197
198 fn is_hungarian_notation(&self, name: &str) -> bool {
199 let hungarian_prefixes = [
200 "str", "int", "bool", "float", "double", "char", "arr", "vec", "list", "map", "set",
202 "g_", "m_", "s_", "p_", "b", "n", "sz", "lp", "dw",
205 ];
206
207 for prefix in hungarian_prefixes {
209 if name.starts_with(prefix) && name.len() > prefix.len() {
210 if let Some(next_char) = name.chars().nth(prefix.len()) {
212 if next_char.is_uppercase() {
213 return true;
214 }
215 }
216 if name.starts_with(&format!("{prefix}_")) {
218 return true;
219 }
220 }
221 }
222
223 false
224 }
225
226 fn create_issue(&self, name: &str, line: usize, column: usize) -> CodeIssue {
227 let messages = if self.lang == "zh-CN" {
228 vec![
229 format!("'{}' 使用了匈牙利命名法?这不是1990年代了", name),
230 format!("看到 '{}' 我仿佛回到了 C++ 的石器时代", name),
231 format!("'{}' 这种命名方式已经过时了,就像我的发型一样", name),
232 format!("匈牙利命名法 '{}'?Rust 编译器已经帮你检查类型了", name),
233 format!("'{}' 让我想起了那些痛苦的 C++ 岁月", name),
234 ]
235 } else {
236 vec![
237 format!(
238 "'{}' uses Hungarian notation? This isn't the 1990s anymore",
239 name
240 ),
241 format!(
242 "Seeing '{}' makes me nostalgic for the dark ages of C++",
243 name
244 ),
245 format!(
246 "'{}' - Hungarian notation is as outdated as my haircut",
247 name
248 ),
249 format!(
250 "Hungarian notation '{}'? Rust's type system has got you covered",
251 name
252 ),
253 format!("'{}' reminds me of painful C++ memories", name),
254 ]
255 };
256
257 CodeIssue {
258 file_path: self.file_path.clone(),
259 line,
260 column,
261 rule_name: "hungarian-notation".to_string(),
262 message: messages[self.issues.len() % messages.len()].clone(),
263 severity: Severity::Mild,
264 roast_level: RoastLevel::Sarcastic,
265 }
266 }
267}
268
269impl<'ast> Visit<'ast> for HungarianNotationVisitor {
270 fn visit_ident(&mut self, ident: &'ast Ident) {
271 let name = ident.to_string();
272 if self.is_hungarian_notation(&name) {
273 let (line, column) = get_position(ident);
274 self.issues.push(self.create_issue(&name, line, column));
275 }
276 syn::visit::visit_ident(self, ident);
277 }
278}
279
280struct AbbreviationAbuseVisitor {
285 file_path: std::path::PathBuf,
286 issues: Vec<CodeIssue>,
287 lang: String,
288}
289
290impl AbbreviationAbuseVisitor {
291 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
292 Self {
293 file_path,
294 issues: Vec::new(),
295 lang: lang.to_string(),
296 }
297 }
298
299 fn is_bad_abbreviation(&self, name: &str) -> Option<&'static str> {
300 let bad_abbreviations = [
301 ("mgr", "manager"),
303 ("mngr", "manager"),
304 ("ctrl", "controller"),
305 ("proc", "processor"),
306 ("hdlr", "handler"),
307 ("usr", "user"),
309 ("pwd", "password"),
310 ("auth", "authentication"),
311 ("cfg", "config"),
312 ("prefs", "preferences"),
313 ("btn", "button"),
315 ("lbl", "label"),
316 ("txt", "text"),
317 ("img", "image"),
318 ("pic", "picture"),
319 ("db", "database"),
321 ("tbl", "table"),
322 ("col", "column"),
323 ("idx", "index"),
324 ("cnt", "count"),
325 ("calc", "calculate"),
327 ("init", "initialize"),
328 ("exec", "execute"),
329 ("impl", "implementation"),
330 ("util", "utility"),
331 ];
332
333 let name_lower = name.to_lowercase();
334 for (abbrev, full) in bad_abbreviations {
335 if name_lower == abbrev || name_lower.starts_with(&format!("{abbrev}_")) {
336 return Some(full);
337 }
338 }
339 None
340 }
341
342 fn create_issue(&self, name: &str, suggestion: &str, line: usize, column: usize) -> CodeIssue {
343 let messages = if self.lang == "zh-CN" {
344 vec![
345 format!("'{}' 缩写得太狠了,建议用 '{}'", name, suggestion),
346 format!("看到 '{}' 我需要解密,不如直接用 '{}'", name, suggestion),
347 format!(
348 "'{}' 这个缩写让我想起了发电报的年代,用 '{}' 吧",
349 name, suggestion
350 ),
351 format!(
352 "'{}' 省了几个字母,却让代码可读性大打折扣,试试 '{}'",
353 name, suggestion
354 ),
355 format!("缩写 '{}' 就像密码一样难懂,'{}'不香吗?", name, suggestion),
356 ]
357 } else {
358 vec![
359 format!("'{}' is too abbreviated, consider '{}'", name, suggestion),
360 format!(
361 "Seeing '{}' makes me feel like I'm decoding, just use '{}'",
362 name, suggestion
363 ),
364 format!(
365 "'{}' reminds me of telegraph era, try '{}'",
366 name, suggestion
367 ),
368 format!(
369 "'{}' saves a few letters but kills readability, use '{}'",
370 name, suggestion
371 ),
372 format!(
373 "Abbreviation '{}' is cryptic, isn't '{}' better?",
374 name, suggestion
375 ),
376 ]
377 };
378
379 CodeIssue {
380 file_path: self.file_path.clone(),
381 line,
382 column,
383 rule_name: "abbreviation-abuse".to_string(),
384 message: messages[self.issues.len() % messages.len()].clone(),
385 severity: Severity::Mild,
386 roast_level: RoastLevel::Gentle,
387 }
388 }
389}
390
391impl<'ast> Visit<'ast> for AbbreviationAbuseVisitor {
392 fn visit_ident(&mut self, ident: &'ast Ident) {
393 let name = ident.to_string();
394 if let Some(suggestion) = self.is_bad_abbreviation(&name) {
395 let (line, column) = get_position(ident);
396 self.issues
397 .push(self.create_issue(&name, suggestion, line, column));
398 }
399 syn::visit::visit_ident(self, ident);
400 }
401}