garbage_code_hunter/rules/
naming.rs

1use regex::Regex;
2use std::path::Path;
3use syn::{visit::Visit, File, Ident};
4
5use crate::analyzer::{CodeIssue, RoastLevel, Severity};
6use crate::rules::Rule;
7
8pub struct TerribleNamingRule;
9
10impl Rule for TerribleNamingRule {
11    fn name(&self) -> &'static str {
12        "terrible-naming"
13    }
14
15    fn check(&self, file_path: &Path, syntax_tree: &File, _content: &str) -> Vec<CodeIssue> {
16        let mut visitor = NamingVisitor::new(file_path.to_path_buf());
17        visitor.visit_file(syntax_tree);
18        visitor.issues
19    }
20}
21
22pub struct SingleLetterVariableRule;
23
24impl Rule for SingleLetterVariableRule {
25    fn name(&self) -> &'static str {
26        "single-letter-variable"
27    }
28
29    fn check(&self, file_path: &Path, syntax_tree: &File, _content: &str) -> Vec<CodeIssue> {
30        let mut visitor = SingleLetterVisitor::new(file_path.to_path_buf());
31        visitor.visit_file(syntax_tree);
32        visitor.issues
33    }
34}
35
36struct NamingVisitor {
37    file_path: std::path::PathBuf,
38    issues: Vec<CodeIssue>,
39    terrible_names: Regex,
40}
41
42impl NamingVisitor {
43    fn new(file_path: std::path::PathBuf) -> Self {
44        let terrible_names = Regex::new(r"^(data|info|temp|tmp|val|value|item|thing|stuff|obj|object|manager|handler|helper|util|utils)(\d+)?$").unwrap();
45
46        Self {
47            file_path,
48            issues: Vec::new(),
49            terrible_names,
50        }
51    }
52
53    fn check_name(&mut self, ident: &Ident) {
54        let name = ident.to_string();
55
56        if self.terrible_names.is_match(&name.to_lowercase()) {
57            let messages = vec![
58                format!("变量名 '{}' 比我的编程技能还要抽象", name),
59                format!("'{}' 这个名字告诉我你已经放弃治疗了", name),
60                format!("用 '{}' 做变量名?你是想让维护代码的人哭吗?", name),
61                format!("'{}' - 恭喜你发明了最没有意义的变量名", name),
62            ];
63
64            self.issues.push(CodeIssue {
65                file_path: self.file_path.clone(),
66                line: 1, // Simplified handling, real projects could use proc-macro2's LineColumn
67                column: 1,
68                rule_name: "terrible-naming".to_string(),
69                message: messages[self.issues.len() % messages.len()].clone(),
70                severity: Severity::Spicy,
71                roast_level: RoastLevel::Sarcastic,
72            });
73        }
74    }
75}
76
77impl<'ast> Visit<'ast> for NamingVisitor {
78    fn visit_ident(&mut self, ident: &'ast Ident) {
79        self.check_name(ident);
80        syn::visit::visit_ident(self, ident);
81    }
82}
83
84struct SingleLetterVisitor {
85    file_path: std::path::PathBuf,
86    issues: Vec<CodeIssue>,
87}
88
89impl SingleLetterVisitor {
90    fn new(file_path: std::path::PathBuf) -> Self {
91        Self {
92            file_path,
93            issues: Vec::new(),
94        }
95    }
96}
97
98impl<'ast> Visit<'ast> for SingleLetterVisitor {
99    fn visit_pat_ident(&mut self, pat_ident: &'ast syn::PatIdent) {
100        let name = pat_ident.ident.to_string();
101
102        // Exclude common single-letter variables (like loop counters i, j, k)
103        if name.len() == 1 && !matches!(name.as_str(), "i" | "j" | "k" | "x" | "y" | "z") {
104            let messages = vec![
105                format!(
106                    "单字母变量 '{}'?你是在写数学公式还是在折磨读代码的人?",
107                    name
108                ),
109                format!("'{}'?这是变量名还是你键盘坏了?", name),
110                format!(
111                    "用 '{}' 做变量名,你可能需要一本《如何给变量起名》的书",
112                    name
113                ),
114            ];
115
116            self.issues.push(CodeIssue {
117                file_path: self.file_path.clone(),
118                line: 1, // Simplified handling
119                column: 1,
120                rule_name: "single-letter-variable".to_string(),
121                message: messages[self.issues.len() % messages.len()].clone(),
122                severity: Severity::Mild,
123                roast_level: RoastLevel::Gentle,
124            });
125        }
126
127        syn::visit::visit_pat_ident(self, pat_ident);
128    }
129}