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(&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, 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 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, 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}