Skip to main content

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, TypeTraitObject,
5};
6
7use crate::analyzer::{CodeIssue, Severity};
8use crate::rules::Rule;
9use crate::utils::{count_non_import_matches, find_line_of_str, get_position};
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;
19
20impl Rule for ChannelAbuseRule {
21    fn name(&self) -> &'static str {
22        "channel-abuse"
23    }
24
25    fn check(
26        &self,
27        file_path: &Path,
28        syntax_tree: &File,
29        content: &str,
30        lang: &str,
31        is_test_file: bool,
32    ) -> Vec<CodeIssue> {
33        if is_test_file {
34            return Vec::new();
35        }
36        let mut visitor = ChannelVisitor::new(file_path.to_path_buf(), lang);
37        visitor.visit_file(syntax_tree);
38
39        // Also check for channel-related usage in content (excluding imports)
40        if content.contains("std::sync::mpsc") || content.contains("tokio::sync") {
41            visitor.channel_count += count_non_import_matches(content, "channel");
42            visitor.channel_count += count_non_import_matches(content, "Sender");
43            visitor.channel_count += count_non_import_matches(content, "Receiver");
44        }
45
46        visitor.check_channel_overuse(content);
47        visitor.issues
48    }
49}
50
51impl Rule for AsyncAbuseRule {
52    fn name(&self) -> &'static str {
53        "async-abuse"
54    }
55
56    fn check(
57        &self,
58        file_path: &Path,
59        syntax_tree: &File,
60        _content: &str,
61        lang: &str,
62        is_test_file: bool,
63    ) -> Vec<CodeIssue> {
64        if is_test_file {
65            return Vec::new();
66        }
67        let mut visitor = AsyncVisitor::new(file_path.to_path_buf(), lang);
68        visitor.visit_file(syntax_tree);
69        visitor.issues
70    }
71}
72
73impl Rule for DynTraitAbuseRule {
74    fn name(&self) -> &'static str {
75        "dyn-trait-abuse"
76    }
77
78    fn check(
79        &self,
80        file_path: &Path,
81        syntax_tree: &File,
82        _content: &str,
83        lang: &str,
84        is_test_file: bool,
85    ) -> Vec<CodeIssue> {
86        if is_test_file {
87            return Vec::new();
88        }
89        let mut visitor = DynTraitVisitor::new(file_path.to_path_buf(), lang);
90        visitor.visit_file(syntax_tree);
91        visitor.issues
92    }
93}
94
95impl Rule for UnsafeAbuseRule {
96    fn name(&self) -> &'static str {
97        "unsafe-abuse"
98    }
99
100    fn check(
101        &self,
102        file_path: &Path,
103        syntax_tree: &File,
104        content: &str,
105        lang: &str,
106        is_test_file: bool,
107    ) -> Vec<CodeIssue> {
108        if is_test_file {
109            return Vec::new();
110        }
111        let mut visitor = UnsafeVisitor::new(file_path.to_path_buf(), lang);
112        visitor.visit_file(syntax_tree);
113
114        // Check unsafe keyword usage in content
115        visitor.check_unsafe_in_content(content);
116        visitor.issues
117    }
118}
119
120impl Rule for FFIAbuseRule {
121    fn name(&self) -> &'static str {
122        "ffi-abuse"
123    }
124
125    fn check(
126        &self,
127        file_path: &Path,
128        syntax_tree: &File,
129        content: &str,
130        lang: &str,
131        is_test_file: bool,
132    ) -> Vec<CodeIssue> {
133        if is_test_file {
134            return Vec::new();
135        }
136        let mut visitor = FFIVisitor::new(file_path.to_path_buf(), lang);
137        visitor.visit_file(syntax_tree);
138
139        // Check FFI-related patterns in content
140        visitor.check_ffi_patterns_in_content(content);
141        visitor.issues
142    }
143}
144
145impl Rule for MacroAbuseRule {
146    fn name(&self) -> &'static str {
147        "macro-abuse"
148    }
149
150    fn check(
151        &self,
152        file_path: &Path,
153        syntax_tree: &File,
154        _content: &str,
155        lang: &str,
156        is_test_file: bool,
157    ) -> Vec<CodeIssue> {
158        if is_test_file {
159            return Vec::new();
160        }
161
162        let mut visitor = MacroVisitor::new(file_path.to_path_buf(), lang);
163        visitor.visit_file(syntax_tree);
164        visitor.issues
165    }
166}
167
168impl Rule for ModuleComplexityRule {
169    fn name(&self) -> &'static str {
170        "module-complexity"
171    }
172
173    fn check(
174        &self,
175        file_path: &Path,
176        syntax_tree: &File,
177        _content: &str,
178        lang: &str,
179        is_test_file: bool,
180    ) -> Vec<CodeIssue> {
181        if is_test_file {
182            return Vec::new();
183        }
184        let mut visitor = ModuleVisitor::new(file_path.to_path_buf(), lang);
185        visitor.visit_file(syntax_tree);
186        visitor.issues
187    }
188}
189
190impl Rule for PatternMatchingAbuseRule {
191    fn name(&self) -> &'static str {
192        "pattern-matching-abuse"
193    }
194
195    fn check(
196        &self,
197        file_path: &Path,
198        syntax_tree: &File,
199        _content: &str,
200        lang: &str,
201        is_test_file: bool,
202    ) -> Vec<CodeIssue> {
203        if is_test_file {
204            return Vec::new();
205        }
206
207        // Skip i18n/internationalization files (they legitimately have many match branches)
208        let file_name = file_path.file_name().and_then(|f| f.to_str()).unwrap_or("");
209        if file_name.contains("i18n") || file_name.contains("locale") || file_name.contains("lang")
210        {
211            return Vec::new();
212        }
213
214        let mut visitor = PatternVisitor::new(file_path.to_path_buf(), lang);
215        visitor.visit_file(syntax_tree);
216        visitor.issues
217    }
218}
219
220// Channel Visitor
221struct ChannelVisitor {
222    file_path: std::path::PathBuf,
223    issues: Vec<CodeIssue>,
224    channel_count: usize,
225    lang: String,
226    last_position: (usize, usize),
227}
228
229impl ChannelVisitor {
230    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
231        Self {
232            file_path,
233            issues: Vec::new(),
234            channel_count: 0,
235            lang: lang.to_string(),
236            last_position: (1, 1),
237        }
238    }
239
240    fn check_channel_overuse(&mut self, content: &str) {
241        // Only report if we actually found channel TYPE usage (not just string literals)
242        // and the count exceeds threshold
243        if self.channel_count > 5 {
244            let messages = if self.lang == "zh-CN" {
245                [
246                    "Channel 用得比我发微信还频繁,你确定不是在写聊天软件?",
247                    "这么多 Channel,你是想开通讯公司吗?",
248                    "Channel 滥用!你的程序比电话交换机还复杂",
249                    "Channel 数量超标,建议重新设计架构",
250                    "这么多 Channel,我怀疑你在写分布式系统",
251                ]
252            } else {
253                [
254                    "You use channels more than I text - are you writing a chat app?",
255                    "This many channels, planning to start a telecom company?",
256                    "Channel abuse! Your program is more complex than a telephone switchboard",
257                    "Channel count exceeds limits - consider redesigning the architecture",
258                    "So many channels, I suspect you're building a distributed system",
259                ]
260            };
261
262            let candidates = [
263                find_line_of_str(content, "channel"),
264                find_line_of_str(content, "Sender"),
265                find_line_of_str(content, "Receiver"),
266            ];
267            let line = candidates
268                .iter()
269                .copied()
270                .filter(|&l| l > 1)
271                .min()
272                .unwrap_or(1);
273
274            self.issues.push(CodeIssue {
275                file_path: self.file_path.clone(),
276                line,
277                column: 1,
278                rule_name: "channel-abuse".to_string(),
279                message: messages[self.issues.len() % messages.len()].to_string(),
280                severity: Severity::Spicy,
281            });
282        }
283    }
284}
285
286impl<'ast> Visit<'ast> for ChannelVisitor {
287    fn visit_type_path(&mut self, type_path: &'ast TypePath) {
288        let path_str = quote::quote!(#type_path).to_string();
289        if path_str.contains("Sender")
290            || path_str.contains("Receiver")
291            || path_str.contains("channel")
292        {
293            self.channel_count += 1;
294            self.last_position = get_position(type_path);
295        }
296        syn::visit::visit_type_path(self, type_path);
297    }
298}
299
300// Async Visitor
301struct AsyncVisitor {
302    file_path: std::path::PathBuf,
303    issues: Vec<CodeIssue>,
304    async_count: usize,
305    await_count: usize,
306    lang: String,
307    last_position: (usize, usize),
308}
309
310impl AsyncVisitor {
311    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
312        Self {
313            file_path,
314            issues: Vec::new(),
315            async_count: 0,
316            await_count: 0,
317            lang: lang.to_string(),
318            last_position: (1, 1),
319        }
320    }
321
322    fn check_async_abuse(&mut self) {
323        if self.async_count > 10 {
324            let messages = if self.lang == "zh-CN" {
325                [
326                    "Async 函数比我的异步人生还要复杂",
327                    "这么多 async,你确定不是在写 JavaScript?",
328                    "Async 滥用!建议学习一下同步编程的美好",
329                    "异步函数过多,小心把自己绕晕了",
330                ]
331            } else {
332                [
333                    "Async functions are more complex than my async life",
334                    "So much async - are you sure you're not writing JavaScript?",
335                    "Async abuse! Consider the beauty of synchronous programming",
336                    "Too many async functions - careful not to confuse yourself",
337                ]
338            };
339
340            let (line, column) = self.last_position;
341            self.issues.push(CodeIssue {
342                file_path: self.file_path.clone(),
343                line,
344                column,
345                rule_name: "async-abuse".to_string(),
346                message: messages[self.issues.len() % messages.len()].to_string(),
347                severity: Severity::Spicy,
348            });
349        }
350
351        if self.await_count > 20 {
352            let messages = if self.lang == "zh-CN" {
353                [
354                    "Await 用得比我等外卖还频繁",
355                    "这么多 await,你的程序是在等什么?世界末日吗?",
356                    "Await 过度使用,建议批量处理",
357                    "等待次数过多,你的程序比我还有耐心",
358                ]
359            } else {
360                [
361                    "You await more than I wait for food delivery",
362                    "So many awaits - what is your program waiting for, the apocalypse?",
363                    "Excessive await usage - consider batching operations",
364                    "Your program is more patient than I'll ever be",
365                ]
366            };
367
368            let (line, column) = self.last_position;
369            self.issues.push(CodeIssue {
370                file_path: self.file_path.clone(),
371                line,
372                column,
373                rule_name: "async-abuse".to_string(),
374                message: messages[self.issues.len() % messages.len()].to_string(),
375                severity: Severity::Mild,
376            });
377        }
378    }
379}
380
381impl<'ast> Visit<'ast> for AsyncVisitor {
382    fn visit_expr_async(&mut self, _async_expr: &'ast ExprAsync) {
383        self.async_count += 1;
384        self.last_position = get_position(_async_expr);
385        self.check_async_abuse();
386        syn::visit::visit_expr_async(self, _async_expr);
387    }
388
389    fn visit_expr_await(&mut self, _await_expr: &'ast ExprAwait) {
390        self.await_count += 1;
391        self.last_position = get_position(_await_expr);
392        self.check_async_abuse();
393        syn::visit::visit_expr_await(self, _await_expr);
394    }
395}
396
397// Dyn Trait Visitor
398struct DynTraitVisitor {
399    file_path: std::path::PathBuf,
400    issues: Vec<CodeIssue>,
401    dyn_count: usize,
402    lang: String,
403}
404
405impl DynTraitVisitor {
406    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
407        Self {
408            file_path,
409            issues: Vec::new(),
410            dyn_count: 0,
411            lang: lang.to_string(),
412        }
413    }
414}
415
416impl<'ast> Visit<'ast> for DynTraitVisitor {
417    fn visit_type_trait_object(&mut self, trait_object: &'ast TypeTraitObject) {
418        self.dyn_count += 1;
419
420        if self.dyn_count > 5 {
421            let messages = if self.lang == "zh-CN" {
422                [
423                    "Dyn trait 用得比我换工作还频繁",
424                    "这么多动态分发,性能都跑到哪里去了?",
425                    "Dyn trait 滥用,你确定不是在写 Python?",
426                    "动态 trait 过多,编译器优化都哭了",
427                    "这么多 dyn,你的程序比变色龙还善变",
428                ]
429            } else {
430                [
431                    "You use dyn traits more often than I change jobs",
432                    "So much dynamic dispatch - where did all the performance go?",
433                    "dyn trait abuse - are you sure you're not writing Python?",
434                    "Too many dynamic traits - the compiler optimizer is crying",
435                    "So many dyns, your program is more wishy-washy than a chameleon",
436                ]
437            };
438
439            let (line, column) = get_position(trait_object);
440            self.issues.push(CodeIssue {
441                file_path: self.file_path.clone(),
442                line,
443                column,
444                rule_name: "dyn-trait-abuse".to_string(),
445                message: messages[self.issues.len() % messages.len()].to_string(),
446                severity: Severity::Spicy,
447            });
448        }
449
450        syn::visit::visit_type_trait_object(self, trait_object);
451    }
452}
453
454// Unsafe Visitor
455struct UnsafeVisitor {
456    file_path: std::path::PathBuf,
457    issues: Vec<CodeIssue>,
458    unsafe_count: usize,
459    unsafe_fn_count: usize,
460    unsafe_impl_count: usize,
461    unsafe_trait_count: usize,
462    lang: String,
463}
464
465impl UnsafeVisitor {
466    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
467        Self {
468            file_path,
469            issues: Vec::new(),
470            unsafe_count: 0,
471            unsafe_fn_count: 0,
472            unsafe_impl_count: 0,
473            unsafe_trait_count: 0,
474            lang: lang.to_string(),
475        }
476    }
477
478    fn check_unsafe_in_content(&mut self, content: &str) {
479        // Check unsafe functions
480        let unsafe_fn_matches = content.matches("unsafe fn").count();
481        self.unsafe_fn_count += unsafe_fn_matches;
482
483        // Check unsafe impl
484        let unsafe_impl_matches = content.matches("unsafe impl").count();
485        self.unsafe_impl_count += unsafe_impl_matches;
486
487        // Check unsafe trait
488        let unsafe_trait_matches = content.matches("unsafe trait").count();
489        self.unsafe_trait_count += unsafe_trait_matches;
490
491        // Check raw pointer operations
492        let raw_ptr_count = content.matches("*const").count() + content.matches("*mut").count();
493
494        // Check memory operation functions
495        let dangerous_ops = [
496            "std::ptr::write",
497            "std::ptr::read",
498            "std::ptr::copy",
499            "std::mem::transmute",
500            "std::mem::forget",
501            "std::mem::uninitialized",
502            "std::slice::from_raw_parts",
503            "std::str::from_utf8_unchecked",
504            "Box::from_raw",
505            "Vec::from_raw_parts",
506            "String::from_raw_parts",
507        ];
508
509        let mut dangerous_op_count = 0;
510        for op in &dangerous_ops {
511            dangerous_op_count += content.matches(op).count();
512        }
513
514        self.generate_unsafe_issues(content, raw_ptr_count, dangerous_op_count);
515    }
516
517    fn generate_unsafe_issues(
518        &mut self,
519        content: &str,
520        raw_ptr_count: usize,
521        dangerous_op_count: usize,
522    ) {
523        // Check for excessive unsafe functions
524        if self.unsafe_fn_count > 2 {
525            let messages = if self.lang == "zh-CN" {
526                [
527                    "Unsafe 函数比我的黑历史还多!你确定这还是 Rust 吗?",
528                    "这么多 unsafe 函数,Rust 的安全保证都被你玩坏了",
529                    "Unsafe 函数过多,建议重新考虑设计架构",
530                    "你的 unsafe 函数让 Rust 编译器都开始怀疑人生了",
531                ]
532            } else {
533                [
534                    "More unsafe functions than my dark secrets! Are you sure this is still Rust?",
535                    "So many unsafe functions - Rust's safety guarantees are in shambles",
536                    "Too many unsafe functions - consider redesigning the architecture",
537                    "Your unsafe functions are making the Rust compiler question its life choices",
538                ]
539            };
540
541            let line = find_line_of_str(content, "unsafe fn");
542            self.issues.push(CodeIssue {
543                file_path: self.file_path.clone(),
544                line,
545                column: 1,
546                rule_name: "unsafe-abuse".to_string(),
547                message: messages[self.issues.len() % messages.len()].to_string(),
548                severity: Severity::Nuclear,
549            });
550        }
551
552        // Check for excessive raw pointers
553        if raw_ptr_count > 5 {
554            let messages = if self.lang == "zh-CN" {
555                [
556                    "原始指针用得比我换手机还频繁,你这是在写 C 语言吗?",
557                    "这么多原始指针,内存安全已经不在服务区了",
558                    "原始指针过多,建议使用安全的 Rust 抽象",
559                    "你的指针操作让 Valgrind 都要加班了",
560                ]
561            } else {
562                [
563                    "You use raw pointers more than I change phones - are you writing C?",
564                    "So many raw pointers, memory safety has left the building",
565                    "Too many raw pointers - consider using safe Rust abstractions",
566                    "Your pointer operations are making Valgrind work overtime",
567                ]
568            };
569
570            let line = find_line_of_str(content, "*const");
571            self.issues.push(CodeIssue {
572                file_path: self.file_path.clone(),
573                line,
574                column: 1,
575                rule_name: "unsafe-abuse".to_string(),
576                message: messages[self.issues.len() % messages.len()].to_string(),
577                severity: Severity::Nuclear,
578            });
579        }
580
581        // Check for excessive dangerous operations
582        if dangerous_op_count > 3 {
583            let messages = if self.lang == "zh-CN" {
584                [
585                    "危险的内存操作比我的危险驾驶还要多!",
586                    "这些危险操作让我想起了 C++ 的恐怖回忆",
587                    "内存操作过于危险,建议使用安全替代方案",
588                    "你的代码比走钢丝还危险,小心内存泄漏!",
589                ]
590            } else {
591                [
592                    "More dangerous memory ops than my reckless driving!",
593                    "These dangerous ops bring back terrifying C++ memories",
594                    "Memory operations are too dangerous - use safe alternatives",
595                    "Your code is more dangerous than tightrope walking - watch for memory leaks!",
596                ]
597            };
598
599            let line = find_line_of_str(content, "std::ptr::write")
600                .max(find_line_of_str(content, "std::mem::transmute"))
601                .max(find_line_of_str(content, "from_raw"));
602            self.issues.push(CodeIssue {
603                file_path: self.file_path.clone(),
604                line,
605                column: 1,
606                rule_name: "unsafe-abuse".to_string(),
607                message: messages[self.issues.len() % messages.len()].to_string(),
608                severity: Severity::Nuclear,
609            });
610        }
611    }
612}
613
614impl<'ast> Visit<'ast> for UnsafeVisitor {
615    fn visit_expr_unsafe(&mut self, _unsafe_expr: &'ast ExprUnsafe) {
616        self.unsafe_count += 1;
617
618        let messages = if self.lang == "zh-CN" {
619            [
620                "Unsafe 代码!你这是在玩火还是在挑战 Rust 的底线?",
621                "又见 unsafe!安全性是什么?能吃吗?",
622                "Unsafe 使用者,恭喜你获得了'内存安全破坏者'称号",
623                "这个 unsafe 让我想起了 C 语言的恐怖回忆",
624                "Unsafe 代码:让 Rust 程序员夜不能寐的存在",
625            ]
626        } else {
627            [
628                "Unsafe code! Playing with fire or challenging Rust's limits?",
629                "Unsafe again! What is safety, can you eat it?",
630                "Unsafe user - congratulations, you've earned the 'Memory Safety Destroyer' title",
631                "This unsafe brings back terrifying memories of C programming",
632                "Unsafe code: the thing that keeps Rust programmers up at night",
633            ]
634        };
635
636        let severity = if self.unsafe_count > 3 {
637            Severity::Nuclear
638        } else {
639            Severity::Spicy
640        };
641
642        let (line, column) = get_position(_unsafe_expr);
643        self.issues.push(CodeIssue {
644            file_path: self.file_path.clone(),
645            line,
646            column,
647            rule_name: "unsafe-abuse".to_string(),
648            message: messages[self.issues.len() % messages.len()].to_string(),
649            severity,
650        });
651
652        syn::visit::visit_expr_unsafe(self, _unsafe_expr);
653    }
654
655    fn visit_item_fn(&mut self, item_fn: &'ast ItemFn) {
656        if item_fn.sig.unsafety.is_some() {
657            self.unsafe_fn_count += 1;
658        }
659        syn::visit::visit_item_fn(self, item_fn);
660    }
661}
662
663// FFI Visitor
664struct FFIVisitor {
665    file_path: std::path::PathBuf,
666    issues: Vec<CodeIssue>,
667    extern_block_count: usize,
668    extern_fn_count: usize,
669    c_repr_count: usize,
670    lang: String,
671}
672
673impl FFIVisitor {
674    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
675        Self {
676            file_path,
677            issues: Vec::new(),
678            extern_block_count: 0,
679            extern_fn_count: 0,
680            c_repr_count: 0,
681            lang: lang.to_string(),
682        }
683    }
684
685    fn check_ffi_patterns_in_content(&mut self, content: &str) {
686        // Check C representation
687        self.c_repr_count += content.matches("#[repr(C)]").count();
688
689        // Check C string operations
690        let c_string_ops = [
691            "CString",
692            "CStr",
693            "c_char",
694            "c_void",
695            "c_int",
696            "c_long",
697            "std::ffi::",
698            "libc::",
699            "std::os::raw::",
700        ];
701
702        let mut c_ops_count = 0;
703        for op in &c_string_ops {
704            c_ops_count += content.matches(op).count();
705        }
706
707        // Check dynamic library loading
708        let dll_ops = ["libloading", "dlopen", "LoadLibrary", "GetProcAddress"];
709        let mut dll_count = 0;
710        for op in &dll_ops {
711            dll_count += content.matches(op).count();
712        }
713
714        self.generate_ffi_issues(content, c_ops_count, dll_count);
715    }
716
717    fn generate_ffi_issues(&mut self, content: &str, c_ops_count: usize, dll_count: usize) {
718        // Check for excessive extern blocks
719        if self.extern_block_count > 2 {
720            let messages = if self.lang == "zh-CN" {
721                [
722                    "Extern 块比我的前任还多,你这是要和多少种语言交互?",
723                    "这么多 extern 块,你确定不是在写多语言翻译器?",
724                    "FFI 接口过多,建议封装成统一的抽象层",
725                    "外部接口比我的社交关系还复杂!",
726                ]
727            } else {
728                [
729                    "More extern blocks than my exes - how many languages are you interfacing with?",
730                    "So many extern blocks - are you building a multilingual translator?",
731                    "Too many FFI interfaces - consider wrapping them in a unified abstraction layer",
732                    "References are more complex than my social life!",
733                ]
734            };
735
736            let line = find_line_of_str(content, "extern \"C\"");
737            self.issues.push(CodeIssue {
738                file_path: self.file_path.clone(),
739                line,
740                column: 1,
741                rule_name: "ffi-abuse".to_string(),
742                message: messages[self.issues.len() % messages.len()].to_string(),
743                severity: Severity::Spicy,
744            });
745        }
746
747        // Check for excessive C operations
748        if c_ops_count > 10 {
749            let messages = if self.lang == "zh-CN" {
750                [
751                    "C 语言操作比我的 C 语言作业还多,你确定这是 Rust 项目?",
752                    "这么多 C FFI,Rust 的安全性都要哭了",
753                    "C 接口过多,建议使用更安全的 Rust 绑定",
754                    "你的 FFI 代码让我想起了指针地狱的恐怖",
755                ]
756            } else {
757                [
758                    "More C operations than my C homework - are you sure this is a Rust project?",
759                    "So much C FFI, Rust's safety guarantees are crying",
760                    "Too many C interfaces - consider using safer Rust bindings",
761                    "Your FFI code brings back the horrors of pointer hell",
762                ]
763            };
764
765            let line =
766                find_line_of_str(content, "CString").max(find_line_of_str(content, "std::ffi"));
767            self.issues.push(CodeIssue {
768                file_path: self.file_path.clone(),
769                line,
770                column: 1,
771                rule_name: "ffi-abuse".to_string(),
772                message: messages[self.issues.len() % messages.len()].to_string(),
773                severity: Severity::Nuclear,
774            });
775        }
776
777        // Check dynamic library loading
778        if dll_count > 0 {
779            let messages = if self.lang == "zh-CN" {
780                [
781                    "动态库加载!你这是在运行时玩杂技吗?",
782                    "动态加载库,小心加载到病毒!",
783                    "运行时库加载,调试的时候准备哭吧",
784                    "动态库操作,你的程序比变形金刚还会变身",
785                ]
786            } else {
787                [
788                    "Dynamic library loading! Are you performing acrobatics at runtime?",
789                    "Dynamic loading - careful not to load a virus!",
790                    "Runtime library loading - get ready to cry during debugging",
791                    "Dynamic library ops - your program transforms more than a Transformer",
792                ]
793            };
794
795            let line =
796                find_line_of_str(content, "libloading").max(find_line_of_str(content, "dlopen"));
797            self.issues.push(CodeIssue {
798                file_path: self.file_path.clone(),
799                line,
800                column: 1,
801                rule_name: "ffi-abuse".to_string(),
802                message: messages[self.issues.len() % messages.len()].to_string(),
803                severity: Severity::Spicy,
804            });
805        }
806
807        // Check for excessive repr(C)
808        if self.c_repr_count > 5 {
809            let messages = if self.lang == "zh-CN" {
810                [
811                    "repr(C) 用得比我说 C 语言还频繁!",
812                    "这么多 C 表示法,你的结构体都要移民到 C 语言了",
813                    "C 表示法过多,内存布局都要乱套了",
814                    "repr(C) 滥用,Rust 的零成本抽象在哭泣",
815                ]
816            } else {
817                [
818                    "You use repr(C) more than I speak C!",
819                    "So many C representations, your structs are emigrating to C",
820                    "Too many C representations - memory layout is a mess",
821                    "repr(C) abuse - Rust's zero-cost abstractions are weeping",
822                ]
823            };
824
825            let line = find_line_of_str(content, "#[repr(C)]");
826            self.issues.push(CodeIssue {
827                file_path: self.file_path.clone(),
828                line,
829                column: 1,
830                rule_name: "ffi-abuse".to_string(),
831                message: messages[self.issues.len() % messages.len()].to_string(),
832                severity: Severity::Spicy,
833            });
834        }
835    }
836}
837
838impl<'ast> Visit<'ast> for FFIVisitor {
839    fn visit_item_foreign_mod(&mut self, foreign_mod: &'ast ItemForeignMod) {
840        self.extern_block_count += 1;
841
842        // Count external functions
843        for item in &foreign_mod.items {
844            if matches!(item, ForeignItem::Fn(_)) {
845                self.extern_fn_count += 1;
846            }
847        }
848
849        // Check for excessive extern functions
850        if self.extern_fn_count > 10 {
851            let messages = if self.lang == "zh-CN" {
852                [
853                    "外部函数比我的外卖订单还多!",
854                    "这么多 extern 函数,你是在开联合国大会吗?",
855                    "外部接口过多,建议分模块管理",
856                    "FFI 函数数量超标,小心接口管理混乱",
857                ]
858            } else {
859                [
860                    "More extern functions than my food delivery orders!",
861                    "So many extern functions - are you hosting a UN summit?",
862                    "Too many external interfaces - consider modular management",
863                    "FFI function count exceeds limits - watch out for interface chaos",
864                ]
865            };
866
867            let (line, column) = get_position(foreign_mod);
868            self.issues.push(CodeIssue {
869                file_path: self.file_path.clone(),
870                line,
871                column,
872                rule_name: "ffi-abuse".to_string(),
873                message: messages[self.issues.len() % messages.len()].to_string(),
874                severity: Severity::Spicy,
875            });
876        }
877
878        syn::visit::visit_item_foreign_mod(self, foreign_mod);
879    }
880
881    fn visit_item_fn(&mut self, item_fn: &'ast ItemFn) {
882        // Check if it's an extern "C" function
883        if let Some(abi) = &item_fn.sig.abi {
884            if let Some(abi_name) = &abi.name {
885                if abi_name.value() == "C" {
886                    self.extern_fn_count += 1;
887                }
888            }
889        }
890        syn::visit::visit_item_fn(self, item_fn);
891    }
892}
893
894// Macro Visitor
895struct MacroVisitor {
896    file_path: std::path::PathBuf,
897    issues: Vec<CodeIssue>,
898    macro_count: usize,
899    lang: String,
900}
901
902impl MacroVisitor {
903    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
904        Self {
905            file_path,
906            issues: Vec::new(),
907            macro_count: 0,
908            lang: lang.to_string(),
909        }
910    }
911}
912
913impl<'ast> Visit<'ast> for MacroVisitor {
914    fn visit_macro(&mut self, _macro: &'ast Macro) {
915        self.macro_count += 1;
916
917        // Only flag if there are many macros (threshold raised from 10 to 20)
918        if self.macro_count > 20 && self.issues.is_empty() {
919            let messages = if self.lang == "zh-CN" {
920                [
921                    "宏定义比我的借口还多",
922                    "这么多宏,你确定不是在写 C 语言?",
923                    "宏滥用!编译时间都被你搞长了",
924                    "宏过多,调试的时候准备哭吧",
925                    "这么多宏,IDE 都要罢工了",
926                ]
927            } else {
928                [
929                    "More macros than my excuses",
930                    "So many macros - are you sure you're not writing C?",
931                    "Macro abuse! You've inflated the compile times",
932                    "Too many macros - get ready to cry during debugging",
933                    "So many macros, your IDE is about to go on strike",
934                ]
935            };
936
937            let (line, column) = get_position(_macro);
938            self.issues.push(CodeIssue {
939                file_path: self.file_path.clone(),
940                line,
941                column,
942                rule_name: "macro-abuse".to_string(),
943                message: messages[self.issues.len() % messages.len()].to_string(),
944                severity: Severity::Mild,
945            });
946        }
947
948        syn::visit::visit_macro(self, _macro);
949    }
950}
951
952// Module Visitor
953struct ModuleVisitor {
954    file_path: std::path::PathBuf,
955    issues: Vec<CodeIssue>,
956    module_depth: usize,
957    max_depth: usize,
958    lang: String,
959}
960
961impl ModuleVisitor {
962    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
963        Self {
964            file_path,
965            issues: Vec::new(),
966            module_depth: 0,
967            max_depth: 0,
968            lang: lang.to_string(),
969        }
970    }
971}
972
973impl<'ast> Visit<'ast> for ModuleVisitor {
974    fn visit_item_mod(&mut self, _module: &'ast ItemMod) {
975        self.module_depth += 1;
976        self.max_depth = self.max_depth.max(self.module_depth);
977
978        if self.module_depth > 5 {
979            let messages = if self.lang == "zh-CN" {
980                [
981                    "模块嵌套比俄罗斯套娃还深",
982                    "这模块结构比我的家族关系还复杂",
983                    "模块嵌套过深,建议重新组织代码结构",
984                    "这么深的模块,找个函数比找宝藏还难",
985                ]
986            } else {
987                [
988                    "Module nesting is deeper than Russian dolls",
989                    "This module structure is more complex than my family tree",
990                    "Module nesting too deep - consider restructuring your code",
991                    "Modules so deep, finding a function is harder than finding treasure",
992                ]
993            };
994
995            let (line, column) = get_position(_module);
996            self.issues.push(CodeIssue {
997                file_path: self.file_path.clone(),
998                line,
999                column,
1000                rule_name: "module-complexity".to_string(),
1001                message: messages[self.issues.len() % messages.len()].to_string(),
1002                severity: Severity::Spicy,
1003            });
1004        }
1005
1006        syn::visit::visit_item_mod(self, _module);
1007        self.module_depth -= 1;
1008    }
1009}
1010
1011// Pattern Visitor
1012struct PatternVisitor {
1013    file_path: std::path::PathBuf,
1014    issues: Vec<CodeIssue>,
1015    complex_pattern_count: usize,
1016    lang: String,
1017    last_position: (usize, usize),
1018}
1019
1020impl PatternVisitor {
1021    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
1022        Self {
1023            file_path,
1024            issues: Vec::new(),
1025            complex_pattern_count: 0,
1026            lang: lang.to_string(),
1027            last_position: (1, 1),
1028        }
1029    }
1030
1031    fn check_pattern_complexity(&mut self, pattern_type: &str) {
1032        self.complex_pattern_count += 1;
1033
1034        if self.complex_pattern_count > 15 {
1035            let messages = if self.lang == "zh-CN" {
1036                [
1037                    format!("{pattern_type}模式匹配比我的感情生活还复杂"),
1038                    format!("这么多{pattern_type}模式,你是在写解谜游戏吗?"),
1039                    format!("{pattern_type}模式过多,建议简化逻辑"),
1040                    format!("复杂的{pattern_type}模式让代码可读性直线下降"),
1041                ]
1042            } else {
1043                let en_type = match pattern_type {
1044                    "元组" => "tuple",
1045                    "切片" => "slice",
1046                    _ => pattern_type,
1047                };
1048                [
1049                    format!("{en_type} pattern matching is more complex than my love life"),
1050                    format!("So many {en_type} patterns - are you writing a puzzle game?"),
1051                    format!("Too many {en_type} patterns - consider simplifying the logic"),
1052                    format!("Complex {en_type} patterns tanking your code readability"),
1053                ]
1054            };
1055
1056            let (line, column) = self.last_position;
1057            self.issues.push(CodeIssue {
1058                file_path: self.file_path.clone(),
1059                line,
1060                column,
1061                rule_name: "pattern-matching-abuse".to_string(),
1062                message: messages[self.issues.len() % messages.len()].to_string(),
1063                severity: Severity::Mild,
1064            });
1065        }
1066    }
1067}
1068
1069impl<'ast> Visit<'ast> for PatternVisitor {
1070    fn visit_pat_tuple(&mut self, _tuple_pat: &'ast PatTuple) {
1071        self.last_position = get_position(_tuple_pat);
1072        self.check_pattern_complexity("元组");
1073        syn::visit::visit_pat_tuple(self, _tuple_pat);
1074    }
1075
1076    fn visit_pat_slice(&mut self, _slice_pat: &'ast PatSlice) {
1077        self.last_position = get_position(_slice_pat);
1078        self.check_pattern_complexity("切片");
1079        syn::visit::visit_pat_slice(self, _slice_pat);
1080    }
1081
1082    fn visit_expr_match(&mut self, match_expr: &'ast ExprMatch) {
1083        // Only flag if there are many match arms (threshold raised from 10 to 20)
1084        if match_expr.arms.len() > 20 {
1085            let messages = if self.lang == "zh-CN" {
1086                [
1087                    "Match 分支比我的人生选择还多",
1088                    "这么多 match 分支,你确定不是在写状态机?",
1089                    "Match 分支过多,建议重构",
1090                    "这个 match 比电视遥控器的按钮还多",
1091                ]
1092            } else {
1093                [
1094                    "More match arms than life choices I've made",
1095                    "So many match branches - are you building a state machine?",
1096                    "Too many match arms - consider refactoring",
1097                    "This match has more arms than a TV remote has buttons",
1098                ]
1099            };
1100
1101            let (line, column) = get_position(match_expr);
1102            self.issues.push(CodeIssue {
1103                file_path: self.file_path.clone(),
1104                line,
1105                column,
1106                rule_name: "pattern-matching-abuse".to_string(),
1107                message: messages[self.issues.len() % messages.len()].to_string(),
1108                severity: Severity::Spicy,
1109            });
1110        }
1111        syn::visit::visit_expr_match(self, match_expr);
1112    }
1113}