1#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
8#![allow(
15 clippy::cast_precision_loss,
16 clippy::cast_possible_truncation,
17 clippy::cast_sign_loss
18)]
19
20use std::fmt;
21
22use crate::checker::Checker;
23use crate::macros::implement_metric_trait;
24use crate::*;
25
26#[derive(Debug, Clone, PartialEq)]
31#[non_exhaustive]
32pub struct Stats {
33 exit: usize,
34 exit_sum: usize,
35 total_space_functions: usize,
36 exit_min: usize,
37 exit_max: usize,
38}
39
40impl Default for Stats {
41 fn default() -> Self {
42 Self {
43 exit: 0,
44 exit_sum: 0,
45 total_space_functions: 1,
46 exit_min: usize::MAX,
47 exit_max: 0,
48 }
49 }
50}
51
52impl fmt::Display for Stats {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 write!(
55 f,
56 "sum: {}, average: {} min: {}, max: {}",
57 self.nexits_sum(),
58 self.nexits_average(),
59 self.nexits_min(),
60 self.nexits_max()
61 )
62 }
63}
64
65impl Stats {
66 pub fn merge(&mut self, other: &Stats) {
68 self.exit_max = self.exit_max.max(other.exit_max);
69 self.exit_min = self.exit_min.min(other.exit_min);
70 self.exit_sum += other.exit_sum;
71 }
72
73 #[must_use]
75 pub fn nexits(&self) -> u64 {
76 self.exit as u64
77 }
78 #[must_use]
80 pub fn nexits_sum(&self) -> u64 {
81 self.exit_sum as u64
82 }
83 #[must_use]
89 pub fn nexits_min(&self) -> u64 {
90 if self.exit_min == usize::MAX {
91 0
92 } else {
93 self.exit_min as u64
94 }
95 }
96 #[must_use]
98 pub fn nexits_max(&self) -> u64 {
99 self.exit_max as u64
100 }
101
102 #[must_use]
113 pub fn nexits_average(&self) -> f64 {
114 crate::metrics::average(self.nexits_sum() as f64, self.total_space_functions)
115 }
116 #[inline]
117 pub(crate) fn compute_sum(&mut self) {
118 self.exit_sum += self.exit;
119 }
120 #[inline]
121 pub(crate) fn compute_minmax(&mut self) {
122 self.exit_max = self.exit_max.max(self.exit);
123 self.exit_min = self.exit_min.min(self.exit);
124 self.compute_sum();
125 }
126 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
127 self.total_space_functions = total_space_functions;
128 }
129}
130
131#[doc(hidden)]
132pub(crate) trait Exit
134where
135 Self: Checker,
136{
137 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
140}
141
142macro_rules! impl_exit_match_kinds {
146 ($code:ty, $lang:ident, [$($kind:ident),+ $(,)?]) => {
147 impl Exit for $code {
148 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
149 if matches!(node.kind_id().into(), $($lang::$kind)|+) {
150 stats.exit += 1;
151 }
152 }
153 }
154 };
155}
156
157impl_exit_match_kinds!(PythonCode, Python, [ReturnStatement, RaiseStatement, Yield]);
162impl_exit_match_kinds!(
165 MozjsCode,
166 Mozjs,
167 [ReturnStatement, ThrowStatement, YieldExpression]
168);
169impl_exit_match_kinds!(
170 JavascriptCode,
171 Javascript,
172 [ReturnStatement, ThrowStatement, YieldExpression]
173);
174impl_exit_match_kinds!(
175 TypescriptCode,
176 Typescript,
177 [ReturnStatement, ThrowStatement, YieldExpression]
178);
179impl_exit_match_kinds!(
180 TsxCode,
181 Tsx,
182 [ReturnStatement, ThrowStatement, YieldExpression]
183);
184impl_exit_match_kinds!(CppCode, Cpp, [ReturnStatement, ThrowStatement]);
185impl_exit_match_kinds!(MozcppCode, Mozcpp, [ReturnStatement, ThrowStatement]);
186impl_exit_match_kinds!(CCode, C, [ReturnStatement]);
188impl_exit_match_kinds!(ObjcCode, Objc, [ReturnStatement, ThrowStatement]);
191impl_exit_match_kinds!(
197 JavaCode,
198 Java,
199 [ReturnStatement, ThrowStatement, YieldStatement]
200);
201impl_exit_match_kinds!(
205 GroovyCode,
206 Groovy,
207 [ReturnStatement, ThrowStatement, YieldStatement]
208);
209
210impl Exit for RustCode {
211 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
212 if matches!(
218 node.kind_id().into(),
219 Rust::ReturnExpression | Rust::TryExpression
220 ) {
221 stats.exit += 1;
222 }
223 }
224}
225
226impl Exit for CsharpCode {
227 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
228 if matches!(
229 node.kind_id().into(),
230 Csharp::ReturnStatement
231 | Csharp::YieldStatement
232 | Csharp::ThrowStatement
233 | Csharp::ThrowExpression
234 ) {
235 stats.exit += 1;
236 }
237 }
238}
239
240impl Exit for GoCode {
241 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
250 if matches!(node.kind_id().into(), Go::ReturnStatement) {
251 stats.exit += 1;
252 } else if node.kind_id() == Go::CallExpression
253 && let Some(function) = node.child_by_field_name("function")
254 && function.kind_id() == Go::Identifier
255 && function.utf8_text(code) == Some("panic")
256 {
257 stats.exit += 1;
258 }
259 }
260}
261
262impl Exit for PerlCode {
263 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
264 if node.kind_id() == Perl::ReturnExpression {
265 stats.exit += 1;
266 }
267 }
268}
269
270impl Exit for KotlinCode {
271 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
272 if matches!(
273 node.kind_id().into(),
274 Kotlin::ReturnExpression | Kotlin::ThrowExpression
275 ) {
276 stats.exit += 1;
277 }
278 }
279}
280
281impl Exit for LuaCode {
282 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
291 if node.kind_id() == Lua::ReturnStatement {
292 stats.exit += 1;
293 } else if node.kind_id() == Lua::FunctionCall
294 && let Some(name) = node.child_by_field_name("name")
295 && matches!(name.utf8_text(code), Some("error" | "os.exit"))
296 {
297 stats.exit += 1;
298 }
299 }
300}
301
302impl Exit for BashCode {
303 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
304 if matches!(node.kind_id().into(), Bash::Command)
309 && let Some(name) = node.child_by_field_name("name")
310 && matches!(name.utf8_text(code), Some("return" | "exit"))
311 {
312 stats.exit += 1;
313 }
314 }
315}
316
317impl Exit for TclCode {
318 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
319 if node.kind_id() == Tcl::Command
322 && let Some(name) = node.child_by_field_name("name")
323 && name.kind_id() == Tcl::SimpleWord
324 && name.utf8_text(code) == Some("return")
325 {
326 stats.exit += 1;
327 }
328 }
329}
330
331impl Exit for IrulesCode {
332 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
333 if node.kind_id() == Irules::Command
342 && let Some(name) = node.child_by_field_name("name")
343 && name.utf8_text(code) == Some("return")
344 {
345 stats.exit += 1;
346 }
347 }
348}
349
350impl Exit for PhpCode {
351 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
359 if matches!(
360 node.kind_id().into(),
361 Php::ReturnStatement | Php::YieldExpression | Php::ThrowExpression | Php::ExitStatement
362 ) {
363 stats.exit += 1;
364 }
365 }
366}
367
368implement_metric_trait!(Exit, PreprocCode, CcommentCode);
370
371impl Exit for RubyCode {
372 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
380 if matches!(node.kind_id().into(), Ruby::Return | Ruby::Return2) {
381 stats.exit += 1;
382 }
383 }
384}
385
386impl Exit for ElixirCode {
387 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
393 if node.kind_id() == Elixir::Call
394 && let Some(target) = node.child_by_field_name("target")
395 && target.kind_id() == Elixir::Identifier
396 && matches!(
397 target.utf8_text(code),
398 Some("throw" | "raise" | "reraise" | "exit")
399 )
400 {
401 stats.exit += 1;
402 }
403 }
404}
405
406#[cfg(test)]
407#[allow(
408 clippy::float_cmp,
409 clippy::cast_precision_loss,
410 clippy::cast_possible_truncation,
411 clippy::cast_sign_loss,
412 clippy::similar_names,
413 clippy::doc_markdown,
414 clippy::needless_raw_string_hashes,
415 clippy::too_many_lines
416)]
417mod tests {
418 use crate::test_support::{
419 check_func_space_only_shim, check_metrics_only_shim, child_space, function_space,
420 };
421
422 use super::*;
423
424 check_metrics_only_shim!(check_metrics, Nexits);
428 check_func_space_only_shim!(check_func_space, Nexits);
429
430 #[test]
435 fn exit_empty_file_min_is_zero() {
436 let stats = Stats::default();
437 assert_eq!(stats.nexits_min(), 0);
438 }
439
440 #[test]
441 fn python_no_exit() {
442 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
443 insta::assert_json_snapshot!(
445 metric.nexits,
446 @r#"
447 {
448 "sum": 0,
449 "average": 0.0,
450 "min": 0,
451 "max": 0
452 }
453 "#
454 );
455 });
456 }
457
458 #[test]
459 fn rust_no_exit() {
460 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
461 insta::assert_json_snapshot!(
463 metric.nexits,
464 @r#"
465 {
466 "sum": 0,
467 "average": 0.0,
468 "min": 0,
469 "max": 0
470 }
471 "#
472 );
473 });
474 }
475
476 #[test]
477 fn rust_question_mark() {
478 check_metrics::<RustParser>("let _ = a? + b? + c?;", "foo.rs", |metric| {
479 insta::assert_json_snapshot!(
481 metric.nexits,
482 @r#"
483 {
484 "sum": 3,
485 "average": 3.0,
486 "min": 3,
487 "max": 3
488 }
489 "#
490 );
491 });
492 }
493
494 #[test]
502 fn rust_explicit_return_with_return_type() {
503 check_metrics::<RustParser>("fn foo() -> i32 { return 1; }", "foo.rs", |metric| {
504 insta::assert_json_snapshot!(
506 metric.nexits,
507 @r#"
508 {
509 "sum": 1,
510 "average": 1.0,
511 "min": 0,
512 "max": 1
513 }
514 "#
515 );
516 });
517 }
518
519 #[test]
523 fn rust_implicit_return_not_counted() {
524 check_metrics::<RustParser>("fn foo() -> i32 { 0 }", "foo.rs", |metric| {
525 insta::assert_json_snapshot!(
527 metric.nexits,
528 @r#"
529 {
530 "sum": 0,
531 "average": 0.0,
532 "min": 0,
533 "max": 0
534 }
535 "#
536 );
537 });
538 }
539
540 #[test]
544 fn rust_mixed_explicit_and_implicit_return() {
545 check_metrics::<RustParser>(
546 "fn foo(x: bool) -> i32 { if x { return 1; } 0 }",
547 "foo.rs",
548 |metric| {
549 insta::assert_json_snapshot!(
551 metric.nexits,
552 @r#"
553 {
554 "sum": 1,
555 "average": 1.0,
556 "min": 0,
557 "max": 1
558 }
559 "#
560 );
561 },
562 );
563 }
564
565 #[test]
569 fn rust_question_mark_in_function() {
570 check_metrics::<RustParser>(
571 "fn foo() -> Result<i32, ()> { Ok(do_thing()?) }",
572 "foo.rs",
573 |metric| {
574 insta::assert_json_snapshot!(
576 metric.nexits,
577 @r#"
578 {
579 "sum": 1,
580 "average": 1.0,
581 "min": 0,
582 "max": 1
583 }
584 "#
585 );
586 },
587 );
588 }
589
590 #[test]
593 fn rust_unit_return_no_exit() {
594 check_metrics::<RustParser>("fn foo() { let _x = 1; }", "foo.rs", |metric| {
595 insta::assert_json_snapshot!(
597 metric.nexits,
598 @r#"
599 {
600 "sum": 0,
601 "average": 0.0,
602 "min": 0,
603 "max": 0
604 }
605 "#
606 );
607 });
608 }
609
610 #[test]
611 fn c_no_exit() {
612 check_metrics::<CParser>("int a = 42;", "foo.c", |metric| {
613 insta::assert_json_snapshot!(
615 metric.nexits,
616 @r#"
617 {
618 "sum": 0,
619 "average": 0.0,
620 "min": 0,
621 "max": 0
622 }
623 "#
624 );
625 });
626 }
627
628 #[test]
631 fn c_multiple_returns_in_branches() {
632 check_metrics::<CParser>(
633 "int f(int x) {
634 if (x < 0) {
635 return -1;
636 } else if (x == 0) {
637 return 0;
638 } else {
639 return 1;
640 }
641 }",
642 "foo.c",
643 |metric| {
644 assert_eq!(metric.nexits.nexits_sum(), 3);
646 assert_eq!(metric.nexits.nexits_max(), 3);
647 insta::assert_json_snapshot!(
648 metric.nexits,
649 @r#"
650 {
651 "sum": 3,
652 "average": 3.0,
653 "min": 0,
654 "max": 3
655 }
656 "#
657 );
658 },
659 );
660 }
661
662 #[test]
671 fn c_keyword_identifiers_parse_and_returns_count() {
672 use std::path::PathBuf;
673
674 let source = "int process(int new, int class) {
675 int delete = new + class;
676 if (delete > 0) {
677 return delete;
678 }
679 return 0;
680 }";
681 let parser = CParser::new(source.as_bytes().to_vec(), &PathBuf::from("foo.c"), None);
682 assert!(
683 !parser.root().has_error(),
684 "C grammar must parse C++-keyword identifiers without an error cascade"
685 );
686
687 check_metrics::<CParser>(source, "foo.c", |metric| {
688 assert_eq!(metric.nom.functions_sum(), 1);
689 assert_eq!(metric.nexits.nexits_sum(), 2);
690 });
691 }
692
693 #[test]
697 fn cpp_return_in_try_catch() {
698 check_metrics::<CppParser>(
699 "int f(int x) {
700 try {
701 if (x == 0) {
702 return 1;
703 }
704 return 2;
705 } catch (...) {
706 return -1;
707 }
708 }",
709 "foo.cpp",
710 |metric| {
711 assert_eq!(metric.nexits.nexits_sum(), 3);
714 assert_eq!(metric.nexits.nexits_max(), 3);
715 insta::assert_json_snapshot!(
716 metric.nexits,
717 @r#"
718 {
719 "sum": 3,
720 "average": 3.0,
721 "min": 0,
722 "max": 3
723 }
724 "#
725 );
726 },
727 );
728 }
729
730 #[test]
733 fn c_early_return_in_loop() {
734 check_metrics::<CParser>(
735 "int find(int* a, int n, int target) {
736 for (int i = 0; i < n; ++i) {
737 if (a[i] == target) {
738 return i;
739 }
740 }
741 return -1;
742 }",
743 "foo.c",
744 |metric| {
745 assert_eq!(metric.nexits.nexits_sum(), 2);
747 assert_eq!(metric.nexits.nexits_max(), 2);
748 insta::assert_json_snapshot!(
749 metric.nexits,
750 @r#"
751 {
752 "sum": 2,
753 "average": 2.0,
754 "min": 0,
755 "max": 2
756 }
757 "#
758 );
759 },
760 );
761 }
762
763 #[test]
766 fn c_void_no_explicit_return() {
767 check_metrics::<CParser>(
768 "void greet(const char* who) {
769 printf(\"hi %s\\n\", who);
770 }",
771 "foo.c",
772 |metric| {
773 assert_eq!(metric.nexits.nexits_sum(), 0);
775 assert_eq!(metric.nexits.nexits_max(), 0);
776 insta::assert_json_snapshot!(
777 metric.nexits,
778 @r#"
779 {
780 "sum": 0,
781 "average": 0.0,
782 "min": 0,
783 "max": 0
784 }
785 "#
786 );
787 },
788 );
789 }
790
791 #[test]
792 fn javascript_no_exit() {
793 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
794 insta::assert_json_snapshot!(
796 metric.nexits,
797 @r#"
798 {
799 "sum": 0,
800 "average": 0.0,
801 "min": 0,
802 "max": 0
803 }
804 "#
805 );
806 });
807 }
808
809 #[test]
810 fn javascript_simple_function() {
811 check_metrics::<JavascriptParser>(
812 "function f(a, b) {
813 if (a) {
814 return a;
815 }
816 return b;
817 }",
818 "foo.js",
819 |metric| {
820 insta::assert_json_snapshot!(
822 metric.nexits,
823 @r#"
824 {
825 "sum": 2,
826 "average": 2.0,
827 "min": 0,
828 "max": 2
829 }
830 "#
831 );
832 },
833 );
834 }
835
836 #[test]
837 fn javascript_nested_functions() {
838 check_metrics::<JavascriptParser>(
839 "function outer() {
840 function inner() {
841 return 1;
842 }
843 return inner();
844 }",
845 "foo.js",
846 |metric| {
847 insta::assert_json_snapshot!(
849 metric.nexits,
850 @r#"
851 {
852 "sum": 2,
853 "average": 1.0,
854 "min": 0,
855 "max": 1
856 }
857 "#
858 );
859 },
860 );
861 }
862
863 #[test]
864 fn python_simple_function() {
865 check_metrics::<PythonParser>(
866 "def f(a, b):
867 if a:
868 return a",
869 "foo.py",
870 |metric| {
871 insta::assert_json_snapshot!(
873 metric.nexits,
874 @r#"
875 {
876 "sum": 1,
877 "average": 1.0,
878 "min": 0,
879 "max": 1
880 }
881 "#
882 );
883 },
884 );
885 }
886
887 #[test]
888 fn python_more_functions() {
889 check_metrics::<PythonParser>(
890 "def f(a, b):
891 if a:
892 return a
893 def f(a, b):
894 if b:
895 return b",
896 "foo.py",
897 |metric| {
898 insta::assert_json_snapshot!(
900 metric.nexits,
901 @r#"
902 {
903 "sum": 2,
904 "average": 1.0,
905 "min": 0,
906 "max": 1
907 }
908 "#
909 );
910 },
911 );
912 }
913
914 #[test]
915 fn python_nested_functions() {
916 check_metrics::<PythonParser>(
917 "def f(a, b):
918 def foo(a):
919 if a:
920 return 1
921 bar = lambda a: lambda b: b or True or True
922 return bar(foo(a))(a)",
923 "foo.py",
924 |metric| {
925 insta::assert_json_snapshot!(
927 metric.nexits,
928 @r#"
929 {
930 "sum": 2,
931 "average": 0.5,
932 "min": 0,
933 "max": 1
934 }
935 "#
936 );
937 },
938 );
939 }
940
941 #[test]
942 fn java_no_exit() {
943 check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
944 insta::assert_json_snapshot!(
946 metric.nexits,
947 @r#"
948 {
949 "sum": 0,
950 "average": 0.0,
951 "min": 0,
952 "max": 0
953 }
954 "#
955 );
956 });
957 }
958
959 #[test]
960 fn java_simple_function() {
961 check_metrics::<JavaParser>(
962 "class A {
963 public int sum(int x, int y) {
964 return x + y;
965 }
966 }",
967 "foo.java",
968 |metric| {
969 insta::assert_json_snapshot!(
971 metric.nexits,
972 @r#"
973 {
974 "sum": 1,
975 "average": 1.0,
976 "min": 0,
977 "max": 1
978 }
979 "#
980 );
981 },
982 );
983 }
984
985 #[test]
986 fn go_no_return() {
987 check_metrics::<GoParser>(
988 "package main
989 func f() {
990 x := 1
991 _ = x
992 }",
993 "foo.go",
994 |metric| {
995 insta::assert_json_snapshot!(
997 metric.nexits,
998 @r#"
999 {
1000 "sum": 0,
1001 "average": 0.0,
1002 "min": 0,
1003 "max": 0
1004 }
1005 "#
1006 );
1007 },
1008 );
1009 }
1010
1011 #[test]
1012 fn go_single_return() {
1013 check_metrics::<GoParser>(
1014 "package main
1015 func f() int {
1016 return 1
1017 }",
1018 "foo.go",
1019 |metric| {
1020 insta::assert_json_snapshot!(
1021 metric.nexits,
1022 @r#"
1023 {
1024 "sum": 1,
1025 "average": 1.0,
1026 "min": 0,
1027 "max": 1
1028 }
1029 "#
1030 );
1031 },
1032 );
1033 }
1034
1035 #[test]
1036 fn go_multiple_returns() {
1037 check_metrics::<GoParser>(
1038 "package main
1039 func f(x int) int {
1040 if x > 0 {
1041 return 1
1042 }
1043 if x < 0 {
1044 return -1
1045 }
1046 return 0
1047 }",
1048 "foo.go",
1049 |metric| {
1050 insta::assert_json_snapshot!(
1052 metric.nexits,
1053 @r#"
1054 {
1055 "sum": 3,
1056 "average": 3.0,
1057 "min": 0,
1058 "max": 3
1059 }
1060 "#
1061 );
1062 },
1063 );
1064 }
1065
1066 #[test]
1067 fn go_naked_return() {
1068 check_metrics::<GoParser>(
1069 "package main
1070 func f() (x int) {
1071 x = 1
1072 return
1073 }",
1074 "foo.go",
1075 |metric| {
1076 insta::assert_json_snapshot!(
1078 metric.nexits,
1079 @r#"
1080 {
1081 "sum": 1,
1082 "average": 1.0,
1083 "min": 0,
1084 "max": 1
1085 }
1086 "#
1087 );
1088 },
1089 );
1090 }
1091
1092 #[test]
1093 fn go_multivalue_return() {
1094 check_metrics::<GoParser>(
1095 "package main
1096 func f() (int, error) {
1097 return 0, nil
1098 }",
1099 "foo.go",
1100 |metric| {
1101 insta::assert_json_snapshot!(
1103 metric.nexits,
1104 @r#"
1105 {
1106 "sum": 1,
1107 "average": 1.0,
1108 "min": 0,
1109 "max": 1
1110 }
1111 "#
1112 );
1113 },
1114 );
1115 }
1116
1117 #[test]
1118 fn go_panic_counts_as_exit() {
1119 check_metrics::<GoParser>(
1120 "package main
1121 func f() {
1122 panic(\"boom\")
1123 }",
1124 "foo.go",
1125 |metric| {
1126 insta::assert_json_snapshot!(
1129 metric.nexits,
1130 @r#"
1131 {
1132 "sum": 1,
1133 "average": 1.0,
1134 "min": 0,
1135 "max": 1
1136 }
1137 "#
1138 );
1139 },
1140 );
1141 }
1142
1143 #[test]
1144 fn go_panic_and_return_both_count() {
1145 check_metrics::<GoParser>(
1146 "package main
1147 func f(x int) int {
1148 if x < 0 {
1149 panic(\"negative\")
1150 }
1151 return x
1152 }",
1153 "foo.go",
1154 |metric| {
1155 insta::assert_json_snapshot!(
1157 metric.nexits,
1158 @r#"
1159 {
1160 "sum": 2,
1161 "average": 2.0,
1162 "min": 0,
1163 "max": 2
1164 }
1165 "#
1166 );
1167 },
1168 );
1169 }
1170
1171 #[test]
1172 fn go_package_qualified_panic_is_not_exit() {
1173 check_metrics::<GoParser>(
1174 "package main
1175 func f() {
1176 foo.panic()
1177 }",
1178 "foo.go",
1179 |metric| {
1180 insta::assert_json_snapshot!(
1184 metric.nexits,
1185 @r#"
1186 {
1187 "sum": 0,
1188 "average": 0.0,
1189 "min": 0,
1190 "max": 0
1191 }
1192 "#
1193 );
1194 },
1195 );
1196 }
1197
1198 #[test]
1199 fn java_split_function() {
1200 check_metrics::<JavaParser>(
1201 "class A {
1202 public int multiply(int x, int y) {
1203 if(x == 0 || y == 0){
1204 return 0;
1205 }
1206 return x * y;
1207 }
1208 }",
1209 "foo.java",
1210 |metric| {
1211 insta::assert_json_snapshot!(
1213 metric.nexits,
1214 @r#"
1215 {
1216 "sum": 2,
1217 "average": 2.0,
1218 "min": 0,
1219 "max": 2
1220 }
1221 "#
1222 );
1223 },
1224 );
1225 }
1226
1227 #[test]
1228 fn csharp_no_exit() {
1229 check_metrics::<CsharpParser>("int a = 42;", "foo.cs", |metric| {
1230 insta::assert_json_snapshot!(
1231 metric.nexits,
1232 @r#"
1233 {
1234 "sum": 0,
1235 "average": 0.0,
1236 "min": 0,
1237 "max": 0
1238 }
1239 "#
1240 );
1241 });
1242 }
1243
1244 #[test]
1245 fn csharp_simple_function() {
1246 check_metrics::<CsharpParser>(
1247 "class A {
1248 public int Sum(int x, int y) {
1249 return x + y;
1250 }
1251 }",
1252 "foo.cs",
1253 |metric| {
1254 insta::assert_json_snapshot!(
1255 metric.nexits,
1256 @r#"
1257 {
1258 "sum": 1,
1259 "average": 1.0,
1260 "min": 0,
1261 "max": 1
1262 }
1263 "#
1264 );
1265 },
1266 );
1267 }
1268
1269 #[test]
1270 fn csharp_split_function() {
1271 check_metrics::<CsharpParser>(
1272 "class A {
1273 public int Multiply(int x, int y) {
1274 if (x == 0 || y == 0) {
1275 return 0;
1276 }
1277 return x * y;
1278 }
1279 }",
1280 "foo.cs",
1281 |metric| {
1282 insta::assert_json_snapshot!(
1283 metric.nexits,
1284 @r#"
1285 {
1286 "sum": 2,
1287 "average": 2.0,
1288 "min": 0,
1289 "max": 2
1290 }
1291 "#
1292 );
1293 },
1294 );
1295 }
1296
1297 #[test]
1298 fn csharp_yield_and_throw() {
1299 check_metrics::<CsharpParser>(
1300 "class A {
1301 public IEnumerable<int> Gen() {
1302 yield return 1;
1303 yield break;
1304 }
1305 public int Bad(int x) {
1306 if (x < 0) throw new System.Exception();
1307 return x;
1308 }
1309 }",
1310 "foo.cs",
1311 |metric| {
1312 insta::assert_json_snapshot!(
1314 metric.nexits,
1315 @r#"
1316 {
1317 "sum": 4,
1318 "average": 2.0,
1319 "min": 0,
1320 "max": 2
1321 }
1322 "#
1323 );
1324 },
1325 );
1326 }
1327
1328 #[test]
1329 fn perl_no_exit() {
1330 check_metrics::<PerlParser>(
1331 "sub f {
1332 print 'hi';
1333 }",
1334 "foo.pl",
1335 |metric| {
1336 insta::assert_json_snapshot!(
1337 metric.nexits,
1338 @r#"
1339 {
1340 "sum": 0,
1341 "average": 0.0,
1342 "min": 0,
1343 "max": 0
1344 }
1345 "#
1346 );
1347 },
1348 );
1349 }
1350
1351 #[test]
1352 fn perl_no_function_no_exit() {
1353 check_metrics::<PerlParser>("my $x = 1;\nprint $x;\n", "foo.pl", |metric| {
1354 insta::assert_json_snapshot!(metric.nexits, @r#"
1355 {
1356 "sum": 0,
1357 "average": 0.0,
1358 "min": 0,
1359 "max": 0
1360 }
1361 "#);
1362 });
1363 }
1364
1365 #[test]
1366 fn perl_multiple_returns() {
1367 check_metrics::<PerlParser>(
1368 "sub f {
1369 return 1 if $_[0];
1370 return 0;
1371 }",
1372 "foo.pl",
1373 |metric| {
1374 insta::assert_json_snapshot!(
1375 metric.nexits,
1376 @r#"
1377 {
1378 "sum": 2,
1379 "average": 2.0,
1380 "min": 0,
1381 "max": 2
1382 }
1383 "#
1384 );
1385 },
1386 );
1387 }
1388
1389 #[test]
1390 fn tsx_function_with_returns() {
1391 check_metrics::<TsxParser>(
1392 "function clamp(val: number, min: number, max: number) {
1393 if (val < min) {
1394 return min;
1395 }
1396 if (val > max) {
1397 return max;
1398 }
1399 return val;
1400 }",
1401 "foo.tsx",
1402 |metric| {
1403 insta::assert_json_snapshot!(
1404 metric.nexits,
1405 @r#"
1406 {
1407 "sum": 3,
1408 "average": 3.0,
1409 "min": 0,
1410 "max": 3
1411 }
1412 "#
1413 );
1414 },
1415 );
1416 }
1417
1418 #[test]
1419 fn typescript_no_exit() {
1420 check_metrics::<TypescriptParser>("const x: number = 42;", "foo.ts", |metric| {
1421 insta::assert_json_snapshot!(
1422 metric.nexits,
1423 @r#"
1424 {
1425 "sum": 0,
1426 "average": 0.0,
1427 "min": 0,
1428 "max": 0
1429 }
1430 "#
1431 );
1432 });
1433 }
1434
1435 #[test]
1436 fn typescript_function_with_returns() {
1437 check_metrics::<TypescriptParser>(
1438 "function safeDivide(a: number, b: number): number | null {
1439 if (b === 0) {
1440 return null;
1441 }
1442 return a / b;
1443 }",
1444 "foo.ts",
1445 |metric| {
1446 insta::assert_json_snapshot!(
1447 metric.nexits,
1448 @r#"
1449 {
1450 "sum": 2,
1451 "average": 2.0,
1452 "min": 0,
1453 "max": 2
1454 }
1455 "#
1456 );
1457 },
1458 );
1459 }
1460
1461 #[test]
1462 fn mozjs_no_exit() {
1463 check_metrics::<MozjsParser>("var a = 42;", "foo.js", |metric| {
1464 insta::assert_json_snapshot!(
1465 metric.nexits,
1466 @r#"
1467 {
1468 "sum": 0,
1469 "average": 0.0,
1470 "min": 0,
1471 "max": 0
1472 }
1473 "#
1474 );
1475 });
1476 }
1477
1478 #[test]
1479 fn mozjs_function_with_returns() {
1480 check_metrics::<MozjsParser>(
1481 "function f(a, b) {
1482 if (a) {
1483 return a;
1484 }
1485 return b;
1486 }",
1487 "foo.js",
1488 |metric| {
1489 insta::assert_json_snapshot!(
1490 metric.nexits,
1491 @r#"
1492 {
1493 "sum": 2,
1494 "average": 2.0,
1495 "min": 0,
1496 "max": 2
1497 }
1498 "#
1499 );
1500 },
1501 );
1502 }
1503
1504 #[test]
1505 fn kotlin_exit_return_and_throw() {
1506 check_metrics::<KotlinParser>(
1507 "fun divide(a: Int, b: Int): Int {
1508 if (b == 0) {
1509 throw IllegalArgumentException(\"zero\")
1510 }
1511 return a / b
1512 }",
1513 "foo.kt",
1514 |metric| {
1515 insta::assert_json_snapshot!(
1516 metric.nexits,
1517 @r#"
1518 {
1519 "sum": 2,
1520 "average": 2.0,
1521 "min": 0,
1522 "max": 2
1523 }
1524 "#
1525 );
1526 },
1527 );
1528 }
1529
1530 #[test]
1531 fn lua_no_exit() {
1532 check_metrics::<LuaParser>(
1533 "local function f(x)
1534 local y = x + 1
1535end",
1536 "foo.lua",
1537 |metric| {
1538 insta::assert_json_snapshot!(
1539 metric.nexits,
1540 @r#"
1541 {
1542 "sum": 0,
1543 "average": 0.0,
1544 "min": 0,
1545 "max": 0
1546 }
1547 "#
1548 );
1549 },
1550 );
1551 }
1552
1553 #[test]
1554 fn lua_return() {
1555 check_metrics::<LuaParser>(
1556 "local function f(x)
1557 if x > 0 then
1558 return x
1559 end
1560 return 0
1561end",
1562 "foo.lua",
1563 |metric| {
1564 insta::assert_json_snapshot!(
1565 metric.nexits,
1566 @r#"
1567 {
1568 "sum": 2,
1569 "average": 2.0,
1570 "min": 0,
1571 "max": 2
1572 }
1573 "#
1574 );
1575 },
1576 );
1577 }
1578
1579 #[test]
1580 fn lua_error_counts_as_exit() {
1581 check_metrics::<LuaParser>(
1582 "local function f(x)
1583 error(\"bad\")
1584end",
1585 "foo.lua",
1586 |metric| {
1587 insta::assert_json_snapshot!(
1590 metric.nexits,
1591 @r#"
1592 {
1593 "sum": 1,
1594 "average": 1.0,
1595 "min": 0,
1596 "max": 1
1597 }
1598 "#
1599 );
1600 },
1601 );
1602 }
1603
1604 #[test]
1605 fn lua_os_exit_counts_as_exit() {
1606 check_metrics::<LuaParser>(
1607 "local function f()
1608 os.exit(1)
1609end",
1610 "foo.lua",
1611 |metric| {
1612 insta::assert_json_snapshot!(
1615 metric.nexits,
1616 @r#"
1617 {
1618 "sum": 1,
1619 "average": 1.0,
1620 "min": 0,
1621 "max": 1
1622 }
1623 "#
1624 );
1625 },
1626 );
1627 }
1628
1629 #[test]
1630 fn lua_error_and_return_both_count() {
1631 check_metrics::<LuaParser>(
1632 "local function f(x)
1633 if x < 0 then
1634 error(\"negative\")
1635 end
1636 return x
1637end",
1638 "foo.lua",
1639 |metric| {
1640 insta::assert_json_snapshot!(
1642 metric.nexits,
1643 @r#"
1644 {
1645 "sum": 2,
1646 "average": 2.0,
1647 "min": 0,
1648 "max": 2
1649 }
1650 "#
1651 );
1652 },
1653 );
1654 }
1655
1656 #[test]
1657 fn lua_user_call_is_not_exit() {
1658 check_metrics::<LuaParser>(
1659 "local function f()
1660 foo()
1661 myError(\"x\")
1662end",
1663 "foo.lua",
1664 |metric| {
1665 insta::assert_json_snapshot!(
1668 metric.nexits,
1669 @r#"
1670 {
1671 "sum": 0,
1672 "average": 0.0,
1673 "min": 0,
1674 "max": 0
1675 }
1676 "#
1677 );
1678 },
1679 );
1680 }
1681
1682 #[test]
1683 fn bash_no_exit() {
1684 check_metrics::<BashParser>("echo \"no exits\"", "foo.sh", |metric| {
1685 insta::assert_json_snapshot!(
1686 metric.nexits,
1687 @r#"
1688 {
1689 "sum": 0,
1690 "average": 0.0,
1691 "min": 0,
1692 "max": 0
1693 }
1694 "#
1695 );
1696 });
1697 }
1698
1699 #[test]
1700 fn bash_explicit_return() {
1701 check_metrics::<BashParser>(
1702 "f() {
1703 if [ -z \"$1\" ]; then
1704 return 1
1705 fi
1706 echo ok
1707 }",
1708 "foo.sh",
1709 |metric| {
1710 insta::assert_json_snapshot!(
1711 metric.nexits,
1712 @r#"
1713 {
1714 "sum": 1,
1715 "average": 1.0,
1716 "min": 0,
1717 "max": 1
1718 }
1719 "#
1720 );
1721 },
1722 );
1723 }
1724
1725 #[test]
1726 fn bash_explicit_exit() {
1727 check_metrics::<BashParser>(
1728 "f() {
1729 exit 0
1730 }",
1731 "foo.sh",
1732 |metric| {
1733 insta::assert_json_snapshot!(
1734 metric.nexits,
1735 @r#"
1736 {
1737 "sum": 1,
1738 "average": 1.0,
1739 "min": 0,
1740 "max": 1
1741 }
1742 "#
1743 );
1744 },
1745 );
1746 }
1747
1748 #[test]
1749 fn bash_multiple_exits() {
1750 check_metrics::<BashParser>(
1751 "f() {
1752 if [ \"$1\" = die ]; then
1753 exit 1
1754 fi
1755 return 0
1756 }",
1757 "foo.sh",
1758 |metric| {
1759 insta::assert_json_snapshot!(
1760 metric.nexits,
1761 @r#"
1762 {
1763 "sum": 2,
1764 "average": 2.0,
1765 "min": 0,
1766 "max": 2
1767 }
1768 "#
1769 );
1770 },
1771 );
1772 }
1773
1774 #[test]
1775 fn bash_returnish_names_are_not_exits() {
1776 check_metrics::<BashParser>(
1781 "returncode=1
1782 returns() {
1783 echo named
1784 }
1785 returns",
1786 "foo.sh",
1787 |metric| {
1788 insta::assert_json_snapshot!(
1789 metric.nexits,
1790 @r#"
1791 {
1792 "sum": 0,
1793 "average": 0.0,
1794 "min": 0,
1795 "max": 0
1796 }
1797 "#
1798 );
1799 },
1800 );
1801 }
1802
1803 #[test]
1804 fn tcl_no_exit() {
1805 check_metrics::<TclParser>(
1806 "proc f {x} {
1807 puts $x
1808}",
1809 "foo.tcl",
1810 |metric| {
1811 insta::assert_json_snapshot!(
1812 metric.nexits,
1813 @r#"
1814 {
1815 "sum": 0,
1816 "average": 0.0,
1817 "min": 0,
1818 "max": 0
1819 }
1820 "#
1821 );
1822 },
1823 );
1824 }
1825
1826 #[test]
1827 fn tcl_return() {
1828 check_metrics::<TclParser>(
1829 "proc f {x} {
1830 return $x
1831}",
1832 "foo.tcl",
1833 |metric| {
1834 assert_eq!(metric.nexits.nexits_sum(), 1);
1835 assert_eq!(metric.nexits.nexits_max(), 1);
1836 insta::assert_json_snapshot!(metric.nexits);
1837 },
1838 );
1839 }
1840
1841 #[test]
1842 fn tcl_multiple_returns() {
1843 check_metrics::<TclParser>(
1844 "proc f {x} {
1845 if {$x > 0} {
1846 return positive
1847 }
1848 return nonpositive
1849}",
1850 "foo.tcl",
1851 |metric| {
1852 assert_eq!(metric.nexits.nexits_sum(), 2);
1853 assert_eq!(metric.nexits.nexits_max(), 2);
1854 insta::assert_json_snapshot!(metric.nexits);
1855 },
1856 );
1857 }
1858
1859 #[test]
1860 fn typescript_multiple_returns() {
1861 check_metrics::<TypescriptParser>(
1862 "function classify(n: number): string {
1863 if (n > 0) {
1864 return 'positive';
1865 } else if (n < 0) {
1866 return 'negative';
1867 }
1868 return 'zero';
1869 }",
1870 "foo.ts",
1871 |metric| {
1872 assert_eq!(metric.nexits.nexits_sum(), 3);
1873 assert_eq!(metric.nexits.nexits_max(), 3);
1874 insta::assert_json_snapshot!(metric.nexits);
1875 },
1876 );
1877 }
1878
1879 #[test]
1880 fn typescript_nested_functions() {
1881 check_metrics::<TypescriptParser>(
1882 "function outer(): number {
1883 function inner(): number {
1884 return 42;
1885 }
1886 return inner();
1887 }",
1888 "foo.ts",
1889 |metric| {
1890 assert_eq!(metric.nexits.nexits_sum(), 2);
1892 assert_eq!(metric.nexits.nexits_max(), 1);
1893 insta::assert_json_snapshot!(metric.nexits);
1894 },
1895 );
1896 }
1897
1898 #[test]
1899 fn tsx_no_exit() {
1900 check_metrics::<TsxParser>(
1901 "function f(): void {
1902 console.log('hello');
1903 }",
1904 "foo.tsx",
1905 |metric| {
1906 assert_eq!(metric.nexits.nexits_sum(), 0);
1907 assert_eq!(metric.nexits.nexits_max(), 0);
1908 insta::assert_json_snapshot!(metric.nexits);
1909 },
1910 );
1911 }
1912
1913 #[test]
1914 fn tsx_multiple_returns() {
1915 check_metrics::<TsxParser>(
1916 "function classify(n: number): string {
1917 if (n > 0) {
1918 return 'positive';
1919 } else if (n < 0) {
1920 return 'negative';
1921 }
1922 return 'zero';
1923 }",
1924 "foo.tsx",
1925 |metric| {
1926 assert_eq!(metric.nexits.nexits_sum(), 3);
1927 assert_eq!(metric.nexits.nexits_max(), 3);
1928 insta::assert_json_snapshot!(metric.nexits);
1929 },
1930 );
1931 }
1932
1933 #[test]
1934 fn kotlin_multiple_returns() {
1935 check_metrics::<KotlinParser>(
1936 "fun classify(n: Int): String {
1937 if (n > 0) {
1938 return \"positive\"
1939 } else if (n < 0) {
1940 return \"negative\"
1941 }
1942 return \"zero\"
1943 }",
1944 "foo.kt",
1945 |metric| {
1946 assert_eq!(metric.nexits.nexits_sum(), 3);
1947 assert_eq!(metric.nexits.nexits_max(), 3);
1948 insta::assert_json_snapshot!(metric.nexits);
1949 },
1950 );
1951 }
1952
1953 #[test]
1954 fn kotlin_no_exit() {
1955 check_metrics::<KotlinParser>(
1956 "fun f(): Unit {
1957 println(\"hello\")
1958 }",
1959 "foo.kt",
1960 |metric| {
1961 assert_eq!(metric.nexits.nexits_sum(), 0);
1962 assert_eq!(metric.nexits.nexits_max(), 0);
1963 insta::assert_json_snapshot!(metric.nexits);
1964 },
1965 );
1966 }
1967
1968 #[test]
1969 fn mozjs_nested_functions() {
1970 check_metrics::<MozjsParser>(
1971 "function outer() {
1972 function inner() {
1973 return 42;
1974 }
1975 return inner();
1976 }",
1977 "foo.js",
1978 |metric| {
1979 assert_eq!(metric.nexits.nexits_sum(), 2);
1981 assert_eq!(metric.nexits.nexits_max(), 1);
1982 insta::assert_json_snapshot!(metric.nexits);
1983 },
1984 );
1985 }
1986
1987 #[test]
1988 fn php_no_exit() {
1989 check_metrics::<PhpParser>("<?php $a = 42;", "foo.php", |metric| {
1990 insta::assert_json_snapshot!(
1991 metric.nexits,
1992 @r#"
1993 {
1994 "sum": 0,
1995 "average": 0.0,
1996 "min": 0,
1997 "max": 0
1998 }
1999 "#
2000 );
2001 });
2002 }
2003
2004 #[test]
2005 fn php_yield_throw() {
2006 check_metrics::<PhpParser>(
2009 "<?php
2010 function gen() {
2011 yield 1;
2012 yield 2;
2013 throw new \\Exception('x');
2014 }",
2015 "foo.php",
2016 |metric| {
2017 insta::assert_json_snapshot!(
2019 metric.nexits,
2020 @r#"
2021 {
2022 "sum": 3,
2023 "average": 3.0,
2024 "min": 0,
2025 "max": 3
2026 }
2027 "#
2028 );
2029 },
2030 );
2031 }
2032
2033 #[test]
2034 fn php_exit_statement() {
2035 check_metrics::<PhpParser>(
2040 "<?php
2041 function bail(int $code): void {
2042 if ($code === 1) {
2043 exit(1);
2044 }
2045 exit;
2046 }",
2047 "foo.php",
2048 |metric| {
2049 insta::assert_json_snapshot!(
2051 metric.nexits,
2052 @r#"
2053 {
2054 "sum": 2,
2055 "average": 2.0,
2056 "min": 0,
2057 "max": 2
2058 }
2059 "#
2060 );
2061 },
2062 );
2063 }
2064
2065 #[test]
2066 fn elixir_no_exit() {
2067 check_metrics::<ElixirParser>(
2072 "defmodule Foo do\n def add(a, b) do\n a + b\n end\nend\n",
2073 "foo.ex",
2074 |metric| {
2075 assert_eq!(metric.nexits.nexits_sum(), 0);
2076 insta::assert_json_snapshot!(
2077 metric.nexits,
2078 @r#"
2079 {
2080 "sum": 0,
2081 "average": 0.0,
2082 "min": 0,
2083 "max": 0
2084 }
2085 "#
2086 );
2087 },
2088 );
2089 }
2090
2091 #[test]
2092 fn elixir_raise_throw_exit() {
2093 check_metrics::<ElixirParser>(
2096 "defmodule Foo do\n def bad(x) do\n raise \"first\"\n throw(:second)\n exit(:third)\n end\nend\n",
2097 "foo.ex",
2098 |metric| {
2099 assert_eq!(metric.nexits.nexits_sum(), 3);
2100 insta::assert_json_snapshot!(
2101 metric.nexits,
2102 @r#"
2103 {
2104 "sum": 3,
2105 "average": 3.0,
2106 "min": 0,
2107 "max": 3
2108 }
2109 "#
2110 );
2111 },
2112 );
2113 }
2114
2115 #[test]
2116 fn elixir_reraise_counts() {
2117 check_metrics::<ElixirParser>(
2121 "defmodule Foo do\n def wrap(stack) do\n reraise(\"oops\", stack)\n end\nend\n",
2122 "foo.ex",
2123 |metric| {
2124 assert_eq!(metric.nexits.nexits_sum(), 1);
2125 },
2126 );
2127 }
2128
2129 #[test]
2130 fn elixir_lookalike_call_is_not_exit() {
2131 check_metrics::<ElixirParser>(
2135 "defmodule Foo do\n def f do\n throw_event(:click)\n Logger.raise_alert()\n exit_code = 0\n exit_code\n end\nend\n",
2136 "foo.ex",
2137 |metric| {
2138 assert_eq!(metric.nexits.nexits_sum(), 0);
2139 },
2140 );
2141 }
2142
2143 #[test]
2144 fn ruby_no_exit() {
2145 check_metrics::<RubyParser>("def foo\n a = 1\n a + 1\nend\n", "foo.rb", |metric| {
2147 assert_eq!(metric.nexits.nexits_sum(), 0);
2148 });
2149 }
2150
2151 #[test]
2152 fn ruby_multiple_returns() {
2153 check_metrics::<RubyParser>(
2156 "def kind(x)\n return :zero if x == 0\n if x > 0\n return :pos\n elsif x < 0\n return :neg\n end\n return :unknown\nend\n",
2157 "foo.rb",
2158 |metric| {
2159 assert_eq!(metric.nexits.nexits_sum(), 4);
2160 },
2161 );
2162 }
2163
2164 #[test]
2165 fn ruby_explicit_returns() {
2166 check_metrics::<RubyParser>(
2170 "def foo(x)\n return 0 if x.nil?\n yield x\n return x * 2\nend\n",
2171 "foo.rb",
2172 |metric| {
2173 assert_eq!(metric.nexits.nexits_sum(), 2);
2174 insta::assert_json_snapshot!(metric.nexits);
2175 },
2176 );
2177 }
2178
2179 #[test]
2180 fn python_return_and_raise() {
2181 check_metrics::<PythonParser>(
2185 "def parse(s):
2186 if not s:
2187 raise ValueError(\"empty\")
2188 return int(s)",
2189 "foo.py",
2190 |metric| {
2191 assert_eq!(metric.nexits.nexits_sum(), 2);
2192 insta::assert_json_snapshot!(
2193 metric.nexits,
2194 @r#"
2195 {
2196 "sum": 2,
2197 "average": 2.0,
2198 "min": 0,
2199 "max": 2
2200 }
2201 "#
2202 );
2203 },
2204 );
2205 }
2206
2207 #[test]
2208 fn javascript_return_and_throw() {
2209 check_metrics::<JavascriptParser>(
2211 "function parseLength(s) {
2212 if (s === null) throw new Error('null');
2213 return s.length;
2214 }",
2215 "foo.js",
2216 |metric| {
2217 assert_eq!(metric.nexits.nexits_sum(), 2);
2218 insta::assert_json_snapshot!(
2219 metric.nexits,
2220 @r#"
2221 {
2222 "sum": 2,
2223 "average": 2.0,
2224 "min": 0,
2225 "max": 2
2226 }
2227 "#
2228 );
2229 },
2230 );
2231 }
2232
2233 #[test]
2234 fn mozjs_return_and_throw() {
2235 check_metrics::<MozjsParser>(
2237 "function parseLength(s) {
2238 if (s === null) throw new Error('null');
2239 return s.length;
2240 }",
2241 "foo.js",
2242 |metric| {
2243 assert_eq!(metric.nexits.nexits_sum(), 2);
2244 insta::assert_json_snapshot!(
2245 metric.nexits,
2246 @r#"
2247 {
2248 "sum": 2,
2249 "average": 2.0,
2250 "min": 0,
2251 "max": 2
2252 }
2253 "#
2254 );
2255 },
2256 );
2257 }
2258
2259 #[test]
2260 fn typescript_return_and_throw() {
2261 check_metrics::<TypescriptParser>(
2262 "function parseLength(s: string | null): number {
2263 if (s === null) throw new Error('null');
2264 return s.length;
2265 }",
2266 "foo.ts",
2267 |metric| {
2268 assert_eq!(metric.nexits.nexits_sum(), 2);
2269 insta::assert_json_snapshot!(
2270 metric.nexits,
2271 @r#"
2272 {
2273 "sum": 2,
2274 "average": 2.0,
2275 "min": 0,
2276 "max": 2
2277 }
2278 "#
2279 );
2280 },
2281 );
2282 }
2283
2284 #[test]
2285 fn tsx_return_and_throw() {
2286 check_metrics::<TsxParser>(
2287 "function parseLength(s: string | null): number {
2288 if (s === null) throw new Error('null');
2289 return s.length;
2290 }",
2291 "foo.tsx",
2292 |metric| {
2293 assert_eq!(metric.nexits.nexits_sum(), 2);
2294 insta::assert_json_snapshot!(
2295 metric.nexits,
2296 @r#"
2297 {
2298 "sum": 2,
2299 "average": 2.0,
2300 "min": 0,
2301 "max": 2
2302 }
2303 "#
2304 );
2305 },
2306 );
2307 }
2308
2309 #[test]
2310 fn java_return_and_throw() {
2311 check_metrics::<JavaParser>(
2313 "class A {
2314 int parseLength(String s) {
2315 if (s == null) throw new NullPointerException();
2316 return s.length();
2317 }
2318 }",
2319 "foo.java",
2320 |metric| {
2321 assert_eq!(metric.nexits.nexits_sum(), 2);
2322 insta::assert_json_snapshot!(
2323 metric.nexits,
2324 @r#"
2325 {
2326 "sum": 2,
2327 "average": 2.0,
2328 "min": 0,
2329 "max": 2
2330 }
2331 "#
2332 );
2333 },
2334 );
2335 }
2336
2337 #[test]
2348 fn java_record_compact_constructor_owns_its_exits() {
2349 check_func_space::<JavaParser, _>(
2350 "record R(int a, int b) {
2351 R {
2352 if (a < 0) { throw new IllegalArgumentException(); }
2353 }
2354 int sum() { return a + b; }
2355 }",
2356 "R.java",
2357 |space| {
2358 assert_eq!(
2359 space.metrics.nexits.nexits_sum(),
2360 2,
2361 "one throw, one return"
2362 );
2363 assert_eq!(
2364 child_space(&space, "R").metrics.nexits.nexits(),
2365 0,
2366 "class R owns neither",
2367 );
2368 assert_eq!(
2369 function_space(&space, "R").metrics.nexits.nexits(),
2370 1,
2371 "the compact constructor owns its throw",
2372 );
2373 },
2374 );
2375 }
2376
2377 #[test]
2378 fn java_yield_in_switch_expression() {
2379 check_metrics::<JavaParser>(
2382 "class A {
2383 int describe(int n) {
2384 return switch (n) {
2385 case 0: yield 100;
2386 default: yield 200;
2387 };
2388 }
2389 }",
2390 "foo.java",
2391 |metric| {
2392 assert_eq!(metric.nexits.nexits_sum(), 3);
2393 },
2394 );
2395 }
2396
2397 #[test]
2398 fn groovy_no_exit() {
2399 check_metrics::<GroovyParser>("int a = 42", "foo.groovy", |metric| {
2401 assert_eq!(metric.nexits.nexits_sum(), 0);
2402 });
2403 }
2404
2405 #[test]
2406 fn groovy_simple_function() {
2407 check_metrics::<GroovyParser>(
2409 "int answer() {
2410 return 42
2411 }",
2412 "foo.groovy",
2413 |metric| {
2414 assert_eq!(metric.nexits.nexits_sum(), 1);
2415 },
2416 );
2417 }
2418
2419 #[test]
2420 fn groovy_return_and_throw() {
2421 check_metrics::<GroovyParser>(
2422 "class A {
2423 int parseLength(String s) {
2424 if (s == null) throw new NullPointerException()
2425 return s.length()
2426 }
2427 }",
2428 "foo.groovy",
2429 |metric| {
2430 assert_eq!(metric.nexits.nexits_sum(), 2);
2431 },
2432 );
2433 }
2434
2435 #[test]
2436 fn groovy_yield_in_switch_expression() {
2437 check_metrics::<GroovyParser>(
2440 "class A {
2441 int describe(int n) {
2442 return switch (n) {
2443 case 0: yield 100;
2444 default: yield 200;
2445 }
2446 }
2447 }",
2448 "foo.groovy",
2449 |metric| {
2450 assert_eq!(metric.nexits.nexits_sum(), 3);
2451 },
2452 );
2453 }
2454
2455 #[test]
2456 fn groovy_implicit_return_not_counted() {
2457 check_metrics::<GroovyParser>("int identity(int x) { x }", "foo.groovy", |metric| {
2462 assert_eq!(metric.nexits.nexits_sum(), 0);
2463 });
2464 }
2465
2466 #[test]
2467 fn cpp_return_and_throw() {
2468 check_metrics::<CppParser>(
2470 "int parseLength(const char* s) {
2471 if (s == nullptr) throw std::invalid_argument(\"null\");
2472 return 0;
2473 }",
2474 "foo.cpp",
2475 |metric| {
2476 assert_eq!(metric.nexits.nexits_sum(), 2);
2477 insta::assert_json_snapshot!(
2478 metric.nexits,
2479 @r#"
2480 {
2481 "sum": 2,
2482 "average": 2.0,
2483 "min": 0,
2484 "max": 2
2485 }
2486 "#
2487 );
2488 },
2489 );
2490 }
2491
2492 #[test]
2493 fn python_yield_counts_as_exit() {
2494 check_metrics::<PythonParser>(
2499 "def gen():
2500 yield 1
2501 yield 2
2502 return",
2503 "foo.py",
2504 |metric| {
2505 assert_eq!(metric.nexits.nexits_sum(), 3);
2506 insta::assert_json_snapshot!(
2507 metric.nexits,
2508 @r#"
2509 {
2510 "sum": 3,
2511 "average": 3.0,
2512 "min": 0,
2513 "max": 3
2514 }
2515 "#
2516 );
2517 },
2518 );
2519 }
2520
2521 #[test]
2522 fn javascript_yield_counts_as_exit() {
2523 check_metrics::<JavascriptParser>(
2526 "function* gen() {
2527 yield 1;
2528 yield 2;
2529 return;
2530 }",
2531 "foo.js",
2532 |metric| {
2533 assert_eq!(metric.nexits.nexits_sum(), 3);
2534 insta::assert_json_snapshot!(
2535 metric.nexits,
2536 @r#"
2537 {
2538 "sum": 3,
2539 "average": 3.0,
2540 "min": 0,
2541 "max": 3
2542 }
2543 "#
2544 );
2545 },
2546 );
2547 }
2548
2549 #[test]
2550 fn mozjs_yield_counts_as_exit() {
2551 check_metrics::<MozjsParser>(
2553 "function* gen() {
2554 yield 1;
2555 yield 2;
2556 return;
2557 }",
2558 "foo.js",
2559 |metric| {
2560 assert_eq!(metric.nexits.nexits_sum(), 3);
2561 insta::assert_json_snapshot!(
2562 metric.nexits,
2563 @r#"
2564 {
2565 "sum": 3,
2566 "average": 3.0,
2567 "min": 0,
2568 "max": 3
2569 }
2570 "#
2571 );
2572 },
2573 );
2574 }
2575
2576 #[test]
2577 fn typescript_yield_counts_as_exit() {
2578 check_metrics::<TypescriptParser>(
2579 "function* gen(): Generator<number> {
2580 yield 1;
2581 yield 2;
2582 return;
2583 }",
2584 "foo.ts",
2585 |metric| {
2586 assert_eq!(metric.nexits.nexits_sum(), 3);
2587 insta::assert_json_snapshot!(
2588 metric.nexits,
2589 @r#"
2590 {
2591 "sum": 3,
2592 "average": 3.0,
2593 "min": 0,
2594 "max": 3
2595 }
2596 "#
2597 );
2598 },
2599 );
2600 }
2601
2602 #[test]
2603 fn tsx_yield_counts_as_exit() {
2604 check_metrics::<TsxParser>(
2605 "function* gen(): Generator<number> {
2606 yield 1;
2607 yield 2;
2608 return;
2609 }",
2610 "foo.tsx",
2611 |metric| {
2612 assert_eq!(metric.nexits.nexits_sum(), 3);
2613 insta::assert_json_snapshot!(
2614 metric.nexits,
2615 @r#"
2616 {
2617 "sum": 3,
2618 "average": 3.0,
2619 "min": 0,
2620 "max": 3
2621 }
2622 "#
2623 );
2624 },
2625 );
2626 }
2627
2628 #[test]
2629 fn python_yield_forms_count_as_exit() {
2630 check_metrics::<PythonParser>(
2635 "def gen():
2636 yield
2637 yield 1
2638 yield from range(3)",
2639 "foo.py",
2640 |metric| {
2641 assert_eq!(metric.nexits.nexits_sum(), 3);
2642 insta::assert_json_snapshot!(
2643 metric.nexits,
2644 @r#"
2645 {
2646 "sum": 3,
2647 "average": 3.0,
2648 "min": 0,
2649 "max": 3
2650 }
2651 "#
2652 );
2653 },
2654 );
2655 }
2656
2657 #[test]
2658 fn javascript_yield_delegate_counts_as_exit() {
2659 check_metrics::<JavascriptParser>(
2664 "function* gen() {
2665 yield 1;
2666 yield* other();
2667 yield 2;
2668 }",
2669 "foo.js",
2670 |metric| {
2671 assert_eq!(metric.nexits.nexits_sum(), 3);
2672 insta::assert_json_snapshot!(
2673 metric.nexits,
2674 @r#"
2675 {
2676 "sum": 3,
2677 "average": 3.0,
2678 "min": 0,
2679 "max": 3
2680 }
2681 "#
2682 );
2683 },
2684 );
2685 }
2686
2687 #[test]
2688 fn mozjs_yield_delegate_counts_as_exit() {
2689 check_metrics::<MozjsParser>(
2690 "function* gen() {
2691 yield 1;
2692 yield* other();
2693 yield 2;
2694 }",
2695 "foo.js",
2696 |metric| {
2697 assert_eq!(metric.nexits.nexits_sum(), 3);
2698 insta::assert_json_snapshot!(
2699 metric.nexits,
2700 @r#"
2701 {
2702 "sum": 3,
2703 "average": 3.0,
2704 "min": 0,
2705 "max": 3
2706 }
2707 "#
2708 );
2709 },
2710 );
2711 }
2712
2713 #[test]
2714 fn typescript_yield_delegate_counts_as_exit() {
2715 check_metrics::<TypescriptParser>(
2716 "function* gen(): Generator<number> {
2717 yield 1;
2718 yield* other();
2719 yield 2;
2720 }",
2721 "foo.ts",
2722 |metric| {
2723 assert_eq!(metric.nexits.nexits_sum(), 3);
2724 insta::assert_json_snapshot!(
2725 metric.nexits,
2726 @r#"
2727 {
2728 "sum": 3,
2729 "average": 3.0,
2730 "min": 0,
2731 "max": 3
2732 }
2733 "#
2734 );
2735 },
2736 );
2737 }
2738
2739 #[test]
2740 fn tsx_yield_delegate_counts_as_exit() {
2741 check_metrics::<TsxParser>(
2742 "function* gen(): Generator<number> {
2743 yield 1;
2744 yield* other();
2745 yield 2;
2746 }",
2747 "foo.tsx",
2748 |metric| {
2749 assert_eq!(metric.nexits.nexits_sum(), 3);
2750 insta::assert_json_snapshot!(
2751 metric.nexits,
2752 @r#"
2753 {
2754 "sum": 3,
2755 "average": 3.0,
2756 "min": 0,
2757 "max": 3
2758 }
2759 "#
2760 );
2761 },
2762 );
2763 }
2764
2765 #[test]
2768 fn irules_no_exit() {
2769 check_metrics::<IrulesParser>(
2770 "when HTTP_REQUEST {
2771 set x 1
2772 log local0. $x
2773}
2774",
2775 "foo.irule",
2776 |metric| {
2777 assert_eq!(metric.nexits.nexits_sum(), 0);
2778 },
2779 );
2780 }
2781
2782 #[test]
2784 fn irules_return() {
2785 check_metrics::<IrulesParser>(
2786 "when HTTP_REQUEST {
2787 if { [HTTP::uri] eq \"/\" } {
2788 return
2789 }
2790 log local0. \"served\"
2791}
2792",
2793 "foo.irule",
2794 |metric| {
2795 assert_eq!(metric.nexits.nexits_sum(), 1);
2796 },
2797 );
2798 }
2799
2800 #[test]
2803 fn irules_multi_value_return_counts_once() {
2804 check_metrics::<IrulesParser>(
2805 "proc pair { a b } {
2806 return [list $a $b]
2807}
2808",
2809 "foo.irule",
2810 |metric| {
2811 assert_eq!(metric.nexits.nexits_sum(), 1);
2812 },
2813 );
2814 }
2815
2816 #[test]
2819 fn objc_no_exit() {
2820 check_metrics::<ObjcParser>(
2821 "@implementation Foo
2822- (void)bar {
2823 [self doWork];
2824}
2825@end
2826",
2827 "foo.m",
2828 |metric| {
2829 assert_eq!(metric.nexits.nexits_sum(), 0);
2830 insta::assert_json_snapshot!(metric.nexits, @r#"
2831 {
2832 "sum": 0,
2833 "average": 0.0,
2834 "min": 0,
2835 "max": 0
2836 }
2837 "#);
2838 },
2839 );
2840 }
2841
2842 #[test]
2845 fn objc_return_and_throw() {
2846 check_metrics::<ObjcParser>(
2847 "@implementation Foo
2848- (int)bar:(int)x {
2849 if (x < 0) {
2850 @throw [NSException exceptionWithName:@\"e\" reason:@\"r\" userInfo:nil];
2851 }
2852 return x;
2853}
2854@end
2855",
2856 "foo.m",
2857 |metric| {
2858 assert_eq!(metric.nexits.nexits_sum(), 2);
2859 insta::assert_json_snapshot!(metric.nexits, @r#"
2860 {
2861 "sum": 2,
2862 "average": 2.0,
2863 "min": 0,
2864 "max": 2
2865 }
2866 "#);
2867 },
2868 );
2869 }
2870}