garbage_code_hunter/rules/
comprehensive_rust.rs

1use std::path::Path;
2use syn::{
3    visit::Visit, ExprAsync, ExprAwait, ExprMatch, ExprUnsafe, File, ForeignItem, ItemFn,
4    ItemForeignMod, ItemMod, Macro, PatSlice, PatTuple, TypePath, TypeReference, TypeSlice,
5    TypeTraitObject,
6};
7
8use crate::analyzer::{CodeIssue, RoastLevel, Severity};
9use crate::rules::Rule;
10
11pub struct ChannelAbuseRule;
12pub struct AsyncAbuseRule;
13pub struct DynTraitAbuseRule;
14pub struct UnsafeAbuseRule;
15pub struct FFIAbuseRule;
16pub struct MacroAbuseRule;
17pub struct ModuleComplexityRule;
18pub struct PatternMatchingAbuseRule;
19pub struct ReferenceAbuseRule;
20pub struct BoxAbuseRule;
21pub struct SliceAbuseRule;
22
23impl Rule for ChannelAbuseRule {
24    fn name(&self) -> &'static str {
25        "channel-abuse"
26    }
27
28    fn check(
29        &self,
30        file_path: &Path,
31        syntax_tree: &File,
32        content: &str,
33        _lang: &str,
34    ) -> Vec<CodeIssue> {
35        let mut visitor = ChannelVisitor::new(file_path.to_path_buf());
36        visitor.visit_file(syntax_tree);
37
38        // Also check for channel-related imports and usage in content
39        if content.contains("std::sync::mpsc") || content.contains("tokio::sync") {
40            visitor.channel_count += content.matches("channel").count();
41            visitor.channel_count += content.matches("Sender").count();
42            visitor.channel_count += content.matches("Receiver").count();
43        }
44
45        visitor.check_channel_overuse();
46        visitor.issues
47    }
48}
49
50impl Rule for AsyncAbuseRule {
51    fn name(&self) -> &'static str {
52        "async-abuse"
53    }
54
55    fn check(
56        &self,
57        file_path: &Path,
58        syntax_tree: &File,
59        _content: &str,
60        _lang: &str,
61    ) -> Vec<CodeIssue> {
62        let mut visitor = AsyncVisitor::new(file_path.to_path_buf());
63        visitor.visit_file(syntax_tree);
64        visitor.issues
65    }
66}
67
68impl Rule for DynTraitAbuseRule {
69    fn name(&self) -> &'static str {
70        "dyn-trait-abuse"
71    }
72
73    fn check(
74        &self,
75        file_path: &Path,
76        syntax_tree: &File,
77        _content: &str,
78        _lang: &str,
79    ) -> Vec<CodeIssue> {
80        let mut visitor = DynTraitVisitor::new(file_path.to_path_buf());
81        visitor.visit_file(syntax_tree);
82        visitor.issues
83    }
84}
85
86impl Rule for UnsafeAbuseRule {
87    fn name(&self) -> &'static str {
88        "unsafe-abuse"
89    }
90
91    fn check(
92        &self,
93        file_path: &Path,
94        syntax_tree: &File,
95        content: &str,
96        _lang: &str,
97    ) -> Vec<CodeIssue> {
98        let mut visitor = UnsafeVisitor::new(file_path.to_path_buf());
99        visitor.visit_file(syntax_tree);
100
101        // 检查内容中的 unsafe 关键字使用
102        visitor.check_unsafe_in_content(content);
103        visitor.issues
104    }
105}
106
107impl Rule for FFIAbuseRule {
108    fn name(&self) -> &'static str {
109        "ffi-abuse"
110    }
111
112    fn check(
113        &self,
114        file_path: &Path,
115        syntax_tree: &File,
116        content: &str,
117        _lang: &str,
118    ) -> Vec<CodeIssue> {
119        let mut visitor = FFIVisitor::new(file_path.to_path_buf());
120        visitor.visit_file(syntax_tree);
121
122        // 检查内容中的 FFI 相关模式
123        visitor.check_ffi_patterns_in_content(content);
124        visitor.issues
125    }
126}
127
128impl Rule for MacroAbuseRule {
129    fn name(&self) -> &'static str {
130        "macro-abuse"
131    }
132
133    fn check(
134        &self,
135        file_path: &Path,
136        syntax_tree: &File,
137        _content: &str,
138        _lang: &str,
139    ) -> Vec<CodeIssue> {
140        let mut visitor = MacroVisitor::new(file_path.to_path_buf());
141        visitor.visit_file(syntax_tree);
142        visitor.issues
143    }
144}
145
146impl Rule for ModuleComplexityRule {
147    fn name(&self) -> &'static str {
148        "module-complexity"
149    }
150
151    fn check(
152        &self,
153        file_path: &Path,
154        syntax_tree: &File,
155        _content: &str,
156        _lang: &str,
157    ) -> Vec<CodeIssue> {
158        let mut visitor = ModuleVisitor::new(file_path.to_path_buf());
159        visitor.visit_file(syntax_tree);
160        visitor.issues
161    }
162}
163
164impl Rule for PatternMatchingAbuseRule {
165    fn name(&self) -> &'static str {
166        "pattern-matching-abuse"
167    }
168
169    fn check(
170        &self,
171        file_path: &Path,
172        syntax_tree: &File,
173        _content: &str,
174        _lang: &str,
175    ) -> Vec<CodeIssue> {
176        let mut visitor = PatternVisitor::new(file_path.to_path_buf());
177        visitor.visit_file(syntax_tree);
178        visitor.issues
179    }
180}
181
182impl Rule for ReferenceAbuseRule {
183    fn name(&self) -> &'static str {
184        "reference-abuse"
185    }
186
187    fn check(
188        &self,
189        file_path: &Path,
190        syntax_tree: &File,
191        _content: &str,
192        _lang: &str,
193    ) -> Vec<CodeIssue> {
194        let mut visitor = ReferenceVisitor::new(file_path.to_path_buf());
195        visitor.visit_file(syntax_tree);
196        visitor.issues
197    }
198}
199
200impl Rule for BoxAbuseRule {
201    fn name(&self) -> &'static str {
202        "box-abuse"
203    }
204
205    fn check(
206        &self,
207        file_path: &Path,
208        syntax_tree: &File,
209        content: &str,
210        _lang: &str,
211    ) -> Vec<CodeIssue> {
212        let mut visitor = BoxVisitor::new(file_path.to_path_buf());
213        visitor.visit_file(syntax_tree);
214
215        // Check for Box usage in content since ExprBox doesn't exist in syn 2.0
216        let box_count = content.matches("Box::new").count() + content.matches("Box<").count();
217        if box_count > 8 {
218            let messages = [
219                "Box 用得比快递还频繁",
220                "这么多 Box,你是在开仓库吗?",
221                "Box 过多,堆内存都要爆炸了",
222                "Box 滥用,建议考虑栈分配",
223                "这么多 Box,内存分配器都累了",
224            ];
225
226            visitor.issues.push(CodeIssue {
227                file_path: file_path.to_path_buf(),
228                line: 1,
229                column: 1,
230                rule_name: "box-abuse".to_string(),
231                message: messages[0].to_string(),
232                severity: Severity::Spicy,
233                roast_level: RoastLevel::Sarcastic,
234            });
235        }
236
237        visitor.issues
238    }
239}
240
241impl Rule for SliceAbuseRule {
242    fn name(&self) -> &'static str {
243        "slice-abuse"
244    }
245
246    fn check(
247        &self,
248        file_path: &Path,
249        syntax_tree: &File,
250        _content: &str,
251        _lang: &str,
252    ) -> Vec<CodeIssue> {
253        let mut visitor = SliceVisitor::new(file_path.to_path_buf());
254        visitor.visit_file(syntax_tree);
255        visitor.issues
256    }
257}
258
259// Channel Visitor
260struct ChannelVisitor {
261    file_path: std::path::PathBuf,
262    issues: Vec<CodeIssue>,
263    channel_count: usize,
264}
265
266impl ChannelVisitor {
267    fn new(file_path: std::path::PathBuf) -> Self {
268        Self {
269            file_path,
270            issues: Vec::new(),
271            channel_count: 0,
272        }
273    }
274
275    fn check_channel_overuse(&mut self) {
276        if self.channel_count > 5 {
277            let messages = [
278                "Channel 用得比我发微信还频繁,你确定不是在写聊天软件?",
279                "这么多 Channel,你是想开通讯公司吗?",
280                "Channel 滥用!你的程序比电话交换机还复杂",
281                "Channel 数量超标,建议重新设计架构",
282                "这么多 Channel,我怀疑你在写分布式系统",
283            ];
284
285            self.issues.push(CodeIssue {
286                file_path: self.file_path.clone(),
287                line: 1,
288                column: 1,
289                rule_name: "channel-abuse".to_string(),
290                message: messages[self.issues.len() % messages.len()].to_string(),
291                severity: Severity::Spicy,
292                roast_level: RoastLevel::Sarcastic,
293            });
294        }
295    }
296}
297
298impl<'ast> Visit<'ast> for ChannelVisitor {
299    fn visit_type_path(&mut self, type_path: &'ast TypePath) {
300        let path_str = quote::quote!(#type_path).to_string();
301        if path_str.contains("Sender")
302            || path_str.contains("Receiver")
303            || path_str.contains("channel")
304        {
305            self.channel_count += 1;
306        }
307        syn::visit::visit_type_path(self, type_path);
308    }
309}
310
311// Async Visitor
312struct AsyncVisitor {
313    file_path: std::path::PathBuf,
314    issues: Vec<CodeIssue>,
315    async_count: usize,
316    await_count: usize,
317}
318
319impl AsyncVisitor {
320    fn new(file_path: std::path::PathBuf) -> Self {
321        Self {
322            file_path,
323            issues: Vec::new(),
324            async_count: 0,
325            await_count: 0,
326        }
327    }
328
329    fn check_async_abuse(&mut self) {
330        if self.async_count > 10 {
331            let messages = [
332                "Async 函数比我的异步人生还要复杂",
333                "这么多 async,你确定不是在写 JavaScript?",
334                "Async 滥用!建议学习一下同步编程的美好",
335                "异步函数过多,小心把自己绕晕了",
336            ];
337
338            self.issues.push(CodeIssue {
339                file_path: self.file_path.clone(),
340                line: 1,
341                column: 1,
342                rule_name: "async-abuse".to_string(),
343                message: messages[self.issues.len() % messages.len()].to_string(),
344                severity: Severity::Spicy,
345                roast_level: RoastLevel::Sarcastic,
346            });
347        }
348
349        if self.await_count > 20 {
350            let messages = [
351                "Await 用得比我等外卖还频繁",
352                "这么多 await,你的程序是在等什么?世界末日吗?",
353                "Await 过度使用,建议批量处理",
354                "等待次数过多,你的程序比我还有耐心",
355            ];
356
357            self.issues.push(CodeIssue {
358                file_path: self.file_path.clone(),
359                line: 1,
360                column: 1,
361                rule_name: "async-abuse".to_string(),
362                message: messages[self.issues.len() % messages.len()].to_string(),
363                severity: Severity::Mild,
364                roast_level: RoastLevel::Gentle,
365            });
366        }
367    }
368}
369
370impl<'ast> Visit<'ast> for AsyncVisitor {
371    fn visit_expr_async(&mut self, _async_expr: &'ast ExprAsync) {
372        self.async_count += 1;
373        self.check_async_abuse();
374        syn::visit::visit_expr_async(self, _async_expr);
375    }
376
377    fn visit_expr_await(&mut self, _await_expr: &'ast ExprAwait) {
378        self.await_count += 1;
379        self.check_async_abuse();
380        syn::visit::visit_expr_await(self, _await_expr);
381    }
382}
383
384// Dyn Trait Visitor
385struct DynTraitVisitor {
386    file_path: std::path::PathBuf,
387    issues: Vec<CodeIssue>,
388    dyn_count: usize,
389}
390
391impl DynTraitVisitor {
392    fn new(file_path: std::path::PathBuf) -> Self {
393        Self {
394            file_path,
395            issues: Vec::new(),
396            dyn_count: 0,
397        }
398    }
399}
400
401impl<'ast> Visit<'ast> for DynTraitVisitor {
402    fn visit_type_trait_object(&mut self, trait_object: &'ast TypeTraitObject) {
403        self.dyn_count += 1;
404
405        if self.dyn_count > 5 {
406            let messages = [
407                "Dyn trait 用得比我换工作还频繁",
408                "这么多动态分发,性能都跑到哪里去了?",
409                "Dyn trait 滥用,你确定不是在写 Python?",
410                "动态 trait 过多,编译器优化都哭了",
411                "这么多 dyn,你的程序比变色龙还善变",
412            ];
413
414            self.issues.push(CodeIssue {
415                file_path: self.file_path.clone(),
416                line: 1,
417                column: 1,
418                rule_name: "dyn-trait-abuse".to_string(),
419                message: messages[self.issues.len() % messages.len()].to_string(),
420                severity: Severity::Spicy,
421                roast_level: RoastLevel::Sarcastic,
422            });
423        }
424
425        syn::visit::visit_type_trait_object(self, trait_object);
426    }
427}
428
429// Unsafe Visitor
430struct UnsafeVisitor {
431    file_path: std::path::PathBuf,
432    issues: Vec<CodeIssue>,
433    unsafe_count: usize,
434    unsafe_fn_count: usize,
435    unsafe_impl_count: usize,
436    unsafe_trait_count: usize,
437}
438
439impl UnsafeVisitor {
440    fn new(file_path: std::path::PathBuf) -> Self {
441        Self {
442            file_path,
443            issues: Vec::new(),
444            unsafe_count: 0,
445            unsafe_fn_count: 0,
446            unsafe_impl_count: 0,
447            unsafe_trait_count: 0,
448        }
449    }
450
451    fn check_unsafe_in_content(&mut self, content: &str) {
452        // 检查 unsafe 函数
453        let unsafe_fn_matches = content.matches("unsafe fn").count();
454        self.unsafe_fn_count += unsafe_fn_matches;
455
456        // 检查 unsafe impl
457        let unsafe_impl_matches = content.matches("unsafe impl").count();
458        self.unsafe_impl_count += unsafe_impl_matches;
459
460        // 检查 unsafe trait
461        let unsafe_trait_matches = content.matches("unsafe trait").count();
462        self.unsafe_trait_count += unsafe_trait_matches;
463
464        // 检查原始指针操作
465        let raw_ptr_count = content.matches("*const").count() + content.matches("*mut").count();
466
467        // 检查内存操作函数
468        let dangerous_ops = [
469            "std::ptr::write",
470            "std::ptr::read",
471            "std::ptr::copy",
472            "std::mem::transmute",
473            "std::mem::forget",
474            "std::mem::uninitialized",
475            "std::slice::from_raw_parts",
476            "std::str::from_utf8_unchecked",
477            "Box::from_raw",
478            "Vec::from_raw_parts",
479            "String::from_raw_parts",
480        ];
481
482        let mut dangerous_op_count = 0;
483        for op in &dangerous_ops {
484            dangerous_op_count += content.matches(op).count();
485        }
486
487        self.generate_unsafe_issues(raw_ptr_count, dangerous_op_count);
488    }
489
490    fn generate_unsafe_issues(&mut self, raw_ptr_count: usize, dangerous_op_count: usize) {
491        // 检查 unsafe 函数过多
492        if self.unsafe_fn_count > 2 {
493            let messages = [
494                "Unsafe 函数比我的黑历史还多!你确定这还是 Rust 吗?",
495                "这么多 unsafe 函数,Rust 的安全保证都被你玩坏了",
496                "Unsafe 函数过多,建议重新考虑设计架构",
497                "你的 unsafe 函数让 Rust 编译器都开始怀疑人生了",
498            ];
499
500            self.issues.push(CodeIssue {
501                file_path: self.file_path.clone(),
502                line: 1,
503                column: 1,
504                rule_name: "unsafe-abuse".to_string(),
505                message: messages[self.issues.len() % messages.len()].to_string(),
506                severity: Severity::Nuclear,
507                roast_level: RoastLevel::Savage,
508            });
509        }
510
511        // 检查原始指针过多
512        if raw_ptr_count > 5 {
513            let messages = [
514                "原始指针用得比我换手机还频繁,你这是在写 C 语言吗?",
515                "这么多原始指针,内存安全已经不在服务区了",
516                "原始指针过多,建议使用安全的 Rust 抽象",
517                "你的指针操作让 Valgrind 都要加班了",
518            ];
519
520            self.issues.push(CodeIssue {
521                file_path: self.file_path.clone(),
522                line: 1,
523                column: 1,
524                rule_name: "unsafe-abuse".to_string(),
525                message: messages[self.issues.len() % messages.len()].to_string(),
526                severity: Severity::Nuclear,
527                roast_level: RoastLevel::Savage,
528            });
529        }
530
531        // 检查危险操作过多
532        if dangerous_op_count > 3 {
533            let messages = [
534                "危险的内存操作比我的危险驾驶还要多!",
535                "这些危险操作让我想起了 C++ 的恐怖回忆",
536                "内存操作过于危险,建议使用安全替代方案",
537                "你的代码比走钢丝还危险,小心内存泄漏!",
538            ];
539
540            self.issues.push(CodeIssue {
541                file_path: self.file_path.clone(),
542                line: 1,
543                column: 1,
544                rule_name: "unsafe-abuse".to_string(),
545                message: messages[self.issues.len() % messages.len()].to_string(),
546                severity: Severity::Nuclear,
547                roast_level: RoastLevel::Savage,
548            });
549        }
550    }
551}
552
553impl<'ast> Visit<'ast> for UnsafeVisitor {
554    fn visit_expr_unsafe(&mut self, _unsafe_expr: &'ast ExprUnsafe) {
555        self.unsafe_count += 1;
556
557        let messages = [
558            "Unsafe 代码!你这是在玩火还是在挑战 Rust 的底线?",
559            "又见 unsafe!安全性是什么?能吃吗?",
560            "Unsafe 使用者,恭喜你获得了'内存安全破坏者'称号",
561            "这个 unsafe 让我想起了 C 语言的恐怖回忆",
562            "Unsafe 代码:让 Rust 程序员夜不能寐的存在",
563        ];
564
565        let severity = if self.unsafe_count > 3 {
566            Severity::Nuclear
567        } else {
568            Severity::Spicy
569        };
570
571        self.issues.push(CodeIssue {
572            file_path: self.file_path.clone(),
573            line: 1,
574            column: 1,
575            rule_name: "unsafe-abuse".to_string(),
576            message: messages[self.issues.len() % messages.len()].to_string(),
577            severity,
578            roast_level: RoastLevel::Savage,
579        });
580
581        syn::visit::visit_expr_unsafe(self, _unsafe_expr);
582    }
583
584    fn visit_item_fn(&mut self, item_fn: &'ast ItemFn) {
585        if item_fn.sig.unsafety.is_some() {
586            self.unsafe_fn_count += 1;
587        }
588        syn::visit::visit_item_fn(self, item_fn);
589    }
590}
591
592// FFI Visitor
593struct FFIVisitor {
594    file_path: std::path::PathBuf,
595    issues: Vec<CodeIssue>,
596    extern_block_count: usize,
597    extern_fn_count: usize,
598    c_repr_count: usize,
599}
600
601impl FFIVisitor {
602    fn new(file_path: std::path::PathBuf) -> Self {
603        Self {
604            file_path,
605            issues: Vec::new(),
606            extern_block_count: 0,
607            extern_fn_count: 0,
608            c_repr_count: 0,
609        }
610    }
611
612    fn check_ffi_patterns_in_content(&mut self, content: &str) {
613        // 检查 C 表示法
614        self.c_repr_count += content.matches("#[repr(C)]").count();
615
616        // 检查 C 字符串操作
617        let c_string_ops = [
618            "CString",
619            "CStr",
620            "c_char",
621            "c_void",
622            "c_int",
623            "c_long",
624            "std::ffi::",
625            "libc::",
626            "std::os::raw::",
627        ];
628
629        let mut c_ops_count = 0;
630        for op in &c_string_ops {
631            c_ops_count += content.matches(op).count();
632        }
633
634        // 检查动态库加载
635        let dll_ops = ["libloading", "dlopen", "LoadLibrary", "GetProcAddress"];
636        let mut dll_count = 0;
637        for op in &dll_ops {
638            dll_count += content.matches(op).count();
639        }
640
641        self.generate_ffi_issues(c_ops_count, dll_count);
642    }
643
644    fn generate_ffi_issues(&mut self, c_ops_count: usize, dll_count: usize) {
645        // 检查 extern 块过多
646        if self.extern_block_count > 2 {
647            let messages = [
648                "Extern 块比我的前任还多,你这是要和多少种语言交互?",
649                "这么多 extern 块,你确定不是在写多语言翻译器?",
650                "FFI 接口过多,建议封装成统一的抽象层",
651                "外部接口比我的社交关系还复杂!",
652            ];
653
654            self.issues.push(CodeIssue {
655                file_path: self.file_path.clone(),
656                line: 1,
657                column: 1,
658                rule_name: "ffi-abuse".to_string(),
659                message: messages[self.issues.len() % messages.len()].to_string(),
660                severity: Severity::Spicy,
661                roast_level: RoastLevel::Sarcastic,
662            });
663        }
664
665        // 检查 C 操作过多
666        if c_ops_count > 10 {
667            let messages = [
668                "C 语言操作比我的 C 语言作业还多,你确定这是 Rust 项目?",
669                "这么多 C FFI,Rust 的安全性都要哭了",
670                "C 接口过多,建议使用更安全的 Rust 绑定",
671                "你的 FFI 代码让我想起了指针地狱的恐怖",
672            ];
673
674            self.issues.push(CodeIssue {
675                file_path: self.file_path.clone(),
676                line: 1,
677                column: 1,
678                rule_name: "ffi-abuse".to_string(),
679                message: messages[self.issues.len() % messages.len()].to_string(),
680                severity: Severity::Nuclear,
681                roast_level: RoastLevel::Savage,
682            });
683        }
684
685        // 检查动态库加载
686        if dll_count > 0 {
687            let messages = [
688                "动态库加载!你这是在运行时玩杂技吗?",
689                "动态加载库,小心加载到病毒!",
690                "运行时库加载,调试的时候准备哭吧",
691                "动态库操作,你的程序比变形金刚还会变身",
692            ];
693
694            self.issues.push(CodeIssue {
695                file_path: self.file_path.clone(),
696                line: 1,
697                column: 1,
698                rule_name: "ffi-abuse".to_string(),
699                message: messages[self.issues.len() % messages.len()].to_string(),
700                severity: Severity::Spicy,
701                roast_level: RoastLevel::Sarcastic,
702            });
703        }
704
705        // 检查 repr(C) 过多
706        if self.c_repr_count > 5 {
707            let messages = [
708                "repr(C) 用得比我说 C 语言还频繁!",
709                "这么多 C 表示法,你的结构体都要移民到 C 语言了",
710                "C 表示法过多,内存布局都要乱套了",
711                "repr(C) 滥用,Rust 的零成本抽象在哭泣",
712            ];
713
714            self.issues.push(CodeIssue {
715                file_path: self.file_path.clone(),
716                line: 1,
717                column: 1,
718                rule_name: "ffi-abuse".to_string(),
719                message: messages[self.issues.len() % messages.len()].to_string(),
720                severity: Severity::Spicy,
721                roast_level: RoastLevel::Sarcastic,
722            });
723        }
724    }
725}
726
727impl<'ast> Visit<'ast> for FFIVisitor {
728    fn visit_item_foreign_mod(&mut self, foreign_mod: &'ast ItemForeignMod) {
729        self.extern_block_count += 1;
730
731        // 统计外部函数数量
732        for item in &foreign_mod.items {
733            if matches!(item, ForeignItem::Fn(_)) {
734                self.extern_fn_count += 1;
735            }
736        }
737
738        // 检查 extern 函数过多
739        if self.extern_fn_count > 10 {
740            let messages = [
741                "外部函数比我的外卖订单还多!",
742                "这么多 extern 函数,你是在开联合国大会吗?",
743                "外部接口过多,建议分模块管理",
744                "FFI 函数数量超标,小心接口管理混乱",
745            ];
746
747            self.issues.push(CodeIssue {
748                file_path: self.file_path.clone(),
749                line: 1,
750                column: 1,
751                rule_name: "ffi-abuse".to_string(),
752                message: messages[self.issues.len() % messages.len()].to_string(),
753                severity: Severity::Spicy,
754                roast_level: RoastLevel::Sarcastic,
755            });
756        }
757
758        syn::visit::visit_item_foreign_mod(self, foreign_mod);
759    }
760
761    fn visit_item_fn(&mut self, item_fn: &'ast ItemFn) {
762        // 检查是否是 extern "C" 函数
763        if let Some(abi) = &item_fn.sig.abi {
764            if let Some(abi_name) = &abi.name {
765                if abi_name.value() == "C" {
766                    self.extern_fn_count += 1;
767                }
768            }
769        }
770        syn::visit::visit_item_fn(self, item_fn);
771    }
772}
773
774// Macro Visitor
775struct MacroVisitor {
776    file_path: std::path::PathBuf,
777    issues: Vec<CodeIssue>,
778    macro_count: usize,
779}
780
781impl MacroVisitor {
782    fn new(file_path: std::path::PathBuf) -> Self {
783        Self {
784            file_path,
785            issues: Vec::new(),
786            macro_count: 0,
787        }
788    }
789}
790
791impl<'ast> Visit<'ast> for MacroVisitor {
792    fn visit_macro(&mut self, _macro: &'ast Macro) {
793        self.macro_count += 1;
794
795        if self.macro_count > 10 {
796            let messages = [
797                "宏定义比我的借口还多",
798                "这么多宏,你确定不是在写 C 语言?",
799                "宏滥用!编译时间都被你搞长了",
800                "宏过多,调试的时候准备哭吧",
801                "这么多宏,IDE 都要罢工了",
802            ];
803
804            self.issues.push(CodeIssue {
805                file_path: self.file_path.clone(),
806                line: 1,
807                column: 1,
808                rule_name: "macro-abuse".to_string(),
809                message: messages[self.issues.len() % messages.len()].to_string(),
810                severity: Severity::Mild,
811                roast_level: RoastLevel::Gentle,
812            });
813        }
814
815        syn::visit::visit_macro(self, _macro);
816    }
817}
818
819// Module Visitor
820struct ModuleVisitor {
821    file_path: std::path::PathBuf,
822    issues: Vec<CodeIssue>,
823    module_depth: usize,
824    max_depth: usize,
825}
826
827impl ModuleVisitor {
828    fn new(file_path: std::path::PathBuf) -> Self {
829        Self {
830            file_path,
831            issues: Vec::new(),
832            module_depth: 0,
833            max_depth: 0,
834        }
835    }
836}
837
838impl<'ast> Visit<'ast> for ModuleVisitor {
839    fn visit_item_mod(&mut self, _module: &'ast ItemMod) {
840        self.module_depth += 1;
841        self.max_depth = self.max_depth.max(self.module_depth);
842
843        if self.module_depth > 5 {
844            let messages = [
845                "模块嵌套比俄罗斯套娃还深",
846                "这模块结构比我的家族关系还复杂",
847                "模块嵌套过深,建议重新组织代码结构",
848                "这么深的模块,找个函数比找宝藏还难",
849            ];
850
851            self.issues.push(CodeIssue {
852                file_path: self.file_path.clone(),
853                line: 1,
854                column: 1,
855                rule_name: "module-complexity".to_string(),
856                message: messages[self.issues.len() % messages.len()].to_string(),
857                severity: Severity::Spicy,
858                roast_level: RoastLevel::Sarcastic,
859            });
860        }
861
862        syn::visit::visit_item_mod(self, _module);
863        self.module_depth -= 1;
864    }
865}
866
867// Pattern Visitor
868struct PatternVisitor {
869    file_path: std::path::PathBuf,
870    issues: Vec<CodeIssue>,
871    complex_pattern_count: usize,
872}
873
874impl PatternVisitor {
875    fn new(file_path: std::path::PathBuf) -> Self {
876        Self {
877            file_path,
878            issues: Vec::new(),
879            complex_pattern_count: 0,
880        }
881    }
882
883    fn check_pattern_complexity(&mut self, pattern_type: &str) {
884        self.complex_pattern_count += 1;
885
886        if self.complex_pattern_count > 15 {
887            let messages = [
888                format!("{pattern_type}模式匹配比我的感情生活还复杂"),
889                format!("这么多{pattern_type}模式,你是在写解谜游戏吗?"),
890                format!("{pattern_type}模式过多,建议简化逻辑"),
891                format!("复杂的{pattern_type}模式让代码可读性直线下降"),
892            ];
893
894            self.issues.push(CodeIssue {
895                file_path: self.file_path.clone(),
896                line: 1,
897                column: 1,
898                rule_name: "pattern-matching-abuse".to_string(),
899                message: messages[self.issues.len() % messages.len()].to_string(),
900                severity: Severity::Mild,
901                roast_level: RoastLevel::Gentle,
902            });
903        }
904    }
905}
906
907impl<'ast> Visit<'ast> for PatternVisitor {
908    fn visit_pat_tuple(&mut self, _tuple_pat: &'ast PatTuple) {
909        self.check_pattern_complexity("元组");
910        syn::visit::visit_pat_tuple(self, _tuple_pat);
911    }
912
913    fn visit_pat_slice(&mut self, _slice_pat: &'ast PatSlice) {
914        self.check_pattern_complexity("切片");
915        syn::visit::visit_pat_slice(self, _slice_pat);
916    }
917
918    fn visit_expr_match(&mut self, match_expr: &'ast ExprMatch) {
919        if match_expr.arms.len() > 10 {
920            let messages = [
921                "Match 分支比我的人生选择还多",
922                "这么多 match 分支,你确定不是在写状态机?",
923                "Match 分支过多,建议重构",
924                "这个 match 比电视遥控器的按钮还多",
925            ];
926
927            self.issues.push(CodeIssue {
928                file_path: self.file_path.clone(),
929                line: 1,
930                column: 1,
931                rule_name: "pattern-matching-abuse".to_string(),
932                message: messages[self.issues.len() % messages.len()].to_string(),
933                severity: Severity::Spicy,
934                roast_level: RoastLevel::Sarcastic,
935            });
936        }
937        syn::visit::visit_expr_match(self, match_expr);
938    }
939}
940
941// Reference Visitor
942struct ReferenceVisitor {
943    file_path: std::path::PathBuf,
944    issues: Vec<CodeIssue>,
945    reference_count: usize,
946}
947
948impl ReferenceVisitor {
949    fn new(file_path: std::path::PathBuf) -> Self {
950        Self {
951            file_path,
952            issues: Vec::new(),
953            reference_count: 0,
954        }
955    }
956}
957
958impl<'ast> Visit<'ast> for ReferenceVisitor {
959    fn visit_type_reference(&mut self, _ref_type: &'ast TypeReference) {
960        self.reference_count += 1;
961
962        if self.reference_count > 20 {
963            let messages = [
964                "引用比我的社交关系还复杂",
965                "这么多引用,你确定不是在写指针迷宫?",
966                "引用过多,小心借用检查器罢工",
967                "引用数量超标,建议重新设计数据结构",
968            ];
969
970            self.issues.push(CodeIssue {
971                file_path: self.file_path.clone(),
972                line: 1,
973                column: 1,
974                rule_name: "reference-abuse".to_string(),
975                message: messages[self.issues.len() % messages.len()].to_string(),
976                severity: Severity::Mild,
977                roast_level: RoastLevel::Gentle,
978            });
979        }
980
981        syn::visit::visit_type_reference(self, _ref_type);
982    }
983}
984
985// Box Visitor
986struct BoxVisitor {
987    #[allow(dead_code)]
988    file_path: std::path::PathBuf,
989    issues: Vec<CodeIssue>,
990}
991
992impl BoxVisitor {
993    fn new(file_path: std::path::PathBuf) -> Self {
994        Self {
995            file_path,
996            issues: Vec::new(),
997        }
998    }
999}
1000
1001impl<'ast> Visit<'ast> for BoxVisitor {
1002    // Box detection is handled in the rule implementation via content analysis
1003}
1004
1005// Slice Visitor
1006struct SliceVisitor {
1007    file_path: std::path::PathBuf,
1008    issues: Vec<CodeIssue>,
1009    slice_count: usize,
1010}
1011
1012impl SliceVisitor {
1013    fn new(file_path: std::path::PathBuf) -> Self {
1014        Self {
1015            file_path,
1016            issues: Vec::new(),
1017            slice_count: 0,
1018        }
1019    }
1020}
1021
1022impl<'ast> Visit<'ast> for SliceVisitor {
1023    fn visit_type_slice(&mut self, _slice_type: &'ast TypeSlice) {
1024        self.slice_count += 1;
1025
1026        if self.slice_count > 15 {
1027            let messages = [
1028                "切片比我切菜还频繁",
1029                "这么多切片,你是在开水果店吗?",
1030                "切片过多,数组都被你切碎了",
1031                "Slice 滥用,建议使用 Vec",
1032            ];
1033
1034            self.issues.push(CodeIssue {
1035                file_path: self.file_path.clone(),
1036                line: 1,
1037                column: 1,
1038                rule_name: "slice-abuse".to_string(),
1039                message: messages[self.issues.len() % messages.len()].to_string(),
1040                severity: Severity::Mild,
1041                roast_level: RoastLevel::Gentle,
1042            });
1043        }
1044
1045        syn::visit::visit_type_slice(self, _slice_type);
1046    }
1047}