Skip to main content

garbage_code_hunter/rules/
rust_specific.rs

1use std::path::Path;
2use syn::{visit::Visit, ExprMethodCall, File};
3
4use crate::analyzer::{CodeIssue, Severity};
5use crate::context::FileContext;
6use crate::rules::Rule;
7use crate::utils::get_position;
8
9pub struct UnwrapAbuseRule;
10
11impl Rule for UnwrapAbuseRule {
12    fn name(&self) -> &'static str {
13        "unwrap-abuse"
14    }
15
16    fn check(
17        &self,
18        file_path: &Path,
19        syntax_tree: &File,
20        _content: &str,
21        lang: &str,
22        is_test_file: bool,
23    ) -> Vec<CodeIssue> {
24        if is_test_file {
25            return Vec::new();
26        }
27        let mut visitor = UnwrapVisitor::new(file_path.to_path_buf(), lang);
28        visitor.visit_file(syntax_tree);
29        visitor.issues
30    }
31
32    fn check_with_context(
33        &self,
34        file_path: &Path,
35        syntax_tree: &File,
36        content: &str,
37        lang: &str,
38        is_test_file: bool,
39        context: &FileContext,
40        _config: &crate::context::ProjectConfig,
41    ) -> Vec<CodeIssue> {
42        // Test/Benchmark/Documentation: 完全跳过(unwrap 在测试中很常见)
43        let weight = context.rule_weight_multiplier();
44        if weight < 0.5 {
45            return Vec::new();
46        }
47
48        // Example/Demo: 提高阈值到 >10 才报告
49        if weight < 0.7 {
50            let issues = self.check(file_path, syntax_tree, content, lang, is_test_file);
51            // 仅当 unwrap 数量 > 10 时才报告
52            if issues.len() > 10 {
53                return issues;
54            }
55            return Vec::new();
56        }
57
58        self.check(file_path, syntax_tree, content, lang, is_test_file)
59    }
60}
61
62pub struct UnnecessaryCloneRule;
63
64impl Rule for UnnecessaryCloneRule {
65    fn name(&self) -> &'static str {
66        "unnecessary-clone"
67    }
68
69    fn check(
70        &self,
71        file_path: &Path,
72        syntax_tree: &File,
73        _content: &str,
74        lang: &str,
75        is_test_file: bool,
76    ) -> Vec<CodeIssue> {
77        if is_test_file {
78            return Vec::new();
79        }
80        let mut visitor = CloneVisitor::new(file_path.to_path_buf(), lang);
81        visitor.visit_file(syntax_tree);
82        visitor.issues
83    }
84}
85
86struct UnwrapVisitor {
87    file_path: std::path::PathBuf,
88    issues: Vec<CodeIssue>,
89    unwrap_count: usize,
90    first_position: (usize, usize),
91    lang: String,
92}
93
94impl UnwrapVisitor {
95    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
96        Self {
97            file_path,
98            issues: Vec::new(),
99            unwrap_count: 0,
100            first_position: (1, 1),
101            lang: lang.to_string(),
102        }
103    }
104}
105
106impl<'ast> Visit<'ast> for UnwrapVisitor {
107    fn visit_expr_method_call(&mut self, method_call: &'ast ExprMethodCall) {
108        if method_call.method == "unwrap" {
109            if self.unwrap_count == 0 {
110                self.first_position = get_position(method_call);
111            }
112            self.unwrap_count += 1;
113        }
114
115        syn::visit::visit_expr_method_call(self, method_call);
116    }
117
118    fn visit_file(&mut self, file: &'ast syn::File) {
119        syn::visit::visit_file(self, file);
120        // After visiting, report a single issue with total count
121        if self.unwrap_count > 0 {
122            let (line, column) = self.first_position;
123
124            // Determine severity and message based on count
125            let (severity, message) = if self.unwrap_count > 15 {
126                let messages = if self.lang == "zh-CN" {
127                    vec![
128                        format!(
129                            "🚨 {} 个 unwrap()!这是在生产环境玩俄罗斯轮盘",
130                            self.unwrap_count
131                        ),
132                        format!(
133                            "{} 个 unwrap(),你的程序比我的感情生活还不稳定",
134                            self.unwrap_count
135                        ),
136                        format!("unwrap() 滥用警报:{} 处潜在崩溃点", self.unwrap_count),
137                        format!(
138                            "{} 个 unwrap() = {} 次潜在的 panic!立即修复!",
139                            self.unwrap_count, self.unwrap_count
140                        ),
141                    ]
142                } else {
143                    vec![
144                        format!(
145                            "🚨 {} unwrap()s! Playing Russian roulette in production?",
146                            self.unwrap_count
147                        ),
148                        format!(
149                            "{} unwrap()s - less stable than my love life",
150                            self.unwrap_count
151                        ),
152                        format!(
153                            "unwrap() abuse alert: {} potential crash points",
154                            self.unwrap_count
155                        ),
156                        format!(
157                            "{} unwrap()s = {} potential panics! Fix immediately!",
158                            self.unwrap_count, self.unwrap_count
159                        ),
160                    ]
161                };
162                (
163                    Severity::Nuclear,
164                    messages[self.issues.len() % messages.len()].clone(),
165                )
166            } else if self.unwrap_count > 8 {
167                let messages = if self.lang == "zh-CN" {
168                    vec![
169                        format!(
170                            "⚠️ {} 个 unwrap(),建议用 ? 运算符或 match 替代",
171                            self.unwrap_count
172                        ),
173                        format!("发现 {} 个 unwrap(),错误处理在哪里?", self.unwrap_count),
174                        format!("{} 个 unwrap(),程序健壮性堪忧", self.unwrap_count),
175                        format!("这么多 unwrap(),Rust 的类型系统在哭泣",),
176                    ]
177                } else {
178                    vec![
179                        format!(
180                            "⚠️ {} unwrap()s - consider using ? or match",
181                            self.unwrap_count
182                        ),
183                        format!(
184                            "Found {} unwrap()s - where's error handling?",
185                            self.unwrap_count
186                        ),
187                        format!(
188                            "{} unwrap()s - program robustness questionable",
189                            self.unwrap_count
190                        ),
191                        format!("So many unwrap()s, Rust's type system is crying",),
192                    ]
193                };
194                (
195                    Severity::Spicy,
196                    messages[self.issues.len() % messages.len()].clone(),
197                )
198            } else if self.unwrap_count > 3 {
199                let messages = if self.lang == "zh-CN" {
200                    vec![
201                        format!(
202                            "{} 个 unwrap(),建议使用 ? 运算符或 match 处理错误",
203                            self.unwrap_count
204                        ),
205                        format!(
206                            "发现 {} 个 unwrap(),考虑用 expect() 至少给个错误信息",
207                            self.unwrap_count
208                        ),
209                        format!(
210                            "{} 个 unwrap() 调用,可以用 .ok()? 或 .map_err()? 替代",
211                            self.unwrap_count
212                        ),
213                    ]
214                } else {
215                    vec![
216                        format!(
217                            "{} unwrap()s - consider using ? operator or match",
218                            self.unwrap_count
219                        ),
220                        format!(
221                            "Found {} unwrap()s - at least use expect() for error messages",
222                            self.unwrap_count
223                        ),
224                        format!(
225                            "{} unwrap() calls - can use .ok()? or .map_err()?",
226                            self.unwrap_count
227                        ),
228                    ]
229                };
230                (
231                    Severity::Mild,
232                    messages[self.issues.len() % messages.len()].clone(),
233                )
234            } else {
235                // 1-3 unwraps: just a gentle reminder
236                let messages = if self.lang == "zh-CN" {
237                    vec![
238                        format!(
239                            "{} 个 unwrap(),小提醒:生产代码建议用 ? 运算符",
240                            self.unwrap_count
241                        ),
242                        format!(
243                            "检测到 {} 个 unwrap(),记得处理可能的 None/Err",
244                            self.unwrap_count
245                        ),
246                    ]
247                } else {
248                    vec![
249                        format!(
250                            "{} unwrap()s - reminder: use ? in production code",
251                            self.unwrap_count
252                        ),
253                        format!(
254                            "Detected {} unwrap()s - remember to handle None/Err",
255                            self.unwrap_count
256                        ),
257                    ]
258                };
259                (
260                    Severity::Mild,
261                    messages[self.issues.len() % messages.len()].clone(),
262                )
263            };
264
265            self.issues.push(CodeIssue {
266                file_path: self.file_path.clone(),
267                line,
268                column,
269                rule_name: "unwrap-abuse".to_string(),
270                message,
271                severity,
272            });
273        }
274    }
275}
276
277struct CloneVisitor {
278    file_path: std::path::PathBuf,
279    issues: Vec<CodeIssue>,
280    clone_count: usize,
281    lang: String,
282}
283
284impl CloneVisitor {
285    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
286        Self {
287            file_path,
288            issues: Vec::new(),
289            clone_count: 0,
290            lang: lang.to_string(),
291        }
292    }
293}
294
295impl<'ast> Visit<'ast> for CloneVisitor {
296    fn visit_expr_method_call(&mut self, method_call: &'ast ExprMethodCall) {
297        if method_call.method == "clone" {
298            self.clone_count += 1;
299
300            // Report once when clone count reaches threshold (>=25)
301            // Increased from 15 to avoid false positives in rule definitions
302            // (each CodeIssue creation uses .clone() for file_path)
303            if self.clone_count >= 25 && self.issues.is_empty() {
304                let messages = if self.lang == "zh-CN" {
305                    [
306                        "clone() 狂魔!你是想把内存用完吗?",
307                        "这么多 clone(),你确定不是在写 Java?",
308                        "clone() 使用过度!Rust 的借用检查器在哭泣",
309                        "又见 clone()!也许你需要重新学习一下 Rust 的所有权系统",
310                        "这些 clone() 让我想起了复印机店的老板",
311                    ]
312                } else {
313                    [
314                        "clone() fanatic! Trying to use up all the memory?",
315                        "This many clone()s - are you sure you're not writing Java?",
316                        "clone() abuse! Rust's borrow checker is crying",
317                        "clone() again! Maybe you need to relearn Rust's ownership system",
318                        "These clone()s remind me of a copy shop owner",
319                    ]
320                };
321
322                let (line, column) = get_position(method_call);
323
324                self.issues.push(CodeIssue {
325                    file_path: self.file_path.clone(),
326                    line,
327                    column,
328                    rule_name: "unnecessary-clone".to_string(),
329                    message: messages[self.issues.len() % messages.len()].to_string(),
330                    severity: Severity::Spicy,
331                });
332            }
333        }
334
335        syn::visit::visit_expr_method_call(self, method_call);
336    }
337}