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