Skip to main content

garbage_code_hunter/rules/
advanced_rust.rs

1use std::path::Path;
2use syn::{visit::Visit, ExprClosure, File, GenericParam, ItemImpl, ItemTrait, Lifetime};
3
4use crate::analyzer::{CodeIssue, Severity};
5use crate::rules::Rule;
6use crate::utils::get_position;
7
8pub struct ComplexClosureRule;
9
10impl Rule for ComplexClosureRule {
11    fn name(&self) -> &'static str {
12        "complex-closure"
13    }
14
15    fn check(
16        &self,
17        file_path: &Path,
18        syntax_tree: &File,
19        _content: &str,
20        lang: &str,
21        is_test_file: bool,
22    ) -> Vec<CodeIssue> {
23        if is_test_file {
24            return Vec::new();
25        }
26        let mut visitor = ClosureVisitor::new(file_path.to_path_buf(), lang);
27        visitor.visit_file(syntax_tree);
28        visitor.issues
29    }
30}
31
32pub struct LifetimeAbuseRule;
33
34impl Rule for LifetimeAbuseRule {
35    fn name(&self) -> &'static str {
36        "lifetime-abuse"
37    }
38
39    fn check(
40        &self,
41        file_path: &Path,
42        syntax_tree: &File,
43        _content: &str,
44        lang: &str,
45        is_test_file: bool,
46    ) -> Vec<CodeIssue> {
47        if is_test_file {
48            return Vec::new();
49        }
50
51        let mut visitor = LifetimeVisitor::new(file_path.to_path_buf(), lang);
52        visitor.visit_file(syntax_tree);
53        visitor.issues
54    }
55}
56
57pub struct TraitComplexityRule;
58
59impl Rule for TraitComplexityRule {
60    fn name(&self) -> &'static str {
61        "trait-complexity"
62    }
63
64    fn check(
65        &self,
66        file_path: &Path,
67        syntax_tree: &File,
68        _content: &str,
69        lang: &str,
70        is_test_file: bool,
71    ) -> Vec<CodeIssue> {
72        if is_test_file {
73            return Vec::new();
74        }
75        let mut visitor = TraitVisitor::new(file_path.to_path_buf(), lang);
76        visitor.visit_file(syntax_tree);
77        visitor.issues
78    }
79}
80
81pub struct GenericAbuseRule;
82
83impl Rule for GenericAbuseRule {
84    fn name(&self) -> &'static str {
85        "generic-abuse"
86    }
87
88    fn check(
89        &self,
90        file_path: &Path,
91        syntax_tree: &File,
92        _content: &str,
93        lang: &str,
94        is_test_file: bool,
95    ) -> Vec<CodeIssue> {
96        if is_test_file {
97            return Vec::new();
98        }
99        let mut visitor = GenericVisitor::new(file_path.to_path_buf(), lang);
100        visitor.visit_file(syntax_tree);
101        visitor.issues
102    }
103}
104
105struct ClosureVisitor {
106    file_path: std::path::PathBuf,
107    issues: Vec<CodeIssue>,
108    closure_depth: usize,
109    lang: String,
110}
111
112impl ClosureVisitor {
113    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
114        Self {
115            file_path,
116            issues: Vec::new(),
117            closure_depth: 0,
118            lang: lang.to_string(),
119        }
120    }
121
122    fn check_closure_complexity(&mut self, closure: &ExprClosure) {
123        // Check for nested closures
124        if self.closure_depth > 2 {
125            let messages = if self.lang == "zh-CN" {
126                vec![
127                    "闭包套闭包?你这是在写俄罗斯套娃还是在考验读者的智商?",
128                    "嵌套闭包比我的人际关系还复杂",
129                    "这闭包嵌套得像洋葱一样,剥一层哭一次",
130                    "闭包嵌套过深,建议拆分成独立函数",
131                ]
132            } else {
133                vec![
134                    "Closures within closures? Are you writing Russian nesting dolls or testing our IQ?",
135                    "Nested closures are more complex than my relationships",
136                    "These closures are nested like an onion - peel one layer, cry once",
137                    "Closures too deeply nested - consider splitting into separate functions",
138                ]
139            };
140
141            let (line, column) = get_position(closure);
142            self.issues.push(CodeIssue {
143                file_path: self.file_path.clone(),
144                line,
145                column,
146                rule_name: "complex-closure".to_string(),
147                message: messages[self.issues.len() % messages.len()].to_string(),
148                severity: Severity::Spicy,
149            });
150        }
151
152        // Check for complex closure parameters
153        if closure.inputs.len() > 5 {
154            let messages = if self.lang == "zh-CN" {
155                vec![
156                    "这个闭包的参数比我的借口还多",
157                    "闭包参数过多,你确定不是在写函数?",
158                    "这么多参数的闭包,建议改成正经函数",
159                ]
160            } else {
161                vec![
162                    "This closure has more parameters than my excuses",
163                    "Too many parameters for a closure - are you sure this isn't a function?",
164                    "So many parameters - consider making this a proper function",
165                ]
166            };
167
168            let (line, column) = get_position(closure);
169            self.issues.push(CodeIssue {
170                file_path: self.file_path.clone(),
171                line,
172                column,
173                rule_name: "complex-closure".to_string(),
174                message: messages[self.issues.len() % messages.len()].to_string(),
175                severity: Severity::Mild,
176            });
177        }
178    }
179}
180
181impl<'ast> Visit<'ast> for ClosureVisitor {
182    fn visit_expr_closure(&mut self, closure: &'ast ExprClosure) {
183        self.closure_depth += 1;
184        self.check_closure_complexity(closure);
185        syn::visit::visit_expr_closure(self, closure);
186        self.closure_depth -= 1;
187    }
188}
189
190struct LifetimeVisitor {
191    file_path: std::path::PathBuf,
192    issues: Vec<CodeIssue>,
193    lifetime_count: usize,
194    lang: String,
195}
196
197impl LifetimeVisitor {
198    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
199        Self {
200            file_path,
201            issues: Vec::new(),
202            lifetime_count: 0,
203            lang: lang.to_string(),
204        }
205    }
206}
207
208impl<'ast> Visit<'ast> for LifetimeVisitor {
209    fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) {
210        // Skip 'static lifetime
211        if lifetime.ident == "static" {
212            return;
213        }
214
215        self.lifetime_count += 1;
216
217        // Check for excessive lifetime usage, cap to 1 issue per file
218        // Increased threshold: 20+ to avoid false positives in trait implementations
219        // (each syn::Visit impl requires 'ast lifetime)
220        if self.lifetime_count > 20 && self.issues.is_empty() {
221            let messages = if self.lang == "zh-CN" {
222                vec![
223                    "生命周期标注比我的生命还复杂",
224                    "这么多生命周期,你是在写哲学论文吗?",
225                    "生命周期滥用,建议重新设计数据结构",
226                    "生命周期多到让人怀疑人生",
227                ]
228            } else {
229                vec![
230                    "Lifetime annotations are more complex than my life",
231                    "So many lifetimes - are you writing a philosophy paper?",
232                    "Lifetime abuse - consider redesigning your data structure",
233                    "Too many lifetimes - making people question their existence",
234                ]
235            };
236
237            let (line, column) = get_position(lifetime);
238            self.issues.push(CodeIssue {
239                file_path: self.file_path.clone(),
240                line,
241                column,
242                rule_name: "lifetime-abuse".to_string(),
243                message: messages[self.issues.len() % messages.len()].to_string(),
244                severity: Severity::Spicy,
245            });
246        }
247
248        syn::visit::visit_lifetime(self, lifetime);
249    }
250}
251
252struct TraitVisitor {
253    file_path: std::path::PathBuf,
254    issues: Vec<CodeIssue>,
255    lang: String,
256}
257
258impl TraitVisitor {
259    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
260        Self {
261            file_path,
262            issues: Vec::new(),
263            lang: lang.to_string(),
264        }
265    }
266
267    fn check_trait_complexity(&mut self, trait_item: &ItemTrait) {
268        // Check for traits with too many methods
269        if trait_item.items.len() > 10 {
270            let messages = if self.lang == "zh-CN" {
271                vec![
272                    "这个 trait 的方法比我的借口还多",
273                    "trait 方法过多,违反了单一职责原则",
274                    "这个 trait 比瑞士军刀还要全能",
275                    "trait 臃肿,建议拆分成多个小 trait",
276                ]
277            } else {
278                vec![
279                    "This trait has more methods than my excuses",
280                    "Too many trait methods - violates single responsibility principle",
281                    "This trait is more versatile than a Swiss Army knife",
282                    "Bloated trait - consider splitting into smaller traits",
283                ]
284            };
285
286            let (line, column) = get_position(trait_item);
287            self.issues.push(CodeIssue {
288                file_path: self.file_path.clone(),
289                line,
290                column,
291                rule_name: "trait-complexity".to_string(),
292                message: messages[self.issues.len() % messages.len()].to_string(),
293                severity: Severity::Spicy,
294            });
295        }
296
297        // Check for traits with too many generic parameters
298        if trait_item.generics.params.len() > 3 {
299            let messages = if self.lang == "zh-CN" {
300                vec![
301                    "泛型参数比我的密码还复杂",
302                    "这么多泛型,你是在写数学公式吗?",
303                    "泛型滥用,建议简化设计",
304                ]
305            } else {
306                vec![
307                    "Generic parameters are more complex than my password",
308                    "So many generics - are you writing a math formula?",
309                    "Generic abuse - simplify your design",
310                ]
311            };
312
313            let (line, column) = get_position(trait_item);
314            self.issues.push(CodeIssue {
315                file_path: self.file_path.clone(),
316                line,
317                column,
318                rule_name: "trait-complexity".to_string(),
319                message: messages[self.issues.len() % messages.len()].to_string(),
320                severity: Severity::Mild,
321            });
322        }
323    }
324}
325
326impl<'ast> Visit<'ast> for TraitVisitor {
327    fn visit_item_trait(&mut self, trait_item: &'ast ItemTrait) {
328        self.check_trait_complexity(trait_item);
329        syn::visit::visit_item_trait(self, trait_item);
330    }
331}
332
333struct GenericVisitor {
334    file_path: std::path::PathBuf,
335    issues: Vec<CodeIssue>,
336    lang: String,
337}
338
339impl GenericVisitor {
340    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
341        Self {
342            file_path,
343            issues: Vec::new(),
344            lang: lang.to_string(),
345        }
346    }
347
348    fn check_generic_abuse(&mut self, generics: &syn::Generics) {
349        if generics.params.len() > 5 {
350            let messages = if self.lang == "zh-CN" {
351                vec![
352                    "泛型参数比我的购物清单还长",
353                    "这么多泛型,编译器都要哭了",
354                    "泛型滥用,建议重新设计架构",
355                    "泛型多到让人怀疑这还是 Rust 吗",
356                ]
357            } else {
358                vec![
359                    "Generic parameters are longer than my shopping list",
360                    "So many generics - even the compiler is crying",
361                    "Generic abuse - consider redesigning your architecture",
362                    "So many generics - making people question if this is still Rust",
363                ]
364            };
365
366            let (line, column) = get_position(generics);
367            self.issues.push(CodeIssue {
368                file_path: self.file_path.clone(),
369                line,
370                column,
371                rule_name: "generic-abuse".to_string(),
372                message: messages[self.issues.len() % messages.len()].to_string(),
373                severity: Severity::Spicy,
374            });
375        }
376
377        // Check for single-letter generic names (except T, U, V)
378        for param in &generics.params {
379            if let GenericParam::Type(type_param) = param {
380                let name = type_param.ident.to_string();
381                if name.len() == 1 && !matches!(name.as_str(), "T" | "U" | "V" | "E" | "K") {
382                    let messages = if self.lang == "zh-CN" {
383                        vec![
384                            format!("泛型参数 '{name}' 的命名创意约等于零"),
385                            format!("泛型 '{name}' 的名字比我的耐心还短"),
386                            format!("用 '{name}' 做泛型名?建议用更有意义的名字"),
387                        ]
388                    } else {
389                        vec![
390                            format!("Generic parameter '{name}' has approximately zero creativity"),
391                            format!("Generic '{name}' is shorter than my patience"),
392                            format!("Using '{name}' as a generic name? Consider something more meaningful"),
393                        ]
394                    };
395
396                    let (line, column) = get_position(type_param);
397                    self.issues.push(CodeIssue {
398                        file_path: self.file_path.clone(),
399                        line,
400                        column,
401                        rule_name: "generic-abuse".to_string(),
402                        message: messages[self.issues.len() % messages.len()].clone(),
403                        severity: Severity::Mild,
404                    });
405                }
406            }
407        }
408    }
409}
410
411impl<'ast> Visit<'ast> for GenericVisitor {
412    fn visit_item_fn(&mut self, func: &'ast syn::ItemFn) {
413        self.check_generic_abuse(&func.sig.generics);
414        syn::visit::visit_item_fn(self, func);
415    }
416
417    fn visit_item_struct(&mut self, struct_item: &'ast syn::ItemStruct) {
418        self.check_generic_abuse(&struct_item.generics);
419        syn::visit::visit_item_struct(self, struct_item);
420    }
421
422    fn visit_item_enum(&mut self, enum_item: &'ast syn::ItemEnum) {
423        self.check_generic_abuse(&enum_item.generics);
424        syn::visit::visit_item_enum(self, enum_item);
425    }
426
427    fn visit_item_impl(&mut self, impl_item: &'ast ItemImpl) {
428        self.check_generic_abuse(&impl_item.generics);
429        syn::visit::visit_item_impl(self, impl_item);
430    }
431}