1use super::base_rules::{CountRule, MacroRule, MethodCallRule};
2use super::complex_rules::{
3 AbbreviationAbuseTsRule, ComplexClosureRule, DeepNestingRule, GodFunctionRule,
4 HungarianNotationTsRule, LongFunctionRule, MagicNumberRule, PrintlnDebuggingRule,
5 SingleLetterTsRule, TerribleNamingRule,
6};
7use super::remaining_rules::{
8 CommentedCodeRule, DeadCodeRule, DuplicateImportsRule, FileTooLongRule, MeaninglessRule,
9 TodoCommentRule,
10};
11use crate::analyzer::Severity;
12use crate::language::Language;
13
14pub fn register_rust_rules(engine: &mut crate::treesitter::rule::TreeSitterRuleEngine) {
16 engine.add(Box::new(MethodCallRule {
18 name: "unwrap-abuse",
19 method_name: "unwrap",
20 threshold: 0,
21 severity_fn: |count| {
22 if count > 15 {
23 Severity::Nuclear
24 } else if count > 8 {
25 Severity::Spicy
26 } else {
27 Severity::Mild
28 }
29 },
30 message_fn: |count| {
31 format!(
32 "Found {} .unwrap() calls — use proper error handling",
33 count
34 )
35 },
36 }));
37
38 engine.add(Box::new(MethodCallRule {
40 name: "unnecessary-clone",
41 method_name: "clone",
42 threshold: 24,
43 severity_fn: |_| Severity::Spicy,
44 message_fn: |count| {
45 format!(
46 "Found {} .clone() calls — consider using references instead",
47 count
48 )
49 },
50 }));
51
52 engine.add(Box::new(CountRule {
54 name: "async-abuse",
55 pattern: "(async_block) @block",
56 threshold: 10,
57 severity: Severity::Spicy,
58 languages: &[Language::Rust],
59 message_fn: |count| {
60 format!(
61 "Found {} async blocks — consider consolidating async operations",
62 count
63 )
64 },
65 }));
66
67 engine.add(Box::new(CountRule {
69 name: "macro-abuse",
70 pattern: "(macro_invocation) @m",
71 threshold: 20,
72 severity: Severity::Mild,
73 languages: &[Language::Rust],
74 message_fn: |count| {
75 format!(
76 "Found {} macro invocations — consider reducing macro usage",
77 count
78 )
79 },
80 }));
81
82 engine.add(Box::new(CountRule {
84 name: "lifetime-abuse",
85 pattern: "(lifetime) @life",
86 threshold: 20,
87 severity: Severity::Spicy,
88 languages: &[Language::Rust],
89 message_fn: |count| {
90 format!(
91 "Found {} lifetime annotations — consider simplifying lifetime management",
92 count
93 )
94 },
95 }));
96
97 engine.add(Box::new(CountRule {
99 name: "trait-complexity",
100 pattern: "(trait_item body: (declaration_list (function_item) @method))",
101 threshold: 10,
102 severity: Severity::Spicy,
103 languages: &[Language::Rust],
104 message_fn: |count| {
105 format!(
106 "Trait has {} methods — consider splitting into smaller traits",
107 count
108 )
109 },
110 }));
111
112 engine.add(Box::new(CountRule {
114 name: "generic-abuse",
115 pattern: "(type_parameters (type_parameter) @param)",
116 threshold: 5,
117 severity: Severity::Spicy,
118 languages: &[Language::Rust],
119 message_fn: |count| {
120 format!(
121 "Found {} generic parameters — consider simplifying the type signature",
122 count
123 )
124 },
125 }));
126
127 engine.add(Box::new(CountRule {
129 name: "pattern-matching-abuse",
130 pattern: "(tuple_pattern) @tp",
131 threshold: 15,
132 severity: Severity::Mild,
133 languages: &[Language::Rust],
134 message_fn: |count| {
135 format!(
136 "Found {} complex tuple patterns — consider using named structs",
137 count
138 )
139 },
140 }));
141
142 engine.add(Box::new(CountRule {
144 name: "box-abuse",
145 pattern: "(call_expression function: (scoped_identifier) @si)",
146 threshold: 8,
147 severity: Severity::Spicy,
148 languages: &[Language::Rust],
149 message_fn: |count| {
150 format!(
151 "Found {} Box::new() calls — consider using stack allocation",
152 count
153 )
154 },
155 }));
156
157 engine.add(Box::new(CountRule {
159 name: "reference-abuse",
160 pattern: "(reference_type) @rt",
161 threshold: 50,
162 severity: Severity::Mild,
163 languages: &[Language::Rust],
164 message_fn: |count| {
165 format!(
166 "Found {} reference types — consider simplifying ownership",
167 count
168 )
169 },
170 }));
171
172 engine.add(Box::new(CountRule {
174 name: "slice-abuse",
175 pattern: "(slice_type) @st",
176 threshold: 29,
177 severity: Severity::Mild,
178 languages: &[Language::Rust],
179 message_fn: |count| {
180 format!(
181 "Found {} slice types — consider using concrete collection types",
182 count
183 )
184 },
185 }));
186
187 engine.add(Box::new(CountRule {
189 name: "module-complexity",
190 pattern: "(mod_item body: (declaration_list (mod_item) @nested))",
191 threshold: 0,
192 severity: Severity::Spicy,
193 languages: &[Language::Rust],
194 message_fn: |count| {
195 format!(
196 "Found {} nested modules — consider flattening the module structure",
197 count
198 )
199 },
200 }));
201
202 engine.add(Box::new(MacroRule {
204 name: "panic-abuse",
205 macro_name: "panic",
206 threshold: 2,
207 severity: Severity::Nuclear,
208 message_fn: |count| {
209 format!(
210 "Found {} panic! calls — use proper error handling with Result",
211 count
212 )
213 },
214 }));
215
216 engine.add(Box::new(DeepNestingRule));
218 engine.add(Box::new(LongFunctionRule));
220
221 engine.add(Box::new(GodFunctionRule));
223
224 engine.add(Box::new(ComplexClosureRule));
226 engine.add(Box::new(TerribleNamingRule));
228
229 engine.add(Box::new(SingleLetterTsRule));
231
232 engine.add(Box::new(HungarianNotationTsRule));
234
235 engine.add(Box::new(AbbreviationAbuseTsRule));
237
238 engine.add(Box::new(PrintlnDebuggingRule));
240
241 engine.add(Box::new(MagicNumberRule));
243
244 engine.add(Box::new(MeaninglessRule));
246
247 engine.add(Box::new(CommentedCodeRule));
249
250 engine.add(Box::new(DeadCodeRule));
252
253 engine.add(Box::new(TodoCommentRule));
255
256 engine.add(Box::new(DuplicateImportsRule));
258
259 engine.add(Box::new(FileTooLongRule));
261
262 engine.add(Box::new(MethodCallRule {
264 name: "string-abuse",
265 method_name: "to_string",
266 threshold: 20,
267 severity_fn: |_| Severity::Mild,
268 message_fn: |count| format!("Found {} .to_string() calls — consider using &str", count),
269 }));
270
271 engine.add(Box::new(CountRule {
273 name: "vec-abuse",
274 pattern: "(macro_invocation macro: (identifier) @m (#eq? @m \"vec\"))",
275 threshold: 15,
276 severity: Severity::Mild,
277 languages: &[Language::Rust],
278 message_fn: |count| format!("Found {} vec![] calls — consider using arrays", count),
279 }));
280
281 engine.add(Box::new(CountRule {
283 name: "goto-abuse",
284 pattern: "(goto_statement) @goto",
285 threshold: 0,
286 severity: Severity::Spicy,
287 languages: &[Language::C, Language::Cpp],
288 message_fn: |count| {
289 format!(
290 "Found {} goto statements — Dijkstra is turning in his grave",
291 count
292 )
293 },
294 }));
295
296 engine.add(Box::new(CountRule {
298 name: "new-expression",
299 pattern: "(new_expression) @new",
300 threshold: 0,
301 severity: Severity::Spicy,
302 languages: &[Language::Cpp],
303 message_fn: |count| {
304 format!(
305 "Found {} new expressions — did you delete() everything?",
306 count
307 )
308 },
309 }));
310
311 engine.add(Box::new(CountRule {
314 name: "malloc-leak",
315 pattern: "(call_expression function: (identifier) @func (#match? @func \"^(malloc|curlx_malloc|Curl_cmalloc|zmalloc|zcalloc|zrealloc|ngx_alloc|ngx_palloc|ngx_pcalloc)$\"))",
316 threshold: 0,
317 severity: Severity::Spicy,
318 languages: &[Language::C, Language::Cpp],
319 message_fn: |count| {
320 format!(
321 "Found {} heap allocation calls — did you free() everything?",
322 count
323 )
324 },
325 }));
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::treesitter::engine::ParsedFile;
332 use crate::treesitter::query::collect_captures;
333 use crate::treesitter::rule::TreeSitterRule;
334 use crate::treesitter::TreeSitterEngine;
335 use std::path::Path;
336
337 fn parse_rust(code: &str) -> ParsedFile {
338 parse_rust_as("main.rs", code)
339 }
340
341 fn parse_rust_as(filename: &str, code: &str) -> ParsedFile {
342 let engine = TreeSitterEngine::new();
343 engine
344 .parse_file(Path::new(filename), code)
345 .expect("Should parse")
346 }
347
348 #[test]
351 fn test_unwrap_abuse_detection() {
352 let file = parse_rust(
353 r#"
354fn main() {
355 let a = x.unwrap();
356 let b = y.unwrap();
357 let c = z.unwrap();
358 let d = w.unwrap();
359}
360"#,
361 );
362 let rule = MethodCallRule {
363 name: "unwrap-abuse",
364 method_name: "unwrap",
365 threshold: 0,
366 severity_fn: |count| {
367 if count > 15 {
368 Severity::Nuclear
369 } else if count > 8 {
370 Severity::Spicy
371 } else {
372 Severity::Mild
373 }
374 },
375 message_fn: |count| format!("{} unwraps", count),
376 };
377 let issues = rule.check(&file);
378 assert_eq!(issues.len(), 1, "Should report one aggregated issue");
379 assert_eq!(issues[0].severity, Severity::Mild);
380 assert!(issues[0].message.contains("4"), "Should count 4 unwraps");
381 }
382
383 #[test]
386 fn test_unwrap_abuse_nuclear() {
387 let mut code = String::from("fn main() {\n");
388 for i in 0..16 {
389 code.push_str(&format!(" let x{} = v{}.unwrap();\n", i, i));
390 }
391 code.push('}');
392 let file = parse_rust(&code);
393 let rule = MethodCallRule {
394 name: "unwrap-abuse",
395 method_name: "unwrap",
396 threshold: 0,
397 severity_fn: |count| {
398 if count > 15 {
399 Severity::Nuclear
400 } else if count > 8 {
401 Severity::Spicy
402 } else {
403 Severity::Mild
404 }
405 },
406 message_fn: |count| format!("{} unwraps", count),
407 };
408 let issues = rule.check(&file);
409 assert_eq!(issues.len(), 1);
410 assert_eq!(issues[0].severity, Severity::Nuclear);
411 }
412
413 #[test]
416 fn test_async_abuse_detection() {
417 let file = parse_rust(
418 r#"
419async fn foo() {
420 let _ = async { 1 }.await;
421 let _ = async { 2 }.await;
422}
423"#,
424 );
425 let pattern = "(async_block) @block";
426 let captures = collect_captures(&file, pattern).expect("query should work");
427 let count: usize = captures.iter().map(|c| c.len()).sum();
428 assert!(
429 count >= 2,
430 "Should find at least 2 async blocks, found {}",
431 count
432 );
433 }
434
435 #[test]
438 fn test_macro_abuse_detection() {
439 let file = parse_rust(
440 r#"
441fn main() {
442 println!("a");
443 println!("b");
444 vec![1, 2, 3];
445}
446"#,
447 );
448 let pattern = "(macro_invocation) @m";
449 let captures = collect_captures(&file, pattern).expect("query should work");
450 let count: usize = captures.iter().map(|c| c.len()).sum();
451 assert!(
452 count >= 3,
453 "Should find at least 3 macro invocations, found {}",
454 count
455 );
456 }
457
458 #[test]
461 fn test_lifetime_detection() {
462 let file = parse_rust(
463 r#"
464fn foo<'a, 'b>(x: &'a str, y: &'b str) -> &'a str { x }
465"#,
466 );
467 let pattern = "(lifetime) @life";
468 let captures = collect_captures(&file, pattern).expect("query should work");
469 let count: usize = captures.iter().map(|c| c.len()).sum();
470 assert!(
471 count >= 2,
472 "Should find at least 2 lifetimes, found {}",
473 count
474 );
475 }
476
477 #[test]
480 fn test_deep_nesting_detection() {
481 let file = parse_rust(
482 r#"
483fn main() {
484 if true {
485 if true {
486 if true {
487 if true {
488 if true {
489 if true {
490 println!("deep");
491 }
492 }
493 }
494 }
495 }
496 }
497}
498"#,
499 );
500 let rule = DeepNestingRule;
501 let issues = rule.check(&file);
502 assert!(!issues.is_empty(), "Should detect deep nesting");
503 assert!(issues.iter().any(|i| i.rule_name == "deep-nesting"));
504 }
505
506 #[test]
509 fn test_deep_nesting_clean_code() {
510 let file = parse_rust(
511 r#"
512fn main() {
513 if true {
514 println!("shallow");
515 }
516}
517"#,
518 );
519 let rule = DeepNestingRule;
520 let issues = rule.check(&file);
521 assert!(
522 issues.is_empty(),
523 "Shallow nesting should not trigger, found {} issues",
524 issues.len()
525 );
526 }
527
528 #[test]
531 fn test_long_function_detection() {
532 let mut code = String::from("fn long_function() {\n");
533 for i in 0..90 {
534 code.push_str(&format!(" let x{} = {};\n", i, i));
535 }
536 code.push_str("}\n");
537 let file = parse_rust_as("main.rs", &code);
538 let rule = LongFunctionRule;
539 let issues = rule.check(&file);
540 assert!(!issues.is_empty(), "Should detect long function");
541 assert!(issues.iter().any(|i| i.rule_name == "long-function"));
542 }
543
544 #[test]
547 fn test_long_function_clean_code() {
548 let file = parse_rust(
549 r#"
550fn short_function() {
551 let x = 1;
552 let y = 2;
553 println!("{}", x + y);
554}
555"#,
556 );
557 let rule = LongFunctionRule;
558 let issues = rule.check(&file);
559 assert!(issues.is_empty(), "Short function should not trigger");
560 }
561
562 #[test]
565 fn test_god_function_detection() {
566 let file = parse_rust(
567 r#"
568fn god(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32) {
569 if a > 0 {
570 if b > 0 {
571 if c > 0 {
572 for x in 0..10 {
573 match d {
574 1 => {},
575 2 => {},
576 _ => {},
577 }
578 }
579 }
580 }
581 }
582 if a > 1 {
583 if b > 1 {
584 if c > 1 {
585 for x in 0..10 {
586 match d {
587 1 => {},
588 2 => {},
589 _ => {},
590 }
591 }
592 }
593 }
594 }
595 if a > 2 {
596 if b > 2 {
597 if c > 2 {
598 for x in 0..10 {
599 match d {
600 1 => {},
601 2 => {},
602 _ => {},
603 }
604 }
605 }
606 }
607 }
608}
609"#,
610 );
611 let rule = GodFunctionRule;
612 let issues = rule.check(&file);
613 assert!(!issues.is_empty(), "Should detect god function");
614 assert!(issues.iter().any(|i| i.rule_name == "god-function"));
615 }
616
617 #[test]
620 fn test_complex_closure_detection() {
621 let file = parse_rust(
622 r#"
623fn main() {
624 let f = |x| {
625 let g = |y| {
626 let h = |z| {
627 z + 1
628 };
629 h(y)
630 };
631 g(x)
632 };
633}
634"#,
635 );
636 let rule = ComplexClosureRule;
637 let issues = rule.check(&file);
638 assert!(!issues.is_empty(), "Should detect complex closure nesting");
639 assert!(issues.iter().any(|i| i.rule_name == "complex-closure"));
640 }
641
642 #[test]
645 fn test_module_complexity_detection() {
646 let file = parse_rust(
647 r#"
648mod outer {
649 mod inner {
650 fn foo() {}
651 }
652}
653"#,
654 );
655 let pattern = "(mod_item body: (declaration_list (mod_item) @nested))";
656 let captures = collect_captures(&file, pattern).expect("query should work");
657 let count: usize = captures.iter().map(|c| c.len()).sum();
658 assert!(count >= 1, "Should find nested module, found {}", count);
659 }
660
661 #[test]
664 fn test_terrible_naming_detection() {
665 let file = parse_rust(
666 r#"
667fn main() {
668 let data = 1;
669 let temp = 2;
670 let value = 3;
671}
672"#,
673 );
674 let rule = TerribleNamingRule;
675 let issues = rule.check(&file);
676 assert!(!issues.is_empty(), "Should detect terrible naming");
677 assert!(issues.iter().any(|i| i.rule_name == "terrible-naming"));
678 }
679
680 #[test]
683 fn test_terrible_naming_clean_code() {
684 let file = parse_rust(
685 r#"
686fn main() {
687 let user_count = 1;
688 let max_retries = 3;
689}
690"#,
691 );
692 let rule = TerribleNamingRule;
693 let issues = rule.check(&file);
694 assert!(issues.is_empty(), "Good names should not trigger");
695 }
696
697 #[test]
700 fn test_single_letter_variable_detection() {
701 let file = parse_rust(
702 r#"
703fn main() {
704 let q = 1;
705}
706"#,
707 );
708 let rule = SingleLetterTsRule;
709 let issues = rule.check(&file);
710 assert!(
711 !issues.is_empty(),
712 "Should detect single-letter variable 'q'"
713 );
714 assert!(issues
715 .iter()
716 .any(|i| i.rule_name == "single-letter-variable"));
717 }
718
719 #[test]
722 fn test_single_letter_allows_loop_counters() {
723 let file = parse_rust(
724 r#"
725fn main() {
726 for i in 0..10 {
727 for j in 0..5 {
728 let q = 42;
729 }
730 }
731}
732"#,
733 );
734 let rule = SingleLetterTsRule;
735 let issues = rule.check(&file);
736 let names: Vec<&str> = issues
737 .iter()
738 .map(|i| i.message.split('\'').nth(1).unwrap_or(""))
739 .collect();
740 assert!(
741 !names.contains(&"i"),
742 "Loop variable 'i' should be allowed (not in issues)"
743 );
744 assert!(
745 !names.contains(&"j"),
746 "Loop variable 'j' should be allowed (not in issues)"
747 );
748 assert!(
749 names.contains(&"q"),
750 "Standalone single-letter 'q' should still be flagged"
751 );
752 }
753
754 #[test]
757 fn test_panic_abuse_detection() {
758 let file = parse_rust(
759 r#"
760fn main() {
761 panic!("oh no");
762 panic!("again");
763 panic!("and again");
764}
765"#,
766 );
767 let rule = MacroRule {
768 name: "panic-abuse",
769 macro_name: "panic",
770 threshold: 2,
771 severity: Severity::Nuclear,
772 message_fn: |count| format!("{} panics", count),
773 };
774 let issues = rule.check(&file);
775 assert!(!issues.is_empty(), "Should detect panic abuse");
776 assert!(issues[0].message.contains("3"), "Should count 3 panics");
777 }
778
779 #[test]
782 fn test_hungarian_notation_detection() {
783 let file = parse_rust(
784 r#"
785fn main() {
786 let strName = "test";
787 let intCount = 42;
788}
789"#,
790 );
791 let rule = HungarianNotationTsRule;
792 let issues = rule.check(&file);
793 assert!(!issues.is_empty(), "Should detect Hungarian notation");
794 assert!(issues.iter().any(|i| i.rule_name == "hungarian-notation"));
795 }
796
797 #[test]
800 fn test_abbreviation_abuse_detection() {
801 let file = parse_rust(
802 r#"
803fn main() {
804 let mgr = get_manager();
805 let btn = get_button();
806}
807"#,
808 );
809 let rule = AbbreviationAbuseTsRule;
810 let issues = rule.check(&file);
811 assert!(!issues.is_empty(), "Should detect abbreviation abuse");
812 assert!(issues.iter().any(|i| i.rule_name == "abbreviation-abuse"));
813 }
814
815 #[test]
818 fn test_abbreviation_abuse_clean_code() {
819 let file = parse_rust(
820 r#"
821fn main() {
822 let manager = get_manager();
823 let button = get_button();
824}
825"#,
826 );
827 let rule = AbbreviationAbuseTsRule;
828 let issues = rule.check(&file);
829 assert!(
830 issues.is_empty(),
831 "Full words should not trigger abbreviation abuse"
832 );
833 }
834
835 #[test]
838 fn test_all_rules_fire_on_comprehensive_code() {
839 use crate::treesitter::rule::TreeSitterRuleEngine;
840 use crate::treesitter::TreeSitterEngine;
841
842 let code = r#"
843fn test_unwrap() {
844 let a = Some(1).unwrap();
845 let b = Some(2).unwrap();
846 let c = Some(3).unwrap();
847 let d = Some(4).unwrap();
848 let e = Some(5).unwrap();
849}
850
851fn test_clone() {
852 let x = vec![1, 2, 3];
853 let y = x.clone(); let z = y.clone(); let w = z.clone();
854 let v = w.clone(); let u = v.clone(); let t = u.clone();
855 let s = t.clone(); let r = s.clone(); let q = r.clone();
856 let p = q.clone(); let o = p.clone(); let n = o.clone();
857 let m = n.clone(); let l = m.clone(); let k = l.clone();
858 let j = k.clone(); let i = j.clone(); let h = i.clone();
859 let g = h.clone(); let f = g.clone(); let e = f.clone();
860 let d = e.clone(); let c = d.clone(); let b = c.clone();
861 let a = b.clone(); let aa = a.clone(); let bb = aa.clone();
862}
863
864fn test_async() {
865 let _ = async { 1 }; let _ = async { 2 }; let _ = async { 3 };
866 let _ = async { 4 }; let _ = async { 5 }; let _ = async { 6 };
867 let _ = async { 7 }; let _ = async { 8 }; let _ = async { 9 };
868 let _ = async { 10 }; let _ = async { 11 };
869}
870
871fn test_panic() {
872 panic!("a"); panic!("b"); panic!("c");
873}
874
875fn test_macros() {
876 println!("a"); println!("b"); println!("c"); println!("d"); println!("e");
877 println!("f"); println!("g"); println!("h"); println!("i"); println!("j");
878 println!("k"); println!("l"); println!("m"); println!("n"); println!("o");
879 println!("p"); println!("q"); println!("r"); println!("s"); println!("t");
880 println!("u");
881}
882
883fn test_nesting() {
884 if true { if true { if true { if true { if true { if true {
885 println!("deep");
886 } } } } } }
887}
888
889fn test_long_fn() {
890 let x0 = 0;
891 let x1 = 1;
892 let x2 = 2;
893 let x3 = 3;
894 let x4 = 4;
895 let x5 = 5;
896 let x6 = 6;
897 let x7 = 7;
898 let x8 = 8;
899 let x9 = 9;
900 let x10 = 10;
901 let x11 = 11;
902 let x12 = 12;
903 let x13 = 13;
904 let x14 = 14;
905 let x15 = 15;
906 let x16 = 16;
907 let x17 = 17;
908 let x18 = 18;
909 let x19 = 19;
910 let x20 = 20;
911 let x21 = 21;
912 let x22 = 22;
913 let x23 = 23;
914 let x24 = 24;
915 let x25 = 25;
916 let x26 = 26;
917 let x27 = 27;
918 let x28 = 28;
919 let x29 = 29;
920 let x30 = 30;
921 let x31 = 31;
922 let x32 = 32;
923 let x33 = 33;
924 let x34 = 34;
925 let x35 = 35;
926 let x36 = 36;
927 let x37 = 37;
928 let x38 = 38;
929 let x39 = 39;
930 let x40 = 40;
931 let x41 = 41;
932 let x42 = 42;
933 let x43 = 43;
934 let x44 = 44;
935 let x45 = 45;
936 let x46 = 46;
937 let x47 = 47;
938 let x48 = 48;
939 let x49 = 49;
940 let x50 = 50;
941 let x51 = 51;
942 let x52 = 52;
943 let x53 = 53;
944 let x54 = 54;
945 let x55 = 55;
946 let x56 = 56;
947 let x57 = 57;
948 let x58 = 58;
949 let x59 = 59;
950 let x60 = 60;
951 let x61 = 61;
952 let x62 = 62;
953 let x63 = 63;
954 let x64 = 64;
955 let x65 = 65;
956 let x66 = 66;
957 let x67 = 67;
958 let x68 = 68;
959 let x69 = 69;
960 let x70 = 70;
961 let x71 = 71;
962 let x72 = 72;
963 let x73 = 73;
964 let x74 = 74;
965 let x75 = 75;
966 let x76 = 76;
967 let x77 = 77;
968 let x78 = 78;
969 let x79 = 79;
970 let x80 = 80;
971 let x81 = 81;
972 let x82 = 82;
973 let x83 = 83;
974 let x84 = 84;
975 let x85 = 85;
976}
977
978fn test_god(a:i32,b:i32,c:i32,d:i32,e:i32,f:i32,g:i32) {
979 if a>0{if b>0{if c>0{for x in 0..10{match d{1=>{},2=>{},_=>{}}}}}}
980 if a>1{if b>1{if c>1{for x in 0..10{match d{1=>{},2=>{},_=>{}}}}}}
981 if a>2{if b>2{if c>2{for x in 0..10{match d{1=>{},2=>{},_=>{}}}}}}
982 if a>3{if b>3{if c>3{for x in 0..10{match d{1=>{},2=>{},_=>{}}}}}}
983 if a>4{if b>4{if c>4{for x in 0..10{match d{1=>{},2=>{},_=>{}}}}}}
984}
985
986fn test_closure() {
987 let f = |x| { let g = |y| { let h = |z| { z + 1 }; h(y) }; g(x) };
988}
989
990fn test_naming() { let data = 1; let temp = 2; let value = 3; let info = 4; }
991
992fn test_single_letter() { let q = 1; let m = 2; }
993
994mod outer { mod inner { fn foo() {} } }
995
996fn test_lifetime<'a,'b,'c,'d,'e,'f,'g,'h,'i,'j,'k,'l,'m,'n,'o,'p,'q,'r,'s,'t,'u,'v>(x:&'a str)->&'b str{x}
997
998fn test_generics<T,U,V,W,X,Y,Z>(a:T,b:U,c:V,d:W,e:X,f:Y,g:Z)->T{a}
999"#;
1000
1001 let engine = TreeSitterEngine::new();
1002 let file = engine
1003 .parse_file(Path::new("comprehensive.rs"), code)
1004 .expect("Should parse");
1005
1006 let mut ts_engine = TreeSitterRuleEngine::new();
1007 register_rust_rules(&mut ts_engine);
1008
1009 let issues = ts_engine.check_file(&file, false);
1010 let rule_names: std::collections::HashSet<&str> =
1011 issues.iter().map(|i| i.rule_name.as_str()).collect();
1012
1013 let expected_rules = [
1014 "unwrap-abuse",
1015 "unnecessary-clone",
1016 "async-abuse",
1017 "panic-abuse",
1018 "macro-abuse",
1019 "deep-nesting",
1020 "long-function",
1021 "god-function",
1022 "complex-closure",
1023 "terrible-naming",
1024 "single-letter-variable",
1025 "module-complexity",
1026 "lifetime-abuse",
1027 "generic-abuse",
1028 ];
1029
1030 let mut missing = Vec::new();
1031 for rule in &expected_rules {
1032 if !rule_names.contains(rule) {
1033 missing.push(*rule);
1034 }
1035 }
1036
1037 assert!(
1038 missing.is_empty(),
1039 "These tree-sitter rules did NOT fire: {:?}\nRules that did fire: {:?}\nTotal issues: {}",
1040 missing,
1041 rule_names,
1042 issues.len()
1043 );
1044
1045 let unwrap_count = issues
1047 .iter()
1048 .filter(|i| i.rule_name == "unwrap-abuse")
1049 .count();
1050 assert!(
1051 unwrap_count >= 1,
1052 "unwrap-abuse should produce at least 1 issue, got {}",
1053 unwrap_count
1054 );
1055
1056 let clone_count = issues
1057 .iter()
1058 .filter(|i| i.rule_name == "unnecessary-clone")
1059 .count();
1060 assert!(
1061 clone_count >= 1,
1062 "unnecessary-clone should produce at least 1 issue, got {}",
1063 clone_count
1064 );
1065
1066 let deep_count = issues
1067 .iter()
1068 .filter(|i| i.rule_name == "deep-nesting")
1069 .count();
1070 assert!(
1071 deep_count >= 1,
1072 "deep-nesting should produce at least 1 issue, got {}",
1073 deep_count
1074 );
1075
1076 let long_count = issues
1077 .iter()
1078 .filter(|i| i.rule_name == "long-function")
1079 .count();
1080 assert!(
1081 long_count >= 1,
1082 "long-function should produce at least 1 issue, got {}",
1083 long_count
1084 );
1085
1086 let god_count = issues
1087 .iter()
1088 .filter(|i| i.rule_name == "god-function")
1089 .count();
1090 assert!(
1091 god_count >= 1,
1092 "god-function should produce at least 1 issue, got {}",
1093 god_count
1094 );
1095
1096 let closure_count = issues
1097 .iter()
1098 .filter(|i| i.rule_name == "complex-closure")
1099 .count();
1100 assert!(
1101 closure_count >= 1,
1102 "complex-closure should produce at least 1 issue, got {}",
1103 closure_count
1104 );
1105
1106 println!(
1107 "All {} tree-sitter rules fired! Total issues: {}",
1108 expected_rules.len(),
1109 issues.len()
1110 );
1111 }
1112}