garbage_code_hunter/rules/
rust_specific.rs1use std::path::Path;
2use syn::{visit::Visit, ExprMethodCall, File};
3
4use crate::analyzer::{CodeIssue, RoastLevel, Severity};
5use crate::rules::Rule;
6
7pub struct UnwrapAbuseRule;
8
9impl Rule for UnwrapAbuseRule {
10 fn name(&self) -> &'static str {
11 "unwrap-abuse"
12 }
13
14 fn check(&self, file_path: &Path, syntax_tree: &File, _content: &str) -> Vec<CodeIssue> {
15 let mut visitor = UnwrapVisitor::new(file_path.to_path_buf());
16 visitor.visit_file(syntax_tree);
17 visitor.issues
18 }
19}
20
21pub struct UnnecessaryCloneRule;
22
23impl Rule for UnnecessaryCloneRule {
24 fn name(&self) -> &'static str {
25 "unnecessary-clone"
26 }
27
28 fn check(&self, file_path: &Path, syntax_tree: &File, _content: &str) -> Vec<CodeIssue> {
29 let mut visitor = CloneVisitor::new(file_path.to_path_buf());
30 visitor.visit_file(syntax_tree);
31 visitor.issues
32 }
33}
34
35struct UnwrapVisitor {
36 file_path: std::path::PathBuf,
37 issues: Vec<CodeIssue>,
38 unwrap_count: usize,
39}
40
41impl UnwrapVisitor {
42 fn new(file_path: std::path::PathBuf) -> Self {
43 Self {
44 file_path,
45 issues: Vec::new(),
46 unwrap_count: 0,
47 }
48 }
49}
50
51impl<'ast> Visit<'ast> for UnwrapVisitor {
52 fn visit_expr_method_call(&mut self, method_call: &'ast ExprMethodCall) {
53 if method_call.method.to_string() == "unwrap" {
54 self.unwrap_count += 1;
55
56 let messages = vec![
57 "又一个 unwrap()!你是想让程序在生产环境里爆炸吗?",
58 "unwrap() 大师!错误处理是什么?能吃吗?",
59 "看到这个 unwrap(),我仿佛听到了程序崩溃的声音",
60 "unwrap() 使用者,恭喜你获得了'程序炸弹制造专家'称号",
61 "这个 unwrap() 就像定时炸弹,不知道什么时候会爆",
62 "又见 unwrap()!建议使用 match 或 if let,除非你喜欢 panic",
63 ];
64
65 let severity = if self.unwrap_count > 5 {
66 Severity::Nuclear
67 } else if self.unwrap_count > 2 {
68 Severity::Spicy
69 } else {
70 Severity::Mild
71 };
72
73 let roast_level = if self.unwrap_count > 5 {
74 RoastLevel::Savage
75 } else {
76 RoastLevel::Sarcastic
77 };
78
79 self.issues.push(CodeIssue {
80 file_path: self.file_path.clone(),
81 line: 1, column: 1,
83 rule_name: "unwrap-abuse".to_string(),
84 message: messages[self.issues.len() % messages.len()].to_string(),
85 severity,
86 roast_level,
87 });
88 }
89
90 syn::visit::visit_expr_method_call(self, method_call);
91 }
92}
93
94struct CloneVisitor {
95 file_path: std::path::PathBuf,
96 issues: Vec<CodeIssue>,
97 clone_count: usize,
98}
99
100impl CloneVisitor {
101 fn new(file_path: std::path::PathBuf) -> Self {
102 Self {
103 file_path,
104 issues: Vec::new(),
105 clone_count: 0,
106 }
107 }
108}
109
110impl<'ast> Visit<'ast> for CloneVisitor {
111 fn visit_expr_method_call(&mut self, method_call: &'ast ExprMethodCall) {
112 if method_call.method.to_string() == "clone" {
113 self.clone_count += 1;
114
115 if self.clone_count > 3 {
117 let messages = vec![
118 "clone() 狂魔!你是想把内存用完吗?",
119 "这么多 clone(),你确定不是在写 Java?",
120 "clone() 使用过度!Rust 的借用检查器在哭泣",
121 "又见 clone()!也许你需要重新学习一下 Rust 的所有权系统",
122 "这些 clone() 让我想起了复印机店的老板",
123 ];
124
125 self.issues.push(CodeIssue {
126 file_path: self.file_path.clone(),
127 line: 1, column: 1,
129 rule_name: "unnecessary-clone".to_string(),
130 message: messages[self.issues.len() % messages.len()].to_string(),
131 severity: Severity::Spicy,
132 roast_level: RoastLevel::Sarcastic,
133 });
134 }
135 }
136
137 syn::visit::visit_expr_method_call(self, method_call);
138 }
139}