garbage_code_hunter/rules/
naming.rs1use 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(
16 &self,
17 file_path: &Path,
18 syntax_tree: &File,
19 _content: &str,
20 lang: &str,
21 ) -> Vec<CodeIssue> {
22 let mut visitor = NamingVisitor::new(file_path.to_path_buf(), lang);
23 visitor.visit_file(syntax_tree);
24 visitor.issues
25 }
26}
27
28pub struct SingleLetterVariableRule;
29
30impl Rule for SingleLetterVariableRule {
31 fn name(&self) -> &'static str {
32 "single-letter-variable"
33 }
34
35 fn check(
36 &self,
37 file_path: &Path,
38 syntax_tree: &File,
39 _content: &str,
40 lang: &str,
41 ) -> Vec<CodeIssue> {
42 let mut visitor = SingleLetterVisitor::new(file_path.to_path_buf(), lang);
43 visitor.visit_file(syntax_tree);
44 visitor.issues
45 }
46}
47
48struct NamingVisitor {
49 file_path: std::path::PathBuf,
50 issues: Vec<CodeIssue>,
51 terrible_names: Regex,
52 lang: String,
53}
54
55impl NamingVisitor {
56 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
57 let terrible_names = Regex::new(r"^(data|info|temp|tmp|val|value|item|thing|stuff|obj|object|manager|handler|helper|util|utils|a|b|c|d|e|f|g|h|test|func|function)(\d+)?$").unwrap();
58
59 Self {
60 file_path,
61 issues: Vec::new(),
62 terrible_names,
63 lang: lang.to_string(),
64 }
65 }
66
67 fn check_name(&mut self, ident: &Ident, context: &str) {
68 let name = ident.to_string();
69
70 if self.terrible_names.is_match(&name.to_lowercase()) {
71 let messages = if self.lang == "zh-CN" {
73 let ctx = if context == "函数名" {
75 "函数名"
76 } else {
77 "变量名"
78 };
79 vec![
80 format!("{} '{}' - 比我的编程技能还要抽象", ctx, name),
81 format!(
82 "{} '{}' - 这个名字告诉我你已经放弃治疗了,建议直接转行卖煎饼果子",
83 ctx, name
84 ),
85 format!(
86 "{} '{}' - 用这个做名字?你是想让维护代码的人哭着辞职吗?",
87 ctx, name
88 ),
89 format!("{} '{}' - 恭喜你发明了最没有意义的标识符", ctx, name),
90 format!("{} '{}' - 创意程度约等于给孩子起名叫'小明'", ctx, name),
91 format!(
92 "{} '{}' - 看到这个名字,我的智商都下降了,现在只能数到3了",
93 ctx, name
94 ),
95 ]
96 } else {
97 let ctx = if context == "Function" {
99 "Function"
100 } else {
101 "Variable"
102 };
103 vec![
104 format!("{} '{}' - more abstract than my programming skills", ctx, name),
105 format!("{} '{}' - this name tells me you've given up on life and should sell hotdogs", ctx, name),
106 format!("{} '{}' - using this name? trying to make maintainers cry and quit?", ctx, name),
107 format!("{} '{}' - congrats on inventing the most meaningless identifier", ctx, name),
108 format!("{} '{}' - creativity level of naming a kid 'Child'", ctx, name),
109 format!("{} '{}' - seeing this name, my IQ dropped to single digits", ctx, name),
110 ]
111 };
112
113 let message_index =
114 (self.issues.len() + name.len() + name.chars().next().unwrap_or('a') as usize)
115 % messages.len();
116
117 self.issues.push(CodeIssue {
118 file_path: self.file_path.clone(),
119 line: self.issues.len() + 2, column: (name.len() % 10) + 1, rule_name: "terrible-naming".to_string(),
122 message: messages[message_index].clone(),
123 severity: Severity::Spicy,
124 roast_level: RoastLevel::Sarcastic,
125 });
126 }
127 }
128}
129
130impl<'ast> Visit<'ast> for NamingVisitor {
131 fn visit_ident(&mut self, ident: &'ast Ident) {
132 let context = if self.lang == "zh-CN" {
133 "变量名"
134 } else {
135 "Variable"
136 };
137 self.check_name(ident, context);
138 syn::visit::visit_ident(self, ident);
139 }
140
141 fn visit_item_fn(&mut self, func: &'ast syn::ItemFn) {
143 let context = if self.lang == "zh-CN" {
144 "函数名"
145 } else {
146 "Function"
147 };
148 self.check_name(&func.sig.ident, context);
149 syn::visit::visit_item_fn(self, func);
150 }
151}
152
153#[allow(dead_code)]
154struct SingleLetterVisitor {
155 file_path: std::path::PathBuf,
156 issues: Vec<CodeIssue>,
157 lang: String,
158}
159
160impl SingleLetterVisitor {
161 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
162 Self {
163 file_path,
164 issues: Vec::new(),
165 lang: lang.to_string(),
166 }
167 }
168}
169
170impl<'ast> Visit<'ast> for SingleLetterVisitor {
171 fn visit_pat_ident(&mut self, pat_ident: &'ast syn::PatIdent) {
172 let name = pat_ident.ident.to_string();
173
174 if name.len() == 1 && !matches!(name.as_str(), "i" | "j" | "k" | "x" | "y" | "z") {
176 let messages = [
177 format!("单字母变量 '{name}'?你是在写数学公式还是在折磨读代码的人?"),
178 format!("变量 '{name}'?这是变量名还是你键盘坏了?"),
179 format!("用 '{name}' 做变量名,你可能需要一本《如何给变量起名》的书"),
180 format!("单字母变量 '{name}':让代码比古埃及象形文字还难懂"),
181 format!("变量 '{name}' 的信息量约等于一个句号"),
182 ];
183
184 let message_index =
185 (self.issues.len() + name.len() + name.chars().next().unwrap_or('a') as usize)
186 % messages.len();
187
188 self.issues.push(CodeIssue {
189 file_path: self.file_path.clone(),
190 line: self.issues.len() + 10, column: (name.len() % 5) + 1,
192 rule_name: "single-letter-variable".to_string(),
193 message: messages[message_index].clone(),
194 severity: Severity::Mild,
195 roast_level: RoastLevel::Gentle,
196 });
197 }
198
199 syn::visit::visit_pat_ident(self, pat_ident);
200 }
201}