garbage_code_hunter/rules/
garbage_naming.rs

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
8/// 检测无意义的占位符命名:foo, bar, baz, qux, test, temp 等
9pub 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
29/// 检测过时的匈牙利命名法:strName, intCount, bIsValid 等
30pub 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
50/// 检测过度缩写:mgr, ctrl, btn, usr, pwd 等
51pub 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
71// ============================================================================
72// Visitor 实现
73// ============================================================================
74
75struct 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            // 经典占位符
93            "foo",
94            "bar",
95            "baz",
96            "qux",
97            "quux",
98            "quuz",
99            // 无意义的通用词
100            "data",
101            "info",
102            "obj",
103            "item",
104            "thing",
105            "stuff",
106            "value",
107            "temp",
108            "tmp",
109            "test",
110            "example",
111            "sample",
112            // 管理器后缀滥用
113            "manager",
114            "handler",
115            "processor",
116            "controller",
117            // 中文拼音(常见的)
118            "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
179// ============================================================================
180// 匈牙利命名法检测
181// ============================================================================
182
183struct 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            // 类型前缀
201            "str", "int", "bool", "float", "double", "char", "arr", "vec", "list", "map", "set",
202            // 作用域前缀
203            "g_", "m_", "s_", "p_", // 其他常见前缀
204            "b", "n", "sz", "lp", "dw",
205        ];
206
207        // 检查是否以匈牙利前缀开头
208        for prefix in hungarian_prefixes {
209            if name.starts_with(prefix) && name.len() > prefix.len() {
210                // 检查前缀后是否跟着大写字母(驼峰命名)
211                if let Some(next_char) = name.chars().nth(prefix.len()) {
212                    if next_char.is_uppercase() {
213                        return true;
214                    }
215                }
216                // 检查下划线分隔的情况
217                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
280// ============================================================================
281// 过度缩写检测
282// ============================================================================
283
284struct 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            // 管理相关
302            ("mgr", "manager"),
303            ("mngr", "manager"),
304            ("ctrl", "controller"),
305            ("proc", "processor"),
306            ("hdlr", "handler"),
307            // 用户相关
308            ("usr", "user"),
309            ("pwd", "password"),
310            ("auth", "authentication"),
311            ("cfg", "config"),
312            ("prefs", "preferences"),
313            // 界面相关
314            ("btn", "button"),
315            ("lbl", "label"),
316            ("txt", "text"),
317            ("img", "image"),
318            ("pic", "picture"),
319            // 数据相关
320            ("db", "database"),
321            ("tbl", "table"),
322            ("col", "column"),
323            ("idx", "index"),
324            ("cnt", "count"),
325            // 其他常见缩写
326            ("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}