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 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 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 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 let box_count = content.matches("Box::new").count() + content.matches("Box<").count();
217 if box_count > 8 {
218 let messages = vec![
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
259struct 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 = vec![
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
311struct 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 = vec![
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 = vec![
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
384struct 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 = vec![
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
429struct 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 let unsafe_fn_matches = content.matches("unsafe fn").count();
454 self.unsafe_fn_count += unsafe_fn_matches;
455
456 let unsafe_impl_matches = content.matches("unsafe impl").count();
458 self.unsafe_impl_count += unsafe_impl_matches;
459
460 let unsafe_trait_matches = content.matches("unsafe trait").count();
462 self.unsafe_trait_count += unsafe_trait_matches;
463
464 let raw_ptr_count = content.matches("*const").count() + content.matches("*mut").count();
466
467 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 if self.unsafe_fn_count > 2 {
493 let messages = vec![
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 if raw_ptr_count > 5 {
513 let messages = vec![
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 if dangerous_op_count > 3 {
533 let messages = vec![
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 = vec![
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
592struct 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 self.c_repr_count += content.matches("#[repr(C)]").count();
615
616 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 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 if self.extern_block_count > 2 {
647 let messages = vec![
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 if c_ops_count > 10 {
667 let messages = vec![
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 if dll_count > 0 {
687 let messages = vec![
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 if self.c_repr_count > 5 {
707 let messages = vec![
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 for item in &foreign_mod.items {
733 if matches!(item, ForeignItem::Fn(_)) {
734 self.extern_fn_count += 1;
735 }
736 }
737
738 if self.extern_fn_count > 10 {
740 let messages = vec![
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 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
774struct 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 = vec![
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
819struct 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 = vec![
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
867struct 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 = vec![
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 = vec![
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
941struct 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 = vec![
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
985struct 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 }
1004
1005struct 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 = vec![
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}