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 serde::Serialize;
21use serde::ser::{SerializeStruct, Serializer};
22use std::fmt;
23
24use crate::checker::Checker;
25use crate::macros::implement_metric_trait;
26use crate::*;
27
28#[derive(Debug, Clone)]
33pub struct Stats {
34 exit: usize,
35 exit_sum: usize,
36 total_space_functions: usize,
37 exit_min: usize,
38 exit_max: usize,
39}
40
41impl Default for Stats {
42 fn default() -> Self {
43 Self {
44 exit: 0,
45 exit_sum: 0,
46 total_space_functions: 1,
47 exit_min: usize::MAX,
48 exit_max: 0,
49 }
50 }
51}
52
53impl Serialize for Stats {
54 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55 where
56 S: Serializer,
57 {
58 let mut st = serializer.serialize_struct("nexits", 4)?;
59 st.serialize_field("sum", &self.exit_sum())?;
60 st.serialize_field("average", &self.exit_average())?;
61 st.serialize_field("min", &self.exit_min())?;
62 st.serialize_field("max", &self.exit_max())?;
63 st.end()
64 }
65}
66
67impl fmt::Display for Stats {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 write!(
70 f,
71 "sum: {}, average: {} min: {}, max: {}",
72 self.exit_sum(),
73 self.exit_average(),
74 self.exit_min(),
75 self.exit_max()
76 )
77 }
78}
79
80impl Stats {
81 pub fn merge(&mut self, other: &Stats) {
83 self.exit_max = self.exit_max.max(other.exit_max);
84 self.exit_min = self.exit_min.min(other.exit_min);
85 self.exit_sum += other.exit_sum;
86 }
87
88 #[must_use]
90 pub fn exit(&self) -> f64 {
91 self.exit as f64
92 }
93 #[must_use]
95 pub fn exit_sum(&self) -> f64 {
96 self.exit_sum as f64
97 }
98 #[must_use]
104 pub fn exit_min(&self) -> f64 {
105 if self.exit_min == usize::MAX {
106 0.0
107 } else {
108 self.exit_min as f64
109 }
110 }
111 #[must_use]
113 pub fn exit_max(&self) -> f64 {
114 self.exit_max as f64
115 }
116
117 #[must_use]
124 pub fn exit_average(&self) -> f64 {
125 self.exit_sum() / self.total_space_functions as f64
126 }
127 #[inline]
128 pub(crate) fn compute_sum(&mut self) {
129 self.exit_sum += self.exit;
130 }
131 #[inline]
132 pub(crate) fn compute_minmax(&mut self) {
133 self.exit_max = self.exit_max.max(self.exit);
134 self.exit_min = self.exit_min.min(self.exit);
135 self.compute_sum();
136 }
137 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
138 self.total_space_functions = total_space_functions;
139 }
140}
141
142#[doc(hidden)]
143pub trait Exit
145where
146 Self: Checker,
147{
148 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
151}
152
153macro_rules! impl_exit_match_kinds {
157 ($code:ty, $lang:ident, [$($kind:ident),+ $(,)?]) => {
158 impl Exit for $code {
159 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
160 if matches!(node.kind_id().into(), $($lang::$kind)|+) {
161 stats.exit += 1;
162 }
163 }
164 }
165 };
166}
167
168impl_exit_match_kinds!(PythonCode, Python, [ReturnStatement, RaiseStatement, Yield]);
173impl_exit_match_kinds!(
176 MozjsCode,
177 Mozjs,
178 [ReturnStatement, ThrowStatement, YieldExpression]
179);
180impl_exit_match_kinds!(
181 JavascriptCode,
182 Javascript,
183 [ReturnStatement, ThrowStatement, YieldExpression]
184);
185impl_exit_match_kinds!(
186 TypescriptCode,
187 Typescript,
188 [ReturnStatement, ThrowStatement, YieldExpression]
189);
190impl_exit_match_kinds!(
191 TsxCode,
192 Tsx,
193 [ReturnStatement, ThrowStatement, YieldExpression]
194);
195impl_exit_match_kinds!(CppCode, Cpp, [ReturnStatement, ThrowStatement]);
196impl_exit_match_kinds!(JavaCode, Java, [ReturnStatement, ThrowStatement]);
197impl_exit_match_kinds!(
201 GroovyCode,
202 Groovy,
203 [ReturnStatement, ThrowStatement, YieldStatement]
204);
205
206impl Exit for RustCode {
207 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
208 if matches!(
214 node.kind_id().into(),
215 Rust::ReturnExpression | Rust::TryExpression
216 ) {
217 stats.exit += 1;
218 }
219 }
220}
221
222impl Exit for CsharpCode {
223 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
224 if matches!(
225 node.kind_id().into(),
226 Csharp::ReturnStatement
227 | Csharp::YieldStatement
228 | Csharp::ThrowStatement
229 | Csharp::ThrowExpression
230 ) {
231 stats.exit += 1;
232 }
233 }
234}
235
236impl Exit for GoCode {
237 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
238 if matches!(node.kind_id().into(), Go::ReturnStatement) {
239 stats.exit += 1;
240 }
241 }
242}
243
244impl Exit for PerlCode {
245 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
246 if node.kind_id() == Perl::ReturnExpression {
247 stats.exit += 1;
248 }
249 }
250}
251
252impl Exit for KotlinCode {
253 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
254 if matches!(
255 node.kind_id().into(),
256 Kotlin::ReturnExpression | Kotlin::ThrowExpression
257 ) {
258 stats.exit += 1;
259 }
260 }
261}
262
263impl Exit for LuaCode {
264 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
265 if node.kind_id() == Lua::ReturnStatement {
266 stats.exit += 1;
267 }
268 }
269}
270
271impl Exit for BashCode {
272 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
273 if matches!(node.kind_id().into(), Bash::Command)
278 && let Some(name) = node.child_by_field_name("name")
279 && matches!(name.utf8_text(code), Some("return" | "exit"))
280 {
281 stats.exit += 1;
282 }
283 }
284}
285
286impl Exit for TclCode {
287 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
288 if node.kind_id() == Tcl::Command
291 && let Some(name) = node.child_by_field_name("name")
292 && name.kind_id() == Tcl::SimpleWord
293 && name.utf8_text(code) == Some("return")
294 {
295 stats.exit += 1;
296 }
297 }
298}
299
300impl Exit for PhpCode {
301 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
309 if matches!(
310 node.kind_id().into(),
311 Php::ReturnStatement | Php::YieldExpression | Php::ThrowExpression | Php::ExitStatement
312 ) {
313 stats.exit += 1;
314 }
315 }
316}
317
318implement_metric_trait!(Exit, PreprocCode, CcommentCode);
320
321impl Exit for RubyCode {
322 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
330 if matches!(node.kind_id().into(), Ruby::Return | Ruby::Return2) {
331 stats.exit += 1;
332 }
333 }
334}
335
336impl Exit for ElixirCode {
337 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
343 if node.kind_id() == Elixir::Call
344 && let Some(target) = node.child_by_field_name("target")
345 && target.kind_id() == Elixir::Identifier
346 && matches!(
347 target.utf8_text(code),
348 Some("throw" | "raise" | "reraise" | "exit")
349 )
350 {
351 stats.exit += 1;
352 }
353 }
354}
355
356#[cfg(test)]
357#[allow(
358 clippy::float_cmp,
359 clippy::cast_precision_loss,
360 clippy::cast_possible_truncation,
361 clippy::cast_sign_loss,
362 clippy::similar_names,
363 clippy::doc_markdown,
364 clippy::needless_raw_string_hashes,
365 clippy::too_many_lines
366)]
367mod tests {
368 use crate::tools::check_metrics;
369
370 use super::*;
371
372 #[test]
377 fn exit_empty_file_min_is_zero() {
378 let stats = Stats::default();
379 assert_eq!(stats.exit_min(), 0.0);
380 }
381
382 #[test]
383 fn python_no_exit() {
384 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
385 insta::assert_json_snapshot!(
387 metric.nexits,
388 @r###"
389 {
390 "sum": 0.0,
391 "average": null,
392 "min": 0.0,
393 "max": 0.0
394 }"###
395 );
396 });
397 }
398
399 #[test]
400 fn rust_no_exit() {
401 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
402 insta::assert_json_snapshot!(
404 metric.nexits,
405 @r###"
406 {
407 "sum": 0.0,
408 "average": null,
409 "min": 0.0,
410 "max": 0.0
411 }"###
412 );
413 });
414 }
415
416 #[test]
417 fn rust_question_mark() {
418 check_metrics::<RustParser>("let _ = a? + b? + c?;", "foo.rs", |metric| {
419 insta::assert_json_snapshot!(
421 metric.nexits,
422 @r###"
423 {
424 "sum": 3.0,
425 "average": null,
426 "min": 3.0,
427 "max": 3.0
428 }"###
429 );
430 });
431 }
432
433 #[test]
441 fn rust_explicit_return_with_return_type() {
442 check_metrics::<RustParser>("fn foo() -> i32 { return 1; }", "foo.rs", |metric| {
443 insta::assert_json_snapshot!(
445 metric.nexits,
446 @r###"
447 {
448 "sum": 1.0,
449 "average": 1.0,
450 "min": 0.0,
451 "max": 1.0
452 }"###
453 );
454 });
455 }
456
457 #[test]
461 fn rust_implicit_return_not_counted() {
462 check_metrics::<RustParser>("fn foo() -> i32 { 0 }", "foo.rs", |metric| {
463 insta::assert_json_snapshot!(
465 metric.nexits,
466 @r###"
467 {
468 "sum": 0.0,
469 "average": 0.0,
470 "min": 0.0,
471 "max": 0.0
472 }"###
473 );
474 });
475 }
476
477 #[test]
481 fn rust_mixed_explicit_and_implicit_return() {
482 check_metrics::<RustParser>(
483 "fn foo(x: bool) -> i32 { if x { return 1; } 0 }",
484 "foo.rs",
485 |metric| {
486 insta::assert_json_snapshot!(
488 metric.nexits,
489 @r###"
490 {
491 "sum": 1.0,
492 "average": 1.0,
493 "min": 0.0,
494 "max": 1.0
495 }"###
496 );
497 },
498 );
499 }
500
501 #[test]
505 fn rust_question_mark_in_function() {
506 check_metrics::<RustParser>(
507 "fn foo() -> Result<i32, ()> { Ok(do_thing()?) }",
508 "foo.rs",
509 |metric| {
510 insta::assert_json_snapshot!(
512 metric.nexits,
513 @r###"
514 {
515 "sum": 1.0,
516 "average": 1.0,
517 "min": 0.0,
518 "max": 1.0
519 }"###
520 );
521 },
522 );
523 }
524
525 #[test]
528 fn rust_unit_return_no_exit() {
529 check_metrics::<RustParser>("fn foo() { let _x = 1; }", "foo.rs", |metric| {
530 insta::assert_json_snapshot!(
532 metric.nexits,
533 @r###"
534 {
535 "sum": 0.0,
536 "average": 0.0,
537 "min": 0.0,
538 "max": 0.0
539 }"###
540 );
541 });
542 }
543
544 #[test]
545 fn c_no_exit() {
546 check_metrics::<CppParser>("int a = 42;", "foo.c", |metric| {
547 insta::assert_json_snapshot!(
549 metric.nexits,
550 @r###"
551 {
552 "sum": 0.0,
553 "average": null,
554 "min": 0.0,
555 "max": 0.0
556 }"###
557 );
558 });
559 }
560
561 #[test]
564 fn c_multiple_returns_in_branches() {
565 check_metrics::<CppParser>(
566 "int f(int x) {
567 if (x < 0) {
568 return -1;
569 } else if (x == 0) {
570 return 0;
571 } else {
572 return 1;
573 }
574 }",
575 "foo.c",
576 |metric| {
577 assert_eq!(metric.nexits.exit_sum(), 3.0);
579 assert_eq!(metric.nexits.exit_max(), 3.0);
580 insta::assert_json_snapshot!(
581 metric.nexits,
582 @r###"
583 {
584 "sum": 3.0,
585 "average": 3.0,
586 "min": 0.0,
587 "max": 3.0
588 }"###
589 );
590 },
591 );
592 }
593
594 #[test]
598 fn cpp_return_in_try_catch() {
599 check_metrics::<CppParser>(
600 "int f(int x) {
601 try {
602 if (x == 0) {
603 return 1;
604 }
605 return 2;
606 } catch (...) {
607 return -1;
608 }
609 }",
610 "foo.cpp",
611 |metric| {
612 assert_eq!(metric.nexits.exit_sum(), 3.0);
615 assert_eq!(metric.nexits.exit_max(), 3.0);
616 insta::assert_json_snapshot!(
617 metric.nexits,
618 @r###"
619 {
620 "sum": 3.0,
621 "average": 3.0,
622 "min": 0.0,
623 "max": 3.0
624 }"###
625 );
626 },
627 );
628 }
629
630 #[test]
633 fn c_early_return_in_loop() {
634 check_metrics::<CppParser>(
635 "int find(int* a, int n, int target) {
636 for (int i = 0; i < n; ++i) {
637 if (a[i] == target) {
638 return i;
639 }
640 }
641 return -1;
642 }",
643 "foo.c",
644 |metric| {
645 assert_eq!(metric.nexits.exit_sum(), 2.0);
647 assert_eq!(metric.nexits.exit_max(), 2.0);
648 insta::assert_json_snapshot!(
649 metric.nexits,
650 @r###"
651 {
652 "sum": 2.0,
653 "average": 2.0,
654 "min": 0.0,
655 "max": 2.0
656 }"###
657 );
658 },
659 );
660 }
661
662 #[test]
665 fn c_void_no_explicit_return() {
666 check_metrics::<CppParser>(
667 "void greet(const char* who) {
668 printf(\"hi %s\\n\", who);
669 }",
670 "foo.c",
671 |metric| {
672 assert_eq!(metric.nexits.exit_sum(), 0.0);
674 assert_eq!(metric.nexits.exit_max(), 0.0);
675 insta::assert_json_snapshot!(
676 metric.nexits,
677 @r###"
678 {
679 "sum": 0.0,
680 "average": 0.0,
681 "min": 0.0,
682 "max": 0.0
683 }"###
684 );
685 },
686 );
687 }
688
689 #[test]
690 fn javascript_no_exit() {
691 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
692 insta::assert_json_snapshot!(
694 metric.nexits,
695 @r###"
696 {
697 "sum": 0.0,
698 "average": null,
699 "min": 0.0,
700 "max": 0.0
701 }"###
702 );
703 });
704 }
705
706 #[test]
707 fn javascript_simple_function() {
708 check_metrics::<JavascriptParser>(
709 "function f(a, b) {
710 if (a) {
711 return a;
712 }
713 return b;
714 }",
715 "foo.js",
716 |metric| {
717 insta::assert_json_snapshot!(
719 metric.nexits,
720 @r###"
721 {
722 "sum": 2.0,
723 "average": 2.0,
724 "min": 0.0,
725 "max": 2.0
726 }"###
727 );
728 },
729 );
730 }
731
732 #[test]
733 fn javascript_nested_functions() {
734 check_metrics::<JavascriptParser>(
735 "function outer() {
736 function inner() {
737 return 1;
738 }
739 return inner();
740 }",
741 "foo.js",
742 |metric| {
743 insta::assert_json_snapshot!(
745 metric.nexits,
746 @r###"
747 {
748 "sum": 2.0,
749 "average": 1.0,
750 "min": 0.0,
751 "max": 1.0
752 }"###
753 );
754 },
755 );
756 }
757
758 #[test]
759 fn python_simple_function() {
760 check_metrics::<PythonParser>(
761 "def f(a, b):
762 if a:
763 return a",
764 "foo.py",
765 |metric| {
766 insta::assert_json_snapshot!(
768 metric.nexits,
769 @r###"
770 {
771 "sum": 1.0,
772 "average": 1.0,
773 "min": 0.0,
774 "max": 1.0
775 }"###
776 );
777 },
778 );
779 }
780
781 #[test]
782 fn python_more_functions() {
783 check_metrics::<PythonParser>(
784 "def f(a, b):
785 if a:
786 return a
787 def f(a, b):
788 if b:
789 return b",
790 "foo.py",
791 |metric| {
792 insta::assert_json_snapshot!(
794 metric.nexits,
795 @r###"
796 {
797 "sum": 2.0,
798 "average": 1.0,
799 "min": 0.0,
800 "max": 1.0
801 }"###
802 );
803 },
804 );
805 }
806
807 #[test]
808 fn python_nested_functions() {
809 check_metrics::<PythonParser>(
810 "def f(a, b):
811 def foo(a):
812 if a:
813 return 1
814 bar = lambda a: lambda b: b or True or True
815 return bar(foo(a))(a)",
816 "foo.py",
817 |metric| {
818 insta::assert_json_snapshot!(
820 metric.nexits,
821 @r###"
822 {
823 "sum": 2.0,
824 "average": 0.5,
825 "min": 0.0,
826 "max": 1.0
827 }"###
828 );
829 },
830 );
831 }
832
833 #[test]
834 fn java_no_exit() {
835 check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
836 insta::assert_json_snapshot!(
838 metric.nexits,
839 @r###"
840 {
841 "sum": 0.0,
842 "average": null,
843 "min": 0.0,
844 "max": 0.0
845 }"###
846 );
847 });
848 }
849
850 #[test]
851 fn java_simple_function() {
852 check_metrics::<JavaParser>(
853 "class A {
854 public int sum(int x, int y) {
855 return x + y;
856 }
857 }",
858 "foo.java",
859 |metric| {
860 insta::assert_json_snapshot!(
862 metric.nexits,
863 @r###"
864 {
865 "sum": 1.0,
866 "average": 1.0,
867 "min": 0.0,
868 "max": 1.0
869 }"###
870 );
871 },
872 );
873 }
874
875 #[test]
876 fn go_no_return() {
877 check_metrics::<GoParser>(
878 "package main
879 func f() {
880 x := 1
881 _ = x
882 }",
883 "foo.go",
884 |metric| {
885 insta::assert_json_snapshot!(
887 metric.nexits,
888 @r###"
889 {
890 "sum": 0.0,
891 "average": 0.0,
892 "min": 0.0,
893 "max": 0.0
894 }"###
895 );
896 },
897 );
898 }
899
900 #[test]
901 fn go_single_return() {
902 check_metrics::<GoParser>(
903 "package main
904 func f() int {
905 return 1
906 }",
907 "foo.go",
908 |metric| {
909 insta::assert_json_snapshot!(
910 metric.nexits,
911 @r###"
912 {
913 "sum": 1.0,
914 "average": 1.0,
915 "min": 0.0,
916 "max": 1.0
917 }"###
918 );
919 },
920 );
921 }
922
923 #[test]
924 fn go_multiple_returns() {
925 check_metrics::<GoParser>(
926 "package main
927 func f(x int) int {
928 if x > 0 {
929 return 1
930 }
931 if x < 0 {
932 return -1
933 }
934 return 0
935 }",
936 "foo.go",
937 |metric| {
938 insta::assert_json_snapshot!(
940 metric.nexits,
941 @r###"
942 {
943 "sum": 3.0,
944 "average": 3.0,
945 "min": 0.0,
946 "max": 3.0
947 }"###
948 );
949 },
950 );
951 }
952
953 #[test]
954 fn go_naked_return() {
955 check_metrics::<GoParser>(
956 "package main
957 func f() (x int) {
958 x = 1
959 return
960 }",
961 "foo.go",
962 |metric| {
963 insta::assert_json_snapshot!(
965 metric.nexits,
966 @r###"
967 {
968 "sum": 1.0,
969 "average": 1.0,
970 "min": 0.0,
971 "max": 1.0
972 }"###
973 );
974 },
975 );
976 }
977
978 #[test]
979 fn go_multivalue_return() {
980 check_metrics::<GoParser>(
981 "package main
982 func f() (int, error) {
983 return 0, nil
984 }",
985 "foo.go",
986 |metric| {
987 insta::assert_json_snapshot!(
989 metric.nexits,
990 @r###"
991 {
992 "sum": 1.0,
993 "average": 1.0,
994 "min": 0.0,
995 "max": 1.0
996 }"###
997 );
998 },
999 );
1000 }
1001
1002 #[test]
1003 fn java_split_function() {
1004 check_metrics::<JavaParser>(
1005 "class A {
1006 public int multiply(int x, int y) {
1007 if(x == 0 || y == 0){
1008 return 0;
1009 }
1010 return x * y;
1011 }
1012 }",
1013 "foo.java",
1014 |metric| {
1015 insta::assert_json_snapshot!(
1017 metric.nexits,
1018 @r###"
1019 {
1020 "sum": 2.0,
1021 "average": 2.0,
1022 "min": 0.0,
1023 "max": 2.0
1024 }"###
1025 );
1026 },
1027 );
1028 }
1029
1030 #[test]
1031 fn csharp_no_exit() {
1032 check_metrics::<CsharpParser>("int a = 42;", "foo.cs", |metric| {
1033 insta::assert_json_snapshot!(
1034 metric.nexits,
1035 @r###"
1036 {
1037 "sum": 0.0,
1038 "average": null,
1039 "min": 0.0,
1040 "max": 0.0
1041 }"###
1042 );
1043 });
1044 }
1045
1046 #[test]
1047 fn csharp_simple_function() {
1048 check_metrics::<CsharpParser>(
1049 "class A {
1050 public int Sum(int x, int y) {
1051 return x + y;
1052 }
1053 }",
1054 "foo.cs",
1055 |metric| {
1056 insta::assert_json_snapshot!(
1057 metric.nexits,
1058 @r###"
1059 {
1060 "sum": 1.0,
1061 "average": 1.0,
1062 "min": 0.0,
1063 "max": 1.0
1064 }"###
1065 );
1066 },
1067 );
1068 }
1069
1070 #[test]
1071 fn csharp_split_function() {
1072 check_metrics::<CsharpParser>(
1073 "class A {
1074 public int Multiply(int x, int y) {
1075 if (x == 0 || y == 0) {
1076 return 0;
1077 }
1078 return x * y;
1079 }
1080 }",
1081 "foo.cs",
1082 |metric| {
1083 insta::assert_json_snapshot!(
1084 metric.nexits,
1085 @r###"
1086 {
1087 "sum": 2.0,
1088 "average": 2.0,
1089 "min": 0.0,
1090 "max": 2.0
1091 }"###
1092 );
1093 },
1094 );
1095 }
1096
1097 #[test]
1098 fn csharp_yield_and_throw() {
1099 check_metrics::<CsharpParser>(
1100 "class A {
1101 public IEnumerable<int> Gen() {
1102 yield return 1;
1103 yield break;
1104 }
1105 public int Bad(int x) {
1106 if (x < 0) throw new System.Exception();
1107 return x;
1108 }
1109 }",
1110 "foo.cs",
1111 |metric| {
1112 insta::assert_json_snapshot!(
1114 metric.nexits,
1115 @r#"
1116 {
1117 "sum": 4.0,
1118 "average": 2.0,
1119 "min": 0.0,
1120 "max": 2.0
1121 }
1122 "#
1123 );
1124 },
1125 );
1126 }
1127
1128 #[test]
1129 fn perl_no_exit() {
1130 check_metrics::<PerlParser>(
1131 "sub f {
1132 print 'hi';
1133 }",
1134 "foo.pl",
1135 |metric| {
1136 insta::assert_json_snapshot!(
1137 metric.nexits,
1138 @r#"
1139 {
1140 "sum": 0.0,
1141 "average": 0.0,
1142 "min": 0.0,
1143 "max": 0.0
1144 }
1145 "#
1146 );
1147 },
1148 );
1149 }
1150
1151 #[test]
1152 fn perl_no_function_no_exit() {
1153 check_metrics::<PerlParser>("my $x = 1;\nprint $x;\n", "foo.pl", |metric| {
1154 insta::assert_json_snapshot!(metric.nexits, @r#"
1155 {
1156 "sum": 0.0,
1157 "average": null,
1158 "min": 0.0,
1159 "max": 0.0
1160 }
1161 "#);
1162 });
1163 }
1164
1165 #[test]
1166 fn perl_multiple_returns() {
1167 check_metrics::<PerlParser>(
1168 "sub f {
1169 return 1 if $_[0];
1170 return 0;
1171 }",
1172 "foo.pl",
1173 |metric| {
1174 insta::assert_json_snapshot!(
1175 metric.nexits,
1176 @r#"
1177 {
1178 "sum": 2.0,
1179 "average": 2.0,
1180 "min": 0.0,
1181 "max": 2.0
1182 }
1183 "#
1184 );
1185 },
1186 );
1187 }
1188
1189 #[test]
1190 fn tsx_function_with_returns() {
1191 check_metrics::<TsxParser>(
1192 "function clamp(val: number, min: number, max: number) {
1193 if (val < min) {
1194 return min;
1195 }
1196 if (val > max) {
1197 return max;
1198 }
1199 return val;
1200 }",
1201 "foo.tsx",
1202 |metric| {
1203 insta::assert_json_snapshot!(
1204 metric.nexits,
1205 @r###"
1206 {
1207 "sum": 3.0,
1208 "average": 3.0,
1209 "min": 0.0,
1210 "max": 3.0
1211 }"###
1212 );
1213 },
1214 );
1215 }
1216
1217 #[test]
1218 fn typescript_no_exit() {
1219 check_metrics::<TypescriptParser>("const x: number = 42;", "foo.ts", |metric| {
1220 insta::assert_json_snapshot!(
1221 metric.nexits,
1222 @r###"
1223 {
1224 "sum": 0.0,
1225 "average": null,
1226 "min": 0.0,
1227 "max": 0.0
1228 }"###
1229 );
1230 });
1231 }
1232
1233 #[test]
1234 fn typescript_function_with_returns() {
1235 check_metrics::<TypescriptParser>(
1236 "function safeDivide(a: number, b: number): number | null {
1237 if (b === 0) {
1238 return null;
1239 }
1240 return a / b;
1241 }",
1242 "foo.ts",
1243 |metric| {
1244 insta::assert_json_snapshot!(
1245 metric.nexits,
1246 @r###"
1247 {
1248 "sum": 2.0,
1249 "average": 2.0,
1250 "min": 0.0,
1251 "max": 2.0
1252 }"###
1253 );
1254 },
1255 );
1256 }
1257
1258 #[test]
1259 fn mozjs_no_exit() {
1260 check_metrics::<MozjsParser>("var a = 42;", "foo.js", |metric| {
1261 insta::assert_json_snapshot!(
1262 metric.nexits,
1263 @r###"
1264 {
1265 "sum": 0.0,
1266 "average": null,
1267 "min": 0.0,
1268 "max": 0.0
1269 }"###
1270 );
1271 });
1272 }
1273
1274 #[test]
1275 fn mozjs_function_with_returns() {
1276 check_metrics::<MozjsParser>(
1277 "function f(a, b) {
1278 if (a) {
1279 return a;
1280 }
1281 return b;
1282 }",
1283 "foo.js",
1284 |metric| {
1285 insta::assert_json_snapshot!(
1286 metric.nexits,
1287 @r###"
1288 {
1289 "sum": 2.0,
1290 "average": 2.0,
1291 "min": 0.0,
1292 "max": 2.0
1293 }"###
1294 );
1295 },
1296 );
1297 }
1298
1299 #[test]
1300 fn kotlin_exit_return_and_throw() {
1301 check_metrics::<KotlinParser>(
1302 "fun divide(a: Int, b: Int): Int {
1303 if (b == 0) {
1304 throw IllegalArgumentException(\"zero\")
1305 }
1306 return a / b
1307 }",
1308 "foo.kt",
1309 |metric| {
1310 insta::assert_json_snapshot!(
1311 metric.nexits,
1312 @r###"
1313 {
1314 "sum": 2.0,
1315 "average": 2.0,
1316 "min": 0.0,
1317 "max": 2.0
1318 }
1319 "###
1320 );
1321 },
1322 );
1323 }
1324
1325 #[test]
1326 fn lua_no_exit() {
1327 check_metrics::<LuaParser>(
1328 "local function f(x)
1329 local y = x + 1
1330end",
1331 "foo.lua",
1332 |metric| {
1333 insta::assert_json_snapshot!(
1334 metric.nexits,
1335 @r###"
1336 {
1337 "sum": 0.0,
1338 "average": 0.0,
1339 "min": 0.0,
1340 "max": 0.0
1341 }
1342 "###
1343 );
1344 },
1345 );
1346 }
1347
1348 #[test]
1349 fn lua_return() {
1350 check_metrics::<LuaParser>(
1351 "local function f(x)
1352 if x > 0 then
1353 return x
1354 end
1355 return 0
1356end",
1357 "foo.lua",
1358 |metric| {
1359 insta::assert_json_snapshot!(
1360 metric.nexits,
1361 @r###"
1362 {
1363 "sum": 2.0,
1364 "average": 2.0,
1365 "min": 0.0,
1366 "max": 2.0
1367 }
1368 "###
1369 );
1370 },
1371 );
1372 }
1373
1374 #[test]
1375 fn bash_no_exit() {
1376 check_metrics::<BashParser>("echo \"no exits\"", "foo.sh", |metric| {
1377 insta::assert_json_snapshot!(
1378 metric.nexits,
1379 @r###"
1380 {
1381 "sum": 0.0,
1382 "average": null,
1383 "min": 0.0,
1384 "max": 0.0
1385 }"###
1386 );
1387 });
1388 }
1389
1390 #[test]
1391 fn bash_explicit_return() {
1392 check_metrics::<BashParser>(
1393 "f() {
1394 if [ -z \"$1\" ]; then
1395 return 1
1396 fi
1397 echo ok
1398 }",
1399 "foo.sh",
1400 |metric| {
1401 insta::assert_json_snapshot!(
1402 metric.nexits,
1403 @r###"
1404 {
1405 "sum": 1.0,
1406 "average": 1.0,
1407 "min": 0.0,
1408 "max": 1.0
1409 }"###
1410 );
1411 },
1412 );
1413 }
1414
1415 #[test]
1416 fn bash_explicit_exit() {
1417 check_metrics::<BashParser>(
1418 "f() {
1419 exit 0
1420 }",
1421 "foo.sh",
1422 |metric| {
1423 insta::assert_json_snapshot!(
1424 metric.nexits,
1425 @r###"
1426 {
1427 "sum": 1.0,
1428 "average": 1.0,
1429 "min": 0.0,
1430 "max": 1.0
1431 }"###
1432 );
1433 },
1434 );
1435 }
1436
1437 #[test]
1438 fn bash_multiple_exits() {
1439 check_metrics::<BashParser>(
1440 "f() {
1441 if [ \"$1\" = die ]; then
1442 exit 1
1443 fi
1444 return 0
1445 }",
1446 "foo.sh",
1447 |metric| {
1448 insta::assert_json_snapshot!(
1449 metric.nexits,
1450 @r###"
1451 {
1452 "sum": 2.0,
1453 "average": 2.0,
1454 "min": 0.0,
1455 "max": 2.0
1456 }"###
1457 );
1458 },
1459 );
1460 }
1461
1462 #[test]
1463 fn bash_returnish_names_are_not_exits() {
1464 check_metrics::<BashParser>(
1469 "returncode=1
1470 returns() {
1471 echo named
1472 }
1473 returns",
1474 "foo.sh",
1475 |metric| {
1476 insta::assert_json_snapshot!(
1477 metric.nexits,
1478 @r###"
1479 {
1480 "sum": 0.0,
1481 "average": 0.0,
1482 "min": 0.0,
1483 "max": 0.0
1484 }"###
1485 );
1486 },
1487 );
1488 }
1489
1490 #[test]
1491 fn tcl_no_exit() {
1492 check_metrics::<TclParser>(
1493 "proc f {x} {
1494 puts $x
1495}",
1496 "foo.tcl",
1497 |metric| {
1498 insta::assert_json_snapshot!(
1499 metric.nexits,
1500 @r#"
1501 {
1502 "sum": 0.0,
1503 "average": 0.0,
1504 "min": 0.0,
1505 "max": 0.0
1506 }
1507 "#
1508 );
1509 },
1510 );
1511 }
1512
1513 #[test]
1514 fn tcl_return() {
1515 check_metrics::<TclParser>(
1516 "proc f {x} {
1517 return $x
1518}",
1519 "foo.tcl",
1520 |metric| {
1521 assert_eq!(metric.nexits.exit_sum(), 1.0);
1522 assert_eq!(metric.nexits.exit_max(), 1.0);
1523 insta::assert_json_snapshot!(metric.nexits);
1524 },
1525 );
1526 }
1527
1528 #[test]
1529 fn tcl_multiple_returns() {
1530 check_metrics::<TclParser>(
1531 "proc f {x} {
1532 if {$x > 0} {
1533 return positive
1534 }
1535 return nonpositive
1536}",
1537 "foo.tcl",
1538 |metric| {
1539 assert_eq!(metric.nexits.exit_sum(), 2.0);
1540 assert_eq!(metric.nexits.exit_max(), 2.0);
1541 insta::assert_json_snapshot!(metric.nexits);
1542 },
1543 );
1544 }
1545
1546 #[test]
1547 fn typescript_multiple_returns() {
1548 check_metrics::<TypescriptParser>(
1549 "function classify(n: number): string {
1550 if (n > 0) {
1551 return 'positive';
1552 } else if (n < 0) {
1553 return 'negative';
1554 }
1555 return 'zero';
1556 }",
1557 "foo.ts",
1558 |metric| {
1559 assert_eq!(metric.nexits.exit_sum(), 3.0);
1560 assert_eq!(metric.nexits.exit_max(), 3.0);
1561 insta::assert_json_snapshot!(metric.nexits);
1562 },
1563 );
1564 }
1565
1566 #[test]
1567 fn typescript_nested_functions() {
1568 check_metrics::<TypescriptParser>(
1569 "function outer(): number {
1570 function inner(): number {
1571 return 42;
1572 }
1573 return inner();
1574 }",
1575 "foo.ts",
1576 |metric| {
1577 assert_eq!(metric.nexits.exit_sum(), 2.0);
1579 assert_eq!(metric.nexits.exit_max(), 1.0);
1580 insta::assert_json_snapshot!(metric.nexits);
1581 },
1582 );
1583 }
1584
1585 #[test]
1586 fn tsx_no_exit() {
1587 check_metrics::<TsxParser>(
1588 "function f(): void {
1589 console.log('hello');
1590 }",
1591 "foo.tsx",
1592 |metric| {
1593 assert_eq!(metric.nexits.exit_sum(), 0.0);
1594 assert_eq!(metric.nexits.exit_max(), 0.0);
1595 insta::assert_json_snapshot!(metric.nexits);
1596 },
1597 );
1598 }
1599
1600 #[test]
1601 fn tsx_multiple_returns() {
1602 check_metrics::<TsxParser>(
1603 "function classify(n: number): string {
1604 if (n > 0) {
1605 return 'positive';
1606 } else if (n < 0) {
1607 return 'negative';
1608 }
1609 return 'zero';
1610 }",
1611 "foo.tsx",
1612 |metric| {
1613 assert_eq!(metric.nexits.exit_sum(), 3.0);
1614 assert_eq!(metric.nexits.exit_max(), 3.0);
1615 insta::assert_json_snapshot!(metric.nexits);
1616 },
1617 );
1618 }
1619
1620 #[test]
1621 fn kotlin_multiple_returns() {
1622 check_metrics::<KotlinParser>(
1623 "fun classify(n: Int): String {
1624 if (n > 0) {
1625 return \"positive\"
1626 } else if (n < 0) {
1627 return \"negative\"
1628 }
1629 return \"zero\"
1630 }",
1631 "foo.kt",
1632 |metric| {
1633 assert_eq!(metric.nexits.exit_sum(), 3.0);
1634 assert_eq!(metric.nexits.exit_max(), 3.0);
1635 insta::assert_json_snapshot!(metric.nexits);
1636 },
1637 );
1638 }
1639
1640 #[test]
1641 fn kotlin_no_exit() {
1642 check_metrics::<KotlinParser>(
1643 "fun f(): Unit {
1644 println(\"hello\")
1645 }",
1646 "foo.kt",
1647 |metric| {
1648 assert_eq!(metric.nexits.exit_sum(), 0.0);
1649 assert_eq!(metric.nexits.exit_max(), 0.0);
1650 insta::assert_json_snapshot!(metric.nexits);
1651 },
1652 );
1653 }
1654
1655 #[test]
1656 fn mozjs_nested_functions() {
1657 check_metrics::<MozjsParser>(
1658 "function outer() {
1659 function inner() {
1660 return 42;
1661 }
1662 return inner();
1663 }",
1664 "foo.js",
1665 |metric| {
1666 assert_eq!(metric.nexits.exit_sum(), 2.0);
1668 assert_eq!(metric.nexits.exit_max(), 1.0);
1669 insta::assert_json_snapshot!(metric.nexits);
1670 },
1671 );
1672 }
1673
1674 #[test]
1675 fn php_no_exit() {
1676 check_metrics::<PhpParser>("<?php $a = 42;", "foo.php", |metric| {
1677 insta::assert_json_snapshot!(
1678 metric.nexits,
1679 @r###"
1680 {
1681 "sum": 0.0,
1682 "average": null,
1683 "min": 0.0,
1684 "max": 0.0
1685 }"###
1686 );
1687 });
1688 }
1689
1690 #[test]
1691 fn php_yield_throw() {
1692 check_metrics::<PhpParser>(
1695 "<?php
1696 function gen() {
1697 yield 1;
1698 yield 2;
1699 throw new \\Exception('x');
1700 }",
1701 "foo.php",
1702 |metric| {
1703 insta::assert_json_snapshot!(
1705 metric.nexits,
1706 @r###"
1707 {
1708 "sum": 3.0,
1709 "average": 3.0,
1710 "min": 0.0,
1711 "max": 3.0
1712 }"###
1713 );
1714 },
1715 );
1716 }
1717
1718 #[test]
1719 fn php_exit_statement() {
1720 check_metrics::<PhpParser>(
1725 "<?php
1726 function bail(int $code): void {
1727 if ($code === 1) {
1728 exit(1);
1729 }
1730 exit;
1731 }",
1732 "foo.php",
1733 |metric| {
1734 insta::assert_json_snapshot!(
1736 metric.nexits,
1737 @r###"
1738 {
1739 "sum": 2.0,
1740 "average": 2.0,
1741 "min": 0.0,
1742 "max": 2.0
1743 }"###
1744 );
1745 },
1746 );
1747 }
1748
1749 #[test]
1750 fn elixir_no_exit() {
1751 check_metrics::<ElixirParser>(
1756 "defmodule Foo do\n def add(a, b) do\n a + b\n end\nend\n",
1757 "foo.ex",
1758 |metric| {
1759 assert_eq!(metric.nexits.exit_sum(), 0.0);
1760 insta::assert_json_snapshot!(
1761 metric.nexits,
1762 @r###"
1763 {
1764 "sum": 0.0,
1765 "average": null,
1766 "min": 0.0,
1767 "max": 0.0
1768 }"###
1769 );
1770 },
1771 );
1772 }
1773
1774 #[test]
1775 fn elixir_raise_throw_exit() {
1776 check_metrics::<ElixirParser>(
1779 "defmodule Foo do\n def bad(x) do\n raise \"first\"\n throw(:second)\n exit(:third)\n end\nend\n",
1780 "foo.ex",
1781 |metric| {
1782 assert_eq!(metric.nexits.exit_sum(), 3.0);
1783 insta::assert_json_snapshot!(
1784 metric.nexits,
1785 @r#"
1786 {
1787 "sum": 3.0,
1788 "average": null,
1789 "min": 0.0,
1790 "max": 3.0
1791 }
1792 "#
1793 );
1794 },
1795 );
1796 }
1797
1798 #[test]
1799 fn elixir_reraise_counts() {
1800 check_metrics::<ElixirParser>(
1804 "defmodule Foo do\n def wrap(stack) do\n reraise(\"oops\", stack)\n end\nend\n",
1805 "foo.ex",
1806 |metric| {
1807 assert_eq!(metric.nexits.exit_sum(), 1.0);
1808 },
1809 );
1810 }
1811
1812 #[test]
1813 fn elixir_lookalike_call_is_not_exit() {
1814 check_metrics::<ElixirParser>(
1818 "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",
1819 "foo.ex",
1820 |metric| {
1821 assert_eq!(metric.nexits.exit_sum(), 0.0);
1822 },
1823 );
1824 }
1825
1826 #[test]
1827 fn ruby_no_exit() {
1828 check_metrics::<RubyParser>("def foo\n a = 1\n a + 1\nend\n", "foo.rb", |metric| {
1830 assert_eq!(metric.nexits.exit_sum(), 0.0);
1831 });
1832 }
1833
1834 #[test]
1835 fn ruby_multiple_returns() {
1836 check_metrics::<RubyParser>(
1839 "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",
1840 "foo.rb",
1841 |metric| {
1842 assert_eq!(metric.nexits.exit_sum(), 4.0);
1843 },
1844 );
1845 }
1846
1847 #[test]
1848 fn ruby_explicit_returns() {
1849 check_metrics::<RubyParser>(
1853 "def foo(x)\n return 0 if x.nil?\n yield x\n return x * 2\nend\n",
1854 "foo.rb",
1855 |metric| {
1856 assert_eq!(metric.nexits.exit_sum(), 2.0);
1857 insta::assert_json_snapshot!(metric.nexits);
1858 },
1859 );
1860 }
1861
1862 #[test]
1863 fn python_return_and_raise() {
1864 check_metrics::<PythonParser>(
1868 "def parse(s):
1869 if not s:
1870 raise ValueError(\"empty\")
1871 return int(s)",
1872 "foo.py",
1873 |metric| {
1874 assert_eq!(metric.nexits.exit_sum(), 2.0);
1875 insta::assert_json_snapshot!(
1876 metric.nexits,
1877 @r###"
1878 {
1879 "sum": 2.0,
1880 "average": 2.0,
1881 "min": 0.0,
1882 "max": 2.0
1883 }
1884 "###
1885 );
1886 },
1887 );
1888 }
1889
1890 #[test]
1891 fn javascript_return_and_throw() {
1892 check_metrics::<JavascriptParser>(
1894 "function parseLength(s) {
1895 if (s === null) throw new Error('null');
1896 return s.length;
1897 }",
1898 "foo.js",
1899 |metric| {
1900 assert_eq!(metric.nexits.exit_sum(), 2.0);
1901 insta::assert_json_snapshot!(
1902 metric.nexits,
1903 @r###"
1904 {
1905 "sum": 2.0,
1906 "average": 2.0,
1907 "min": 0.0,
1908 "max": 2.0
1909 }
1910 "###
1911 );
1912 },
1913 );
1914 }
1915
1916 #[test]
1917 fn mozjs_return_and_throw() {
1918 check_metrics::<MozjsParser>(
1920 "function parseLength(s) {
1921 if (s === null) throw new Error('null');
1922 return s.length;
1923 }",
1924 "foo.js",
1925 |metric| {
1926 assert_eq!(metric.nexits.exit_sum(), 2.0);
1927 insta::assert_json_snapshot!(
1928 metric.nexits,
1929 @r###"
1930 {
1931 "sum": 2.0,
1932 "average": 2.0,
1933 "min": 0.0,
1934 "max": 2.0
1935 }
1936 "###
1937 );
1938 },
1939 );
1940 }
1941
1942 #[test]
1943 fn typescript_return_and_throw() {
1944 check_metrics::<TypescriptParser>(
1945 "function parseLength(s: string | null): number {
1946 if (s === null) throw new Error('null');
1947 return s.length;
1948 }",
1949 "foo.ts",
1950 |metric| {
1951 assert_eq!(metric.nexits.exit_sum(), 2.0);
1952 insta::assert_json_snapshot!(
1953 metric.nexits,
1954 @r###"
1955 {
1956 "sum": 2.0,
1957 "average": 2.0,
1958 "min": 0.0,
1959 "max": 2.0
1960 }
1961 "###
1962 );
1963 },
1964 );
1965 }
1966
1967 #[test]
1968 fn tsx_return_and_throw() {
1969 check_metrics::<TsxParser>(
1970 "function parseLength(s: string | null): number {
1971 if (s === null) throw new Error('null');
1972 return s.length;
1973 }",
1974 "foo.tsx",
1975 |metric| {
1976 assert_eq!(metric.nexits.exit_sum(), 2.0);
1977 insta::assert_json_snapshot!(
1978 metric.nexits,
1979 @r###"
1980 {
1981 "sum": 2.0,
1982 "average": 2.0,
1983 "min": 0.0,
1984 "max": 2.0
1985 }
1986 "###
1987 );
1988 },
1989 );
1990 }
1991
1992 #[test]
1993 fn java_return_and_throw() {
1994 check_metrics::<JavaParser>(
1996 "class A {
1997 int parseLength(String s) {
1998 if (s == null) throw new NullPointerException();
1999 return s.length();
2000 }
2001 }",
2002 "foo.java",
2003 |metric| {
2004 assert_eq!(metric.nexits.exit_sum(), 2.0);
2005 insta::assert_json_snapshot!(
2006 metric.nexits,
2007 @r###"
2008 {
2009 "sum": 2.0,
2010 "average": 2.0,
2011 "min": 0.0,
2012 "max": 2.0
2013 }
2014 "###
2015 );
2016 },
2017 );
2018 }
2019
2020 #[test]
2021 fn groovy_no_exit() {
2022 check_metrics::<GroovyParser>("int a = 42", "foo.groovy", |metric| {
2024 assert_eq!(metric.nexits.exit_sum(), 0.0);
2025 });
2026 }
2027
2028 #[test]
2029 fn groovy_simple_function() {
2030 check_metrics::<GroovyParser>(
2032 "int answer() {
2033 return 42
2034 }",
2035 "foo.groovy",
2036 |metric| {
2037 assert_eq!(metric.nexits.exit_sum(), 1.0);
2038 },
2039 );
2040 }
2041
2042 #[test]
2043 fn groovy_return_and_throw() {
2044 check_metrics::<GroovyParser>(
2045 "class A {
2046 int parseLength(String s) {
2047 if (s == null) throw new NullPointerException()
2048 return s.length()
2049 }
2050 }",
2051 "foo.groovy",
2052 |metric| {
2053 assert_eq!(metric.nexits.exit_sum(), 2.0);
2054 },
2055 );
2056 }
2057
2058 #[test]
2059 fn groovy_yield_in_switch_expression() {
2060 check_metrics::<GroovyParser>(
2063 "class A {
2064 int describe(int n) {
2065 return switch (n) {
2066 case 0: yield 100;
2067 default: yield 200;
2068 }
2069 }
2070 }",
2071 "foo.groovy",
2072 |metric| {
2073 assert_eq!(metric.nexits.exit_sum(), 3.0);
2074 },
2075 );
2076 }
2077
2078 #[test]
2079 fn groovy_implicit_return_not_counted() {
2080 check_metrics::<GroovyParser>("int identity(int x) { x }", "foo.groovy", |metric| {
2085 assert_eq!(metric.nexits.exit_sum(), 0.0);
2086 });
2087 }
2088
2089 #[test]
2090 fn cpp_return_and_throw() {
2091 check_metrics::<CppParser>(
2093 "int parseLength(const char* s) {
2094 if (s == nullptr) throw std::invalid_argument(\"null\");
2095 return 0;
2096 }",
2097 "foo.cpp",
2098 |metric| {
2099 assert_eq!(metric.nexits.exit_sum(), 2.0);
2100 insta::assert_json_snapshot!(
2101 metric.nexits,
2102 @r###"
2103 {
2104 "sum": 2.0,
2105 "average": 2.0,
2106 "min": 0.0,
2107 "max": 2.0
2108 }
2109 "###
2110 );
2111 },
2112 );
2113 }
2114
2115 #[test]
2116 fn python_yield_counts_as_exit() {
2117 check_metrics::<PythonParser>(
2122 "def gen():
2123 yield 1
2124 yield 2
2125 return",
2126 "foo.py",
2127 |metric| {
2128 assert_eq!(metric.nexits.exit_sum(), 3.0);
2129 insta::assert_json_snapshot!(
2130 metric.nexits,
2131 @r###"
2132 {
2133 "sum": 3.0,
2134 "average": 3.0,
2135 "min": 0.0,
2136 "max": 3.0
2137 }
2138 "###
2139 );
2140 },
2141 );
2142 }
2143
2144 #[test]
2145 fn javascript_yield_counts_as_exit() {
2146 check_metrics::<JavascriptParser>(
2149 "function* gen() {
2150 yield 1;
2151 yield 2;
2152 return;
2153 }",
2154 "foo.js",
2155 |metric| {
2156 assert_eq!(metric.nexits.exit_sum(), 3.0);
2157 insta::assert_json_snapshot!(
2158 metric.nexits,
2159 @r###"
2160 {
2161 "sum": 3.0,
2162 "average": 3.0,
2163 "min": 0.0,
2164 "max": 3.0
2165 }
2166 "###
2167 );
2168 },
2169 );
2170 }
2171
2172 #[test]
2173 fn mozjs_yield_counts_as_exit() {
2174 check_metrics::<MozjsParser>(
2176 "function* gen() {
2177 yield 1;
2178 yield 2;
2179 return;
2180 }",
2181 "foo.js",
2182 |metric| {
2183 assert_eq!(metric.nexits.exit_sum(), 3.0);
2184 insta::assert_json_snapshot!(
2185 metric.nexits,
2186 @r###"
2187 {
2188 "sum": 3.0,
2189 "average": 3.0,
2190 "min": 0.0,
2191 "max": 3.0
2192 }
2193 "###
2194 );
2195 },
2196 );
2197 }
2198
2199 #[test]
2200 fn typescript_yield_counts_as_exit() {
2201 check_metrics::<TypescriptParser>(
2202 "function* gen(): Generator<number> {
2203 yield 1;
2204 yield 2;
2205 return;
2206 }",
2207 "foo.ts",
2208 |metric| {
2209 assert_eq!(metric.nexits.exit_sum(), 3.0);
2210 insta::assert_json_snapshot!(
2211 metric.nexits,
2212 @r###"
2213 {
2214 "sum": 3.0,
2215 "average": 3.0,
2216 "min": 0.0,
2217 "max": 3.0
2218 }
2219 "###
2220 );
2221 },
2222 );
2223 }
2224
2225 #[test]
2226 fn tsx_yield_counts_as_exit() {
2227 check_metrics::<TsxParser>(
2228 "function* gen(): Generator<number> {
2229 yield 1;
2230 yield 2;
2231 return;
2232 }",
2233 "foo.tsx",
2234 |metric| {
2235 assert_eq!(metric.nexits.exit_sum(), 3.0);
2236 insta::assert_json_snapshot!(
2237 metric.nexits,
2238 @r###"
2239 {
2240 "sum": 3.0,
2241 "average": 3.0,
2242 "min": 0.0,
2243 "max": 3.0
2244 }
2245 "###
2246 );
2247 },
2248 );
2249 }
2250
2251 #[test]
2252 fn python_yield_forms_count_as_exit() {
2253 check_metrics::<PythonParser>(
2258 "def gen():
2259 yield
2260 yield 1
2261 yield from range(3)",
2262 "foo.py",
2263 |metric| {
2264 assert_eq!(metric.nexits.exit_sum(), 3.0);
2265 insta::assert_json_snapshot!(
2266 metric.nexits,
2267 @r###"
2268 {
2269 "sum": 3.0,
2270 "average": 3.0,
2271 "min": 0.0,
2272 "max": 3.0
2273 }
2274 "###
2275 );
2276 },
2277 );
2278 }
2279
2280 #[test]
2281 fn javascript_yield_delegate_counts_as_exit() {
2282 check_metrics::<JavascriptParser>(
2287 "function* gen() {
2288 yield 1;
2289 yield* other();
2290 yield 2;
2291 }",
2292 "foo.js",
2293 |metric| {
2294 assert_eq!(metric.nexits.exit_sum(), 3.0);
2295 insta::assert_json_snapshot!(
2296 metric.nexits,
2297 @r###"
2298 {
2299 "sum": 3.0,
2300 "average": 3.0,
2301 "min": 0.0,
2302 "max": 3.0
2303 }
2304 "###
2305 );
2306 },
2307 );
2308 }
2309
2310 #[test]
2311 fn mozjs_yield_delegate_counts_as_exit() {
2312 check_metrics::<MozjsParser>(
2313 "function* gen() {
2314 yield 1;
2315 yield* other();
2316 yield 2;
2317 }",
2318 "foo.js",
2319 |metric| {
2320 assert_eq!(metric.nexits.exit_sum(), 3.0);
2321 insta::assert_json_snapshot!(
2322 metric.nexits,
2323 @r###"
2324 {
2325 "sum": 3.0,
2326 "average": 3.0,
2327 "min": 0.0,
2328 "max": 3.0
2329 }
2330 "###
2331 );
2332 },
2333 );
2334 }
2335
2336 #[test]
2337 fn typescript_yield_delegate_counts_as_exit() {
2338 check_metrics::<TypescriptParser>(
2339 "function* gen(): Generator<number> {
2340 yield 1;
2341 yield* other();
2342 yield 2;
2343 }",
2344 "foo.ts",
2345 |metric| {
2346 assert_eq!(metric.nexits.exit_sum(), 3.0);
2347 insta::assert_json_snapshot!(
2348 metric.nexits,
2349 @r###"
2350 {
2351 "sum": 3.0,
2352 "average": 3.0,
2353 "min": 0.0,
2354 "max": 3.0
2355 }
2356 "###
2357 );
2358 },
2359 );
2360 }
2361
2362 #[test]
2363 fn tsx_yield_delegate_counts_as_exit() {
2364 check_metrics::<TsxParser>(
2365 "function* gen(): Generator<number> {
2366 yield 1;
2367 yield* other();
2368 yield 2;
2369 }",
2370 "foo.tsx",
2371 |metric| {
2372 assert_eq!(metric.nexits.exit_sum(), 3.0);
2373 insta::assert_json_snapshot!(
2374 metric.nexits,
2375 @r###"
2376 {
2377 "sum": 3.0,
2378 "average": 3.0,
2379 "min": 0.0,
2380 "max": 3.0
2381 }
2382 "###
2383 );
2384 },
2385 );
2386 }
2387}