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;
24
25use crate::*;
26
27#[derive(Clone, Debug, PartialEq)]
29#[non_exhaustive]
30pub struct Stats {
31 functions: usize,
32 closures: usize,
33 functions_sum: usize,
34 closures_sum: usize,
35 functions_min: usize,
36 functions_max: usize,
37 closures_min: usize,
38 closures_max: usize,
39 space_count: usize,
40}
41
42impl Default for Stats {
43 fn default() -> Self {
44 Self {
45 functions: 0,
46 closures: 0,
47 functions_sum: 0,
48 closures_sum: 0,
49 functions_min: usize::MAX,
50 functions_max: 0,
51 closures_min: usize::MAX,
52 closures_max: 0,
53 space_count: 1,
54 }
55 }
56}
57
58impl fmt::Display for Stats {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 write!(
61 f,
62 "functions: {}, \
63 closures: {}, \
64 functions_average: {}, \
65 closures_average: {}, \
66 total: {}, \
67 average: {}, \
68 functions_min: {}, \
69 functions_max: {}, \
70 closures_min: {}, \
71 closures_max: {}",
72 self.functions_sum(),
73 self.closures_sum(),
74 self.functions_average(),
75 self.closures_average(),
76 self.total(),
77 self.average(),
78 self.functions_min(),
79 self.functions_max(),
80 self.closures_min(),
81 self.closures_max(),
82 )
83 }
84}
85
86impl Stats {
87 pub fn merge(&mut self, other: &Stats) {
89 self.functions_min = self.functions_min.min(other.functions_min);
90 self.functions_max = self.functions_max.max(other.functions_max);
91 self.closures_min = self.closures_min.min(other.closures_min);
92 self.closures_max = self.closures_max.max(other.closures_max);
93 self.functions_sum += other.functions_sum;
94 self.closures_sum += other.closures_sum;
95 self.space_count += other.space_count;
96 }
97
98 #[inline]
100 #[must_use]
101 pub fn functions(&self) -> u64 {
102 self.functions as u64
104 }
105
106 #[inline]
108 #[must_use]
109 pub fn closures(&self) -> u64 {
110 self.closures as u64
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn functions_sum(&self) -> u64 {
117 self.functions_sum as u64
119 }
120
121 #[inline]
123 #[must_use]
124 pub fn closures_sum(&self) -> u64 {
125 self.closures_sum as u64
126 }
127
128 #[inline]
136 #[must_use]
137 pub fn functions_average(&self) -> f64 {
138 crate::metrics::average(self.functions_sum() as f64, self.space_count)
139 }
140
141 #[inline]
143 #[must_use]
144 pub fn closures_average(&self) -> f64 {
145 crate::metrics::average(self.closures_sum() as f64, self.space_count)
146 }
147
148 #[inline]
150 #[must_use]
151 pub fn average(&self) -> f64 {
152 crate::metrics::average(self.total() as f64, self.space_count)
153 }
154
155 #[inline]
161 #[must_use]
162 pub fn functions_min(&self) -> u64 {
163 if self.functions_min == usize::MAX {
165 0
166 } else {
167 self.functions_min as u64
168 }
169 }
170
171 #[inline]
175 #[must_use]
176 pub fn closures_min(&self) -> u64 {
177 if self.closures_min == usize::MAX {
178 0
179 } else {
180 self.closures_min as u64
181 }
182 }
183 #[inline]
185 #[must_use]
186 pub fn functions_max(&self) -> u64 {
187 self.functions_max as u64
189 }
190
191 #[inline]
193 #[must_use]
194 pub fn closures_max(&self) -> u64 {
195 self.closures_max as u64
196 }
197 #[inline]
200 #[must_use]
201 pub fn total(&self) -> u64 {
202 self.functions_sum() + self.closures_sum()
203 }
204 #[inline]
205 pub(crate) fn compute_sum(&mut self) {
206 self.functions_sum += self.functions;
207 self.closures_sum += self.closures;
208 }
209 #[inline]
210 pub(crate) fn compute_minmax(&mut self) {
211 self.functions_min = self.functions_min.min(self.functions);
212 self.functions_max = self.functions_max.max(self.functions);
213 self.closures_min = self.closures_min.min(self.closures);
214 self.closures_max = self.closures_max.max(self.closures);
215 self.compute_sum();
216 }
217}
218
219#[doc(hidden)]
220pub(crate) trait Nom
222where
223 Self: Checker,
224{
225 fn compute<'a>(node: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, stats: &mut Stats) {
241 if Self::is_func_with_code(node, code, ancestors) {
242 stats.functions += 1;
243 return;
244 }
245 if Self::is_closure(node, ancestors) {
246 stats.closures += 1;
247 }
248 }
249}
250
251implement_metric_trait!(
252 [Nom],
253 PythonCode,
254 MozjsCode,
255 JavascriptCode,
256 TypescriptCode,
257 TsxCode,
258 CppCode,
259 MozcppCode,
260 CCode,
261 ObjcCode,
262 RustCode,
263 PreprocCode,
264 CcommentCode,
265 JavaCode,
266 KotlinCode,
267 GoCode,
268 PerlCode,
269 BashCode,
270 LuaCode,
271 TclCode,
272 PhpCode,
273 CsharpCode,
274 ElixirCode,
275 RubyCode,
276 GroovyCode,
277 IrulesCode
278);
279
280#[cfg(test)]
281#[allow(
282 clippy::float_cmp,
283 clippy::cast_precision_loss,
284 clippy::cast_possible_truncation,
285 clippy::cast_sign_loss,
286 clippy::similar_names,
287 clippy::doc_markdown,
288 clippy::needless_raw_string_hashes,
289 clippy::too_many_lines
290)]
291mod tests {
292 use crate::test_support::check_metrics_only_shim;
293
294 use super::*;
295
296 check_metrics_only_shim!(check_metrics, Nom);
297 check_metrics_only_shim!(check_nom_and_npm, Nom, Npm);
301
302 #[test]
307 fn nom_empty_file_min_is_zero() {
308 let stats = Stats::default();
309 assert_eq!(stats.functions_min(), 0);
310 assert_eq!(stats.closures_min(), 0);
311 }
312
313 #[cfg(feature = "python")]
314 #[test]
315 fn python_nom() {
316 check_metrics::<PythonParser>(
317 "def a():
318 pass
319 def b():
320 pass
321 def c():
322 pass
323 x = lambda a : a + 42",
324 "foo.py",
325 |metric| {
326 assert_eq!(metric.nom.closures_sum(), 1);
332 insta::assert_json_snapshot!(
333 metric.nom,
334 @r#"
335 {
336 "functions": 3,
337 "closures": 1,
338 "functions_average": 0.75,
339 "closures_average": 0.25,
340 "total": 4,
341 "average": 1.0,
342 "functions_min": 0,
343 "functions_max": 1,
344 "closures_min": 0,
345 "closures_max": 1
346 }
347 "#
348 );
349 },
350 );
351 }
352
353 #[cfg(feature = "rust")]
354 #[test]
355 fn rust_nom() {
356 check_metrics::<RustParser>(
357 "mod A { fn foo() {}}
358 mod B { fn foo() {}}
359 let closure = |i: i32| -> i32 { i + 42 };",
360 "foo.rs",
361 |metric| {
362 insta::assert_json_snapshot!(
364 metric.nom,
365 @r#"
366 {
367 "functions": 2,
368 "closures": 1,
369 "functions_average": 0.5,
370 "closures_average": 0.25,
371 "total": 3,
372 "average": 0.75,
373 "functions_min": 0,
374 "functions_max": 1,
375 "closures_min": 0,
376 "closures_max": 1
377 }
378 "#
379 );
380 },
381 );
382 }
383
384 #[cfg(feature = "c")]
385 #[test]
386 fn c_nom() {
387 check_metrics::<CParser>(
388 "int foo();
389
390 int foo() {
391 return 0;
392 }",
393 "foo.c",
394 |metric| {
395 insta::assert_json_snapshot!(
397 metric.nom,
398 @r#"
399 {
400 "functions": 1,
401 "closures": 0,
402 "functions_average": 0.5,
403 "closures_average": 0.0,
404 "total": 1,
405 "average": 0.5,
406 "functions_min": 0,
407 "functions_max": 1,
408 "closures_min": 0,
409 "closures_max": 0
410 }
411 "#
412 );
413 },
414 );
415 }
416
417 #[cfg(feature = "cpp")]
418 #[test]
419 fn cpp_nom() {
420 check_metrics::<CppParser>(
421 "struct A {
422 void foo(int) {}
423 void foo(double) {}
424 };
425 int b = [](int x) -> int { return x + 42; };",
426 "foo.cpp",
427 |metric| {
428 insta::assert_json_snapshot!(
430 metric.nom,
431 @r#"
432 {
433 "functions": 2,
434 "closures": 1,
435 "functions_average": 0.5,
436 "closures_average": 0.25,
437 "total": 3,
438 "average": 0.75,
439 "functions_min": 0,
440 "functions_max": 1,
441 "closures_min": 0,
442 "closures_max": 1
443 }
444 "#
445 );
446 },
447 );
448 }
449
450 #[cfg(feature = "cpp")]
455 #[test]
456 fn cpp_free_and_member_functions() {
457 check_metrics::<CppParser>(
458 "int free_fn(int x) { return x; }
459 struct S {
460 int member_fn(int x) { return x + 1; }
461 };",
462 "foo.cpp",
463 |metric| {
464 let s = &metric.nom;
466 assert_eq!(s.functions_sum(), 2);
467 assert_eq!(s.closures_sum(), 0);
468 assert_eq!(s.total(), 2);
469 insta::assert_json_snapshot!(metric.nom);
470 },
471 );
472 }
473
474 #[cfg(feature = "cpp")]
478 #[test]
479 fn cpp_static_member_function() {
480 check_metrics::<CppParser>(
481 "struct S {
482 static int factory(int x) { return x; }
483 int method(int x) { return x + 1; }
484 };",
485 "foo.cpp",
486 |metric| {
487 let s = &metric.nom;
488 assert_eq!(s.functions_sum(), 2);
489 assert_eq!(s.closures_sum(), 0);
490 insta::assert_json_snapshot!(metric.nom);
491 },
492 );
493 }
494
495 #[cfg(feature = "cpp")]
500 #[test]
501 fn cpp_constructor_and_destructor() {
502 check_metrics::<CppParser>(
503 "struct S {
504 S() {}
505 ~S() {}
506 int method() { return 0; }
507 };",
508 "foo.cpp",
509 |metric| {
510 let s = &metric.nom;
511 assert_eq!(s.functions_sum(), 3);
513 assert_eq!(s.closures_sum(), 0);
514 insta::assert_json_snapshot!(metric.nom);
515 },
516 );
517 }
518
519 #[cfg(feature = "cpp")]
523 #[test]
524 fn cpp_operator_overloads() {
525 check_metrics::<CppParser>(
526 "struct V {
527 int x;
528 V operator+(const V& o) const { return V{x + o.x}; }
529 bool operator==(const V& o) const { return x == o.x; }
530 };",
531 "foo.cpp",
532 |metric| {
533 let s = &metric.nom;
534 assert_eq!(s.functions_sum(), 2);
535 assert_eq!(s.closures_sum(), 0);
536 insta::assert_json_snapshot!(metric.nom);
537 },
538 );
539 }
540
541 #[cfg(feature = "cpp")]
545 #[test]
546 fn cpp_function_template() {
547 check_metrics::<CppParser>(
548 "template<typename T>
549 T identity(T x) { return x; }",
550 "foo.cpp",
551 |metric| {
552 let s = &metric.nom;
553 assert_eq!(s.functions_sum(), 1);
554 assert_eq!(s.closures_sum(), 0);
555 insta::assert_json_snapshot!(metric.nom);
556 },
557 );
558 }
559
560 #[cfg(feature = "cpp")]
564 #[test]
565 fn cpp_class_template_members() {
566 check_metrics::<CppParser>(
567 "template<typename T>
568 struct Box {
569 T value;
570 T get() const { return value; }
571 void set(T v) { value = v; }
572 };",
573 "foo.cpp",
574 |metric| {
575 let s = &metric.nom;
576 assert_eq!(s.functions_sum(), 2);
577 assert_eq!(s.closures_sum(), 0);
578 insta::assert_json_snapshot!(metric.nom);
579 },
580 );
581 }
582
583 #[cfg(feature = "cpp")]
588 #[test]
589 fn cpp_lambdas_inside_function() {
590 check_metrics::<CppParser>(
591 "int run() {
592 auto add = [](int a, int b) { return a + b; };
593 auto mul = [](int a, int b) { return a * b; };
594 return add(1, 2) + mul(3, 4);
595 }",
596 "foo.cpp",
597 |metric| {
598 let s = &metric.nom;
599 assert_eq!(s.functions_sum(), 1);
601 assert_eq!(s.closures_sum(), 2);
602 assert_eq!(s.total(), 3);
603 insta::assert_json_snapshot!(metric.nom);
604 },
605 );
606 }
607
608 #[cfg(feature = "javascript")]
609 #[test]
610 fn javascript_nom() {
611 check_metrics::<JavascriptParser>(
612 "function f(a, b) {
613 function foo(a) {
614 return a;
615 }
616 var bar = (function () {
617 var counter = 0;
618 return function () {
619 counter += 1;
620 return counter
621 }
622 })();
623 return bar(foo(a), a);
624 }",
625 "foo.js",
626 |metric| {
627 insta::assert_json_snapshot!(
638 metric.nom,
639 @r#"
640 {
641 "functions": 2,
642 "closures": 2,
643 "functions_average": 0.4,
644 "closures_average": 0.4,
645 "total": 4,
646 "average": 0.8,
647 "functions_min": 0,
648 "functions_max": 1,
649 "closures_min": 0,
650 "closures_max": 1
651 }
652 "#
653 );
654 },
655 );
656 }
657
658 #[cfg(any(feature = "javascript", feature = "mozjs", feature = "typescript"))]
671 fn check_js_binding_site_parity(lang: crate::LANG) {
672 let split = |source: &str| {
673 let m = crate::test_support::metrics_verbatim(
674 lang,
675 source.as_bytes(),
676 crate::MetricsOptions::default(),
677 );
678 (m.nom.functions_sum(), m.nom.closures_sum())
679 };
680
681 let cases: &[(&str, &str, &str, bool)] = &[
683 (
689 "bare IIFE",
690 "(function (c) { return c; })(1);",
691 "(() => 1)(1);",
692 false,
693 ),
694 (
695 "bound IIFE",
696 "const v = (function (c) { return c; })(1);",
697 "const v = (() => 1)(1);",
698 false,
699 ),
700 (
705 "string-keyed pair",
706 "({ \"k\": function () { return 1; } });",
707 "({ \"k\": () => 1 });",
708 true,
709 ),
710 (
714 "class field initialiser",
715 "class C { p = function () { return 1; }; }",
716 "class C { p = () => 1; }",
717 true,
718 ),
719 (
728 "computed-key class field",
729 "class C { [\"k\"] = function () { return 1; }; }",
730 "class C { [\"k\"] = () => 1; }",
731 true,
732 ),
733 (
734 "string-named class field",
735 "class C { \"s\" = function () { return 1; }; }",
736 "class C { \"s\" = () => 1; }",
737 true,
738 ),
739 (
740 "private class field",
741 "class C { #p = function () { return 1; }; }",
742 "class C { #p = () => 1; }",
743 true,
744 ),
745 (
747 "positional callback",
748 "run(function () { return 1; });",
749 "run(() => 1);",
750 false,
751 ),
752 (
763 "member-expression object",
764 "run(function () { return 1; }.bind(this));",
765 "run((() => 1).bind(this));",
766 false,
767 ),
768 ];
769
770 for &(label, func_form, arrow_form, expected) in cases {
771 for (form, source) in [("function", func_form), ("arrow", arrow_form)] {
772 let (functions, closures) = split(source);
773 assert_eq!(
774 functions == 1,
775 expected,
776 "{lang:?} {label} / {form}: expected a {}, got functions {functions} closures {closures}",
777 if expected { "function" } else { "closure" },
778 );
779 }
780 }
781
782 assert_eq!(
790 split("run(function g() { return 1; });").0,
791 1,
792 "{lang:?}: a named function expression carries its own name",
793 );
794 assert_eq!(
795 split("run(x => x);").1,
796 1,
797 "{lang:?}: an arrow's identifier child is its parameter, not a name",
798 );
799 }
800
801 #[cfg(feature = "javascript")]
802 #[test]
803 fn javascript_binding_site_parity() {
804 check_js_binding_site_parity(crate::LANG::Javascript);
805 }
806
807 #[cfg(feature = "mozjs")]
808 #[test]
809 fn mozjs_binding_site_parity() {
810 check_js_binding_site_parity(crate::LANG::Mozjs);
811 }
812
813 #[cfg(feature = "typescript")]
814 #[test]
815 fn typescript_binding_site_parity() {
816 check_js_binding_site_parity(crate::LANG::Typescript);
817 }
818
819 #[cfg(feature = "typescript")]
820 #[test]
821 fn tsx_binding_site_parity() {
822 check_js_binding_site_parity(crate::LANG::Tsx);
823 }
824
825 #[cfg(feature = "javascript")]
826 #[test]
827 fn javascript_call_nom() {
828 check_metrics::<JavascriptParser>(
829 "add_task(async function test_safe_mode() {
830 gAppInfo.inSafeMode = true;
831 });",
832 "foo.js",
833 |metric| {
834 insta::assert_json_snapshot!(
837 metric.nom,
838 @r#"
839 {
840 "functions": 1,
841 "closures": 0,
842 "functions_average": 0.5,
843 "closures_average": 0.0,
844 "total": 1,
845 "average": 0.5,
846 "functions_min": 0,
847 "functions_max": 1,
848 "closures_min": 0,
849 "closures_max": 0
850 }
851 "#
852 );
853 },
854 );
855 }
856
857 #[cfg(feature = "javascript")]
858 #[test]
859 fn javascript_assignment_nom() {
860 check_metrics::<JavascriptParser>(
861 "AnimationTest.prototype.enableDisplay = function(element) {};",
862 "foo.js",
863 |metric| {
864 insta::assert_json_snapshot!(
866 metric.nom,
867 @r#"
868 {
869 "functions": 1,
870 "closures": 0,
871 "functions_average": 0.5,
872 "closures_average": 0.0,
873 "total": 1,
874 "average": 0.5,
875 "functions_min": 0,
876 "functions_max": 1,
877 "closures_min": 0,
878 "closures_max": 0
879 }
880 "#
881 );
882 },
883 );
884 }
885
886 #[cfg(feature = "javascript")]
887 #[test]
888 fn javascript_labeled_nom() {
889 check_metrics::<JavascriptParser>(
890 "toJSON: function() {
891 return this.inspect(true);
892 }",
893 "foo.js",
894 |metric| {
895 insta::assert_json_snapshot!(
897 metric.nom,
898 @r#"
899 {
900 "functions": 1,
901 "closures": 0,
902 "functions_average": 0.5,
903 "closures_average": 0.0,
904 "total": 1,
905 "average": 0.5,
906 "functions_min": 0,
907 "functions_max": 1,
908 "closures_min": 0,
909 "closures_max": 0
910 }
911 "#
912 );
913 },
914 );
915 }
916
917 #[cfg(feature = "javascript")]
918 #[test]
919 fn javascript_labeled_arrow_nom() {
920 check_metrics::<JavascriptParser>(
921 "const dimConverters = {
922 pt: x => x,
923 };",
924 "foo.js",
925 |metric| {
926 insta::assert_json_snapshot!(
928 metric.nom,
929 @r#"
930 {
931 "functions": 1,
932 "closures": 0,
933 "functions_average": 0.5,
934 "closures_average": 0.0,
935 "total": 1,
936 "average": 0.5,
937 "functions_min": 0,
938 "functions_max": 1,
939 "closures_min": 0,
940 "closures_max": 0
941 }
942 "#
943 );
944 },
945 );
946 }
947
948 #[cfg(feature = "javascript")]
949 #[test]
950 fn javascript_pair_nom() {
951 check_metrics::<JavascriptParser>(
952 "return {
953 initialize: function(object) {
954 this._object = object.toObject();
955 },
956 }",
957 "foo.js",
958 |metric| {
959 insta::assert_json_snapshot!(
961 metric.nom,
962 @r#"
963 {
964 "functions": 1,
965 "closures": 0,
966 "functions_average": 0.5,
967 "closures_average": 0.0,
968 "total": 1,
969 "average": 0.5,
970 "functions_min": 0,
971 "functions_max": 1,
972 "closures_min": 0,
973 "closures_max": 0
974 }
975 "#
976 );
977 },
978 );
979 }
980
981 #[cfg(any(feature = "javascript", feature = "mozjs", feature = "typescript"))]
982 fn check_returned_object_arrow_nom<T: MetricSuite>(file_name: &str) {
983 check_metrics::<T>(
984 "function f() { return { foo: x => x }; }",
985 file_name,
986 |metric| {
987 insta::allow_duplicates! {
988 insta::assert_json_snapshot!(
989 metric.nom,
990 @r#"
991 {
992 "functions": 2,
993 "closures": 0,
994 "functions_average": 0.6666666666666666,
995 "closures_average": 0.0,
996 "total": 2,
997 "average": 0.6666666666666666,
998 "functions_min": 0,
999 "functions_max": 1,
1000 "closures_min": 0,
1001 "closures_max": 0
1002 }
1003 "#
1004 );
1005 }
1006 },
1007 );
1008 }
1009
1010 #[cfg(feature = "javascript")]
1011 #[test]
1012 fn javascript_returned_object_arrow_nom() {
1013 check_returned_object_arrow_nom::<JavascriptParser>("foo.js");
1014 }
1015
1016 #[cfg(feature = "mozjs")]
1017 #[test]
1018 fn mozjs_returned_object_arrow_nom() {
1019 check_returned_object_arrow_nom::<MozjsParser>("foo.js");
1020 }
1021
1022 #[cfg(feature = "typescript")]
1023 #[test]
1024 fn typescript_returned_object_arrow_nom() {
1025 check_returned_object_arrow_nom::<TypescriptParser>("foo.ts");
1026 }
1027
1028 #[cfg(feature = "typescript")]
1029 #[test]
1030 fn tsx_returned_object_arrow_nom() {
1031 check_returned_object_arrow_nom::<TsxParser>("foo.tsx");
1032 }
1033
1034 #[cfg(feature = "javascript")]
1035 #[test]
1036 fn javascript_unnamed_nom() {
1037 check_metrics::<JavascriptParser>(
1038 "Ajax.getTransport = Try.these(
1039 function() {
1040 return function(){ return new XMLHttpRequest()}
1041 }
1042 );",
1043 "foo.js",
1044 |metric| {
1045 insta::assert_json_snapshot!(
1047 metric.nom,
1048 @r#"
1049 {
1050 "functions": 0,
1051 "closures": 2,
1052 "functions_average": 0.0,
1053 "closures_average": 0.6666666666666666,
1054 "total": 2,
1055 "average": 0.6666666666666666,
1056 "functions_min": 0,
1057 "functions_max": 0,
1058 "closures_min": 0,
1059 "closures_max": 1
1060 }
1061 "#
1062 );
1063 },
1064 );
1065 }
1066
1067 #[cfg(feature = "javascript")]
1068 #[test]
1069 fn javascript_arrow_nom() {
1070 check_metrics::<JavascriptParser>(
1071 "var materials = [\"Hydrogen\"];
1072 materials.map(material => material.length);
1073 let add = (a, b) => a + b;",
1074 "foo.js",
1075 |metric| {
1076 insta::assert_json_snapshot!(
1080 metric.nom,
1081 @r#"
1082 {
1083 "functions": 1,
1084 "closures": 1,
1085 "functions_average": 0.3333333333333333,
1086 "closures_average": 0.3333333333333333,
1087 "total": 2,
1088 "average": 0.6666666666666666,
1089 "functions_min": 0,
1090 "functions_max": 1,
1091 "closures_min": 0,
1092 "closures_max": 1
1093 }
1094 "#
1095 );
1096 },
1097 );
1098 }
1099
1100 #[cfg(feature = "javascript")]
1101 #[test]
1102 fn javascript_arrow_assignment_nom() {
1103 check_metrics::<JavascriptParser>("sink.onPull = () => { };", "foo.js", |metric| {
1104 insta::assert_json_snapshot!(
1106 metric.nom,
1107 @r#"
1108 {
1109 "functions": 1,
1110 "closures": 0,
1111 "functions_average": 0.5,
1112 "closures_average": 0.0,
1113 "total": 1,
1114 "average": 0.5,
1115 "functions_min": 0,
1116 "functions_max": 1,
1117 "closures_min": 0,
1118 "closures_max": 0
1119 }
1120 "#
1121 );
1122 });
1123 }
1124
1125 #[cfg(feature = "javascript")]
1126 #[test]
1127 fn javascript_arrow_new_nom() {
1128 check_metrics::<JavascriptParser>(
1129 "const response = new Promise(resolve => channel.port1.onmessage = resolve);",
1130 "foo.js",
1131 |metric| {
1132 insta::assert_json_snapshot!(
1134 metric.nom,
1135 @r#"
1136 {
1137 "functions": 0,
1138 "closures": 1,
1139 "functions_average": 0.0,
1140 "closures_average": 0.5,
1141 "total": 1,
1142 "average": 0.5,
1143 "functions_min": 0,
1144 "functions_max": 0,
1145 "closures_min": 0,
1146 "closures_max": 1
1147 }
1148 "#
1149 );
1150 },
1151 );
1152 }
1153
1154 #[cfg(feature = "javascript")]
1155 #[test]
1156 fn javascript_arrow_call_nom() {
1157 check_metrics::<JavascriptParser>(
1158 "let notDisabled = TestUtils.waitForCondition(
1159 () => !backbutton.hasAttribute(\"disabled\")
1160 );",
1161 "foo.js",
1162 |metric| {
1163 insta::assert_json_snapshot!(
1165 metric.nom,
1166 @r#"
1167 {
1168 "functions": 0,
1169 "closures": 1,
1170 "functions_average": 0.0,
1171 "closures_average": 0.5,
1172 "total": 1,
1173 "average": 0.5,
1174 "functions_min": 0,
1175 "functions_max": 0,
1176 "closures_min": 0,
1177 "closures_max": 1
1178 }
1179 "#
1180 );
1181 },
1182 );
1183 }
1184
1185 #[cfg(feature = "java")]
1186 #[test]
1187 fn java_nom() {
1188 check_metrics::<JavaParser>(
1189 "class A {
1190 public void foo(){
1191 return;
1192 }
1193 public void bar(){
1194 return;
1195 }
1196 }",
1197 "foo.java",
1198 |metric| {
1199 insta::assert_json_snapshot!(
1201 metric.nom,
1202 @r#"
1203 {
1204 "functions": 2,
1205 "closures": 0,
1206 "functions_average": 0.5,
1207 "closures_average": 0.0,
1208 "total": 2,
1209 "average": 0.5,
1210 "functions_min": 0,
1211 "functions_max": 1,
1212 "closures_min": 0,
1213 "closures_max": 0
1214 }
1215 "#
1216 );
1217 },
1218 );
1219 }
1220
1221 #[cfg(feature = "csharp")]
1222 #[test]
1223 fn csharp_nom() {
1224 check_metrics::<CsharpParser>(
1225 "class A {
1226 public void Foo() {
1227 return;
1228 }
1229 public void Bar() {
1230 return;
1231 }
1232 public int X { get; set; }
1233 public void Outer() {
1234 void Local() { return; }
1235 Local();
1236 }
1237 }",
1238 "foo.cs",
1239 |metric| {
1240 insta::assert_json_snapshot!(
1245 metric.nom,
1246 @r#"
1247 {
1248 "functions": 6,
1249 "closures": 0,
1250 "functions_average": 0.75,
1251 "closures_average": 0.0,
1252 "total": 6,
1253 "average": 0.75,
1254 "functions_min": 0,
1255 "functions_max": 1,
1256 "closures_min": 0,
1257 "closures_max": 0
1258 }
1259 "#
1260 );
1261 },
1262 );
1263 }
1264
1265 #[cfg(feature = "csharp")]
1266 #[test]
1267 fn csharp_closure_nom() {
1268 check_metrics::<CsharpParser>(
1269 "class A {
1270 public void Run() {
1271 System.Func<int, int> f = x => x + 1;
1272 System.Action g = delegate(int y) { System.Console.WriteLine(y); };
1273 }
1274 }",
1275 "foo.cs",
1276 |metric| {
1277 insta::assert_json_snapshot!(
1279 metric.nom,
1280 @r#"
1281 {
1282 "functions": 1,
1283 "closures": 2,
1284 "functions_average": 0.2,
1285 "closures_average": 0.4,
1286 "total": 3,
1287 "average": 0.6,
1288 "functions_min": 0,
1289 "functions_max": 1,
1290 "closures_min": 0,
1291 "closures_max": 1
1292 }
1293 "#
1294 );
1295 },
1296 );
1297 }
1298
1299 #[cfg(feature = "csharp")]
1300 #[test]
1301 fn csharp_indexer_nom() {
1302 check_nom_and_npm::<CsharpParser>(
1308 "class A {
1309 private int[] _d;
1310 public int this[int i] { get => _d[i]; set => _d[i] = value; }
1311 }",
1312 "foo.cs",
1313 |metric| {
1314 assert_eq!(metric.nom.functions_sum(), 2);
1317 assert_eq!(metric.npm.class_nm_sum(), 2);
1318 insta::assert_json_snapshot!(
1319 metric.nom,
1320 @r#"
1321 {
1322 "functions": 2,
1323 "closures": 0,
1324 "functions_average": 0.5,
1325 "closures_average": 0.0,
1326 "total": 2,
1327 "average": 0.5,
1328 "functions_min": 0,
1329 "functions_max": 1,
1330 "closures_min": 0,
1331 "closures_max": 0
1332 }
1333 "#
1334 );
1335 },
1336 );
1337 }
1338
1339 #[cfg(feature = "csharp")]
1340 #[test]
1341 fn csharp_expression_bodied_indexer_nom() {
1342 check_nom_and_npm::<CsharpParser>(
1349 "class A {
1350 private int[] _d;
1351 public int this[int i] => _d[i];
1352 }",
1353 "foo.cs",
1354 |metric| {
1355 assert_eq!(metric.nom.functions_sum(), 1);
1357 assert_eq!(metric.npm.class_nm_sum(), 1);
1358 insta::assert_json_snapshot!(
1359 metric.nom,
1360 @r#"
1361 {
1362 "functions": 1,
1363 "closures": 0,
1364 "functions_average": 0.3333333333333333,
1365 "closures_average": 0.0,
1366 "total": 1,
1367 "average": 0.3333333333333333,
1368 "functions_min": 0,
1369 "functions_max": 1,
1370 "closures_min": 0,
1371 "closures_max": 0
1372 }
1373 "#
1374 );
1375 },
1376 );
1377 }
1378
1379 #[cfg(feature = "csharp")]
1380 #[test]
1381 fn csharp_property_nom() {
1382 check_nom_and_npm::<CsharpParser>(
1388 "class A {
1389 private int _w;
1390 public int W { get => _w; set => _w = value; }
1391 }",
1392 "foo.cs",
1393 |metric| {
1394 assert_eq!(metric.nom.functions_sum(), 2);
1397 assert_eq!(metric.npm.class_nm_sum(), 2);
1398 },
1399 );
1400 }
1401
1402 #[cfg(feature = "csharp")]
1403 #[test]
1404 fn csharp_auto_property_nom() {
1405 check_nom_and_npm::<CsharpParser>(
1409 "class A {
1410 public int Y { get; set; }
1411 }",
1412 "foo.cs",
1413 |metric| {
1414 assert_eq!(metric.nom.functions_sum(), 2);
1416 assert_eq!(metric.npm.class_nm_sum(), 2);
1417 },
1418 );
1419 }
1420
1421 #[cfg(feature = "csharp")]
1422 #[test]
1423 fn csharp_expression_bodied_property_nom() {
1424 check_nom_and_npm::<CsharpParser>(
1432 "class A {
1433 private int _w;
1434 public int W => _w;
1435 }",
1436 "foo.cs",
1437 |metric| {
1438 assert_eq!(metric.nom.functions_sum(), 1);
1440 assert_eq!(metric.npm.class_nm_sum(), 1);
1441 },
1442 );
1443 }
1444
1445 #[cfg(feature = "go")]
1446 #[test]
1447 fn go_top_level_funcs() {
1448 check_metrics::<GoParser>(
1449 "package main
1450 func a() {}
1451 func b() {}
1452 func c() {}",
1453 "foo.go",
1454 |metric| {
1455 insta::assert_json_snapshot!(
1457 metric.nom,
1458 @r#"
1459 {
1460 "functions": 3,
1461 "closures": 0,
1462 "functions_average": 0.75,
1463 "closures_average": 0.0,
1464 "total": 3,
1465 "average": 0.75,
1466 "functions_min": 0,
1467 "functions_max": 1,
1468 "closures_min": 0,
1469 "closures_max": 0
1470 }
1471 "#
1472 );
1473 },
1474 );
1475 }
1476
1477 #[cfg(feature = "go")]
1478 #[test]
1479 fn go_method_declaration() {
1480 check_metrics::<GoParser>(
1481 "package main
1482 type T struct{}
1483 func (r *T) M() {}",
1484 "foo.go",
1485 |metric| {
1486 insta::assert_json_snapshot!(
1488 metric.nom,
1489 @r#"
1490 {
1491 "functions": 1,
1492 "closures": 0,
1493 "functions_average": 0.5,
1494 "closures_average": 0.0,
1495 "total": 1,
1496 "average": 0.5,
1497 "functions_min": 0,
1498 "functions_max": 1,
1499 "closures_min": 0,
1500 "closures_max": 0
1501 }
1502 "#
1503 );
1504 },
1505 );
1506 }
1507
1508 #[cfg(feature = "go")]
1509 #[test]
1510 fn go_func_literal_is_closure() {
1511 check_metrics::<GoParser>(
1512 "package main
1513 var f = func() {}",
1514 "foo.go",
1515 |metric| {
1516 insta::assert_json_snapshot!(
1518 metric.nom,
1519 @r#"
1520 {
1521 "functions": 0,
1522 "closures": 1,
1523 "functions_average": 0.0,
1524 "closures_average": 0.5,
1525 "total": 1,
1526 "average": 0.5,
1527 "functions_min": 0,
1528 "functions_max": 0,
1529 "closures_min": 0,
1530 "closures_max": 1
1531 }
1532 "#
1533 );
1534 },
1535 );
1536 }
1537
1538 #[cfg(feature = "go")]
1539 #[test]
1540 fn go_nested_closures() {
1541 check_metrics::<GoParser>(
1542 "package main
1543 func f() {
1544 inner := func() {
1545 deeper := func() {}
1546 _ = deeper
1547 }
1548 _ = inner
1549 }",
1550 "foo.go",
1551 |metric| {
1552 insta::assert_json_snapshot!(
1554 metric.nom,
1555 @r#"
1556 {
1557 "functions": 1,
1558 "closures": 2,
1559 "functions_average": 0.25,
1560 "closures_average": 0.5,
1561 "total": 3,
1562 "average": 0.75,
1563 "functions_min": 0,
1564 "functions_max": 1,
1565 "closures_min": 0,
1566 "closures_max": 1
1567 }
1568 "#
1569 );
1570 },
1571 );
1572 }
1573
1574 #[cfg(feature = "java")]
1583 #[test]
1584 fn java_record_compact_constructor_counts_as_a_function() {
1585 check_metrics::<JavaParser>(
1586 "record R(int a, int b) {
1587 R { if (a < 0) { throw new IllegalArgumentException(); } }
1588 R(int a) { this(a, 0); }
1589 int sum() { return a + b; }
1590 }",
1591 "R.java",
1592 |metric| {
1593 assert_eq!(metric.nom.functions_sum(), 3);
1596 assert_eq!(metric.nom.closures_sum(), 0);
1597 },
1598 );
1599 }
1600
1601 #[cfg(feature = "java")]
1602 #[test]
1603 fn java_closure_nom() {
1604 check_metrics::<JavaParser>(
1605 "interface printable{
1606 void print();
1607 }
1608
1609 interface IntFunc {
1610 int func(int n);
1611 }
1612
1613 class Printer implements printable{
1614 public void print(){System.out.println(\"Hello\");}
1615
1616 public static void main(String args[]){
1617 Printer obj = new Printer();
1618 obj.print();
1619 IntFunc meaning = (i) -> i + 42;
1620 int i = meaning.func(1);
1621 }
1622 }",
1623 "foo.java",
1624 |metric| {
1625 insta::assert_json_snapshot!(
1627 metric.nom,
1628 @r#"
1629 {
1630 "functions": 4,
1631 "closures": 1,
1632 "functions_average": 0.5,
1633 "closures_average": 0.125,
1634 "total": 5,
1635 "average": 0.625,
1636 "functions_min": 0,
1637 "functions_max": 1,
1638 "closures_min": 0,
1639 "closures_max": 1
1640 }
1641 "#
1642 );
1643 },
1644 );
1645 }
1646
1647 #[cfg(feature = "groovy")]
1648 #[test]
1649 fn groovy_nom() {
1650 check_metrics::<GroovyParser>(
1651 "class Printer {
1652 void print() {
1653 println 'hello'
1654 }
1655 static void main(String[] args) {
1656 def p = new Printer()
1657 p.print()
1658 def doubler = { x -> x * 2 }
1659 int r = doubler(21)
1660 }
1661 }",
1662 "foo.groovy",
1663 |metric| {
1664 assert_eq!(metric.nom.functions_sum(), 2);
1671 assert_eq!(metric.nom.closures_sum(), 1);
1672 },
1673 );
1674 }
1675
1676 #[cfg(feature = "groovy")]
1677 #[test]
1678 fn groovy_nom_function_definition() {
1679 check_metrics::<GroovyParser>(
1682 "def greet(name) {
1683 println(name)
1684 }
1685 greet('world')",
1686 "foo.groovy",
1687 |metric| {
1688 assert_eq!(metric.nom.functions_sum(), 1);
1689 },
1690 );
1691 }
1692
1693 #[cfg(feature = "perl")]
1694 #[test]
1695 fn perl_nom() {
1696 check_metrics::<PerlParser>(
1697 "sub a { 1 }
1698 sub b { 2 }
1699 my $c = sub { 3 };
1700 sub outer {
1701 my $inner = sub { 4 };
1702 return $inner;
1703 }",
1704 "foo.pl",
1705 |metric| {
1706 insta::assert_json_snapshot!(
1707 metric.nom,
1708 @r#"
1709 {
1710 "functions": 3,
1711 "closures": 2,
1712 "functions_average": 0.5,
1713 "closures_average": 0.3333333333333333,
1714 "total": 5,
1715 "average": 0.8333333333333334,
1716 "functions_min": 0,
1717 "functions_max": 1,
1718 "closures_min": 0,
1719 "closures_max": 1
1720 }
1721 "#
1722 );
1723 },
1724 );
1725 }
1726
1727 #[cfg(feature = "typescript")]
1728 #[test]
1729 fn tsx_named_and_arrow_functions() {
1730 check_metrics::<TsxParser>(
1731 "function greet(name: string): string {
1732 return `Hello, ${name}`;
1733 }
1734 const add = (a: number, b: number) => a + b;
1735 const log = () => { console.log('done'); };",
1736 "foo.tsx",
1737 |metric| {
1738 insta::assert_json_snapshot!(
1739 metric.nom,
1740 @r#"
1741 {
1742 "functions": 3,
1743 "closures": 0,
1744 "functions_average": 0.75,
1745 "closures_average": 0.0,
1746 "total": 3,
1747 "average": 0.75,
1748 "functions_min": 0,
1749 "functions_max": 1,
1750 "closures_min": 0,
1751 "closures_max": 0
1752 }
1753 "#
1754 );
1755 },
1756 );
1757 }
1758
1759 #[cfg(feature = "typescript")]
1760 #[test]
1761 fn typescript_named_arrow_and_class_methods() {
1762 check_metrics::<TypescriptParser>(
1763 "function compute(x: number): number {
1764 return x * 2;
1765 }
1766 const double = (n: number): number => n * 2;
1767 class Calculator {
1768 add(a: number, b: number): number {
1769 return a + b;
1770 }
1771 }",
1772 "foo.ts",
1773 |metric| {
1774 insta::assert_json_snapshot!(
1775 metric.nom,
1776 @r#"
1777 {
1778 "functions": 3,
1779 "closures": 0,
1780 "functions_average": 0.6,
1781 "closures_average": 0.0,
1782 "total": 3,
1783 "average": 0.6,
1784 "functions_min": 0,
1785 "functions_max": 1,
1786 "closures_min": 0,
1787 "closures_max": 0
1788 }
1789 "#
1790 );
1791 },
1792 );
1793 }
1794
1795 #[cfg(feature = "mozjs")]
1796 #[test]
1797 fn mozjs_nom() {
1798 check_metrics::<MozjsParser>(
1799 "function f(a, b) {
1800 function foo(a) {
1801 return a;
1802 }
1803 var bar = (function () {
1804 var counter = 0;
1805 return function () {
1806 counter += 1;
1807 return counter
1808 }
1809 })();
1810 return bar(foo(a), a);
1811 }",
1812 "foo.js",
1813 |metric| {
1814 insta::assert_json_snapshot!(
1815 metric.nom,
1816 @r#"
1817 {
1818 "functions": 2,
1819 "closures": 2,
1820 "functions_average": 0.4,
1821 "closures_average": 0.4,
1822 "total": 4,
1823 "average": 0.8,
1824 "functions_min": 0,
1825 "functions_max": 1,
1826 "closures_min": 0,
1827 "closures_max": 1
1828 }
1829 "#
1830 );
1831 },
1832 );
1833 }
1834
1835 #[cfg(feature = "mozjs")]
1836 #[test]
1837 fn mozjs_arrow_and_method() {
1838 check_metrics::<MozjsParser>(
1839 "let add = (a, b) => a + b;
1840 class Counter {
1841 increment() {
1842 this.count++;
1843 }
1844 }",
1845 "foo.js",
1846 |metric| {
1847 insta::assert_json_snapshot!(
1848 metric.nom,
1849 @r#"
1850 {
1851 "functions": 2,
1852 "closures": 0,
1853 "functions_average": 0.5,
1854 "closures_average": 0.0,
1855 "total": 2,
1856 "average": 0.5,
1857 "functions_min": 0,
1858 "functions_max": 1,
1859 "closures_min": 0,
1860 "closures_max": 0
1861 }
1862 "#
1863 );
1864 },
1865 );
1866 }
1867
1868 #[cfg(feature = "kotlin")]
1869 #[test]
1870 fn kotlin_nom_class_with_methods() {
1871 check_metrics::<KotlinParser>(
1872 "class Calculator {
1873 fun add(a: Int, b: Int): Int {
1874 return a + b
1875 }
1876 fun subtract(a: Int, b: Int): Int {
1877 return a - b
1878 }
1879 }",
1880 "foo.kt",
1881 |metric| {
1882 insta::assert_json_snapshot!(
1883 metric.nom,
1884 @r#"
1885 {
1886 "functions": 2,
1887 "closures": 0,
1888 "functions_average": 0.5,
1889 "closures_average": 0.0,
1890 "total": 2,
1891 "average": 0.5,
1892 "functions_min": 0,
1893 "functions_max": 1,
1894 "closures_min": 0,
1895 "closures_max": 0
1896 }
1897 "#
1898 );
1899 },
1900 );
1901 }
1902
1903 #[cfg(feature = "lua")]
1904 #[test]
1905 fn lua_nom() {
1906 check_metrics::<LuaParser>(
1907 "function greet(name)
1908 return \"hello \" .. name
1909end
1910
1911local add = function(a, b)
1912 return a + b
1913end
1914
1915local function outer()
1916 local inner = function()
1917 return 42
1918 end
1919 return inner()
1920end",
1921 "foo.lua",
1922 |metric| {
1923 insta::assert_json_snapshot!(metric.nom, @r#"
1925 {
1926 "functions": 2,
1927 "closures": 2,
1928 "functions_average": 0.4,
1929 "closures_average": 0.4,
1930 "total": 4,
1931 "average": 0.8,
1932 "functions_min": 0,
1933 "functions_max": 1,
1934 "closures_min": 0,
1935 "closures_max": 1
1936 }
1937 "#);
1938 },
1939 );
1940 }
1941
1942 #[cfg(feature = "bash")]
1943 #[test]
1944 fn bash_nom() {
1945 check_metrics::<BashParser>(
1946 "#!/bin/bash
1947foo() {
1948 echo 'hello'
1949}
1950bar() {
1951 echo 'world'
1952}
1953foo
1954bar",
1955 "foo.sh",
1956 |metric| {
1957 insta::assert_json_snapshot!(
1958 metric.nom,
1959 @r#"
1960 {
1961 "functions": 2,
1962 "closures": 0,
1963 "functions_average": 0.6666666666666666,
1964 "closures_average": 0.0,
1965 "total": 2,
1966 "average": 0.6666666666666666,
1967 "functions_min": 0,
1968 "functions_max": 1,
1969 "closures_min": 0,
1970 "closures_max": 0
1971 }
1972 "#
1973 );
1974 },
1975 );
1976 }
1977
1978 #[cfg(feature = "tcl")]
1979 #[test]
1980 fn tcl_nom() {
1981 check_metrics::<TclParser>(
1982 "proc foo {a} { puts $a }
1983proc bar {x y} { puts $x }
1984foo 1
1985bar 2 3",
1986 "foo.tcl",
1987 |metric| {
1988 assert_eq!(metric.nom.functions_sum(), 2);
1989 assert_eq!(metric.nom.closures_sum(), 0);
1990 insta::assert_json_snapshot!(metric.nom);
1991 },
1992 );
1993 }
1994
1995 #[cfg(feature = "tcl")]
1996 #[test]
1997 fn tcl_nested_nom() {
1998 check_metrics::<TclParser>(
1999 "proc outer {a} {
2000 proc inner {x} { puts $x }
2001 inner $a
2002}",
2003 "foo.tcl",
2004 |metric| {
2005 assert_eq!(metric.nom.functions_sum(), 2);
2006 assert_eq!(metric.nom.closures_sum(), 0);
2007 insta::assert_json_snapshot!(metric.nom);
2008 },
2009 );
2010 }
2011
2012 #[cfg(feature = "typescript")]
2013 #[test]
2014 fn typescript_class_methods() {
2015 check_metrics::<TypescriptParser>(
2016 "class Calc {
2017 add(a: number, b: number): number { return a + b; }
2018 sub(a: number, b: number): number { return a - b; }
2019 }",
2020 "foo.ts",
2021 |metric| {
2022 assert_eq!(metric.nom.functions_sum(), 2);
2023 assert_eq!(metric.nom.closures_sum(), 0);
2024 insta::assert_json_snapshot!(metric.nom);
2025 },
2026 );
2027 }
2028
2029 #[cfg(feature = "typescript")]
2030 #[test]
2031 fn typescript_arrow_and_function() {
2032 check_metrics::<TypescriptParser>(
2033 "function f(): number { return 1; }
2034 const g = (): number => 2;
2035 const h = (x: number): number => x * 2;",
2036 "foo.ts",
2037 |metric| {
2038 assert_eq!(metric.nom.functions_sum(), 3);
2039 assert_eq!(metric.nom.closures_sum(), 0);
2040 insta::assert_json_snapshot!(metric.nom);
2041 },
2042 );
2043 }
2044
2045 #[cfg(feature = "typescript")]
2046 #[test]
2047 fn tsx_class_methods() {
2048 check_metrics::<TsxParser>(
2049 "class Calc {
2050 add(a: number, b: number): number { return a + b; }
2051 sub(a: number, b: number): number { return a - b; }
2052 }",
2053 "foo.tsx",
2054 |metric| {
2055 assert_eq!(metric.nom.functions_sum(), 2);
2056 assert_eq!(metric.nom.closures_sum(), 0);
2057 insta::assert_json_snapshot!(metric.nom);
2058 },
2059 );
2060 }
2061
2062 #[cfg(feature = "typescript")]
2063 #[test]
2064 fn tsx_arrow_and_function() {
2065 check_metrics::<TsxParser>(
2066 "function f(): number { return 1; }
2067 const g = (): number => 2;
2068 const h = (x: number): number => x * 2;",
2069 "foo.tsx",
2070 |metric| {
2071 assert_eq!(metric.nom.functions_sum(), 3);
2072 assert_eq!(metric.nom.closures_sum(), 0);
2073 insta::assert_json_snapshot!(metric.nom);
2074 },
2075 );
2076 }
2077
2078 #[cfg(feature = "bash")]
2079 #[test]
2080 fn bash_multiple_functions_nom() {
2081 check_metrics::<BashParser>(
2082 "#!/bin/bash
2083f() {
2084 echo hello
2085}
2086g() {
2087 echo world
2088}",
2089 "foo.sh",
2090 |metric| {
2091 assert_eq!(metric.nom.functions_sum(), 2);
2092 assert_eq!(metric.nom.closures_sum(), 0);
2093 insta::assert_json_snapshot!(metric.nom);
2094 },
2095 );
2096 }
2097
2098 #[cfg(feature = "bash")]
2099 #[test]
2100 fn bash_nested_functions_nom() {
2101 check_metrics::<BashParser>(
2102 "#!/bin/bash
2103outer() {
2104 inner() {
2105 echo inner
2106 }
2107 inner
2108}",
2109 "foo.sh",
2110 |metric| {
2111 assert_eq!(metric.nom.functions_sum(), 2);
2112 assert_eq!(metric.nom.closures_sum(), 0);
2113 insta::assert_json_snapshot!(metric.nom);
2114 },
2115 );
2116 }
2117
2118 #[cfg(feature = "mozjs")]
2119 #[test]
2120 fn mozjs_nested_function_nom() {
2121 check_metrics::<MozjsParser>(
2122 "function outer() {
2123 function inner() {
2124 return 1;
2125 }
2126 return inner();
2127 }",
2128 "foo.js",
2129 |metric| {
2130 assert_eq!(metric.nom.functions_sum(), 2);
2131 assert_eq!(metric.nom.closures_sum(), 0);
2132 insta::assert_json_snapshot!(metric.nom);
2133 },
2134 );
2135 }
2136
2137 #[cfg(feature = "mozjs")]
2138 #[test]
2139 fn mozjs_class_methods_nom() {
2140 check_metrics::<MozjsParser>(
2141 "class Calc {
2142 add(a, b) { return a + b; }
2143 sub(a, b) { return a - b; }
2144 }",
2145 "foo.js",
2146 |metric| {
2147 assert_eq!(metric.nom.functions_sum(), 2);
2148 assert_eq!(metric.nom.closures_sum(), 0);
2149 insta::assert_json_snapshot!(metric.nom);
2150 },
2151 );
2152 }
2153
2154 #[cfg(feature = "mozjs")]
2155 #[test]
2156 fn mozjs_iife_nom() {
2157 check_metrics::<MozjsParser>(
2158 "(function() {
2159 var x = 1;
2160 return x;
2161 })();",
2162 "foo.js",
2163 |metric| {
2164 assert_eq!(metric.nom.functions_sum(), 0);
2165 assert_eq!(metric.nom.closures_sum(), 1);
2166 insta::assert_json_snapshot!(metric.nom);
2167 },
2168 );
2169 }
2170
2171 #[cfg(feature = "kotlin")]
2172 #[test]
2173 fn kotlin_class_methods_nom() {
2174 check_metrics::<KotlinParser>(
2175 "class Calc {
2176 fun add(a: Int, b: Int): Int = a + b
2177 fun sub(a: Int, b: Int): Int = a - b
2178 }",
2179 "foo.kt",
2180 |metric| {
2181 assert_eq!(metric.nom.functions_sum(), 2);
2182 assert_eq!(metric.nom.closures_sum(), 0);
2183 insta::assert_json_snapshot!(metric.nom);
2184 },
2185 );
2186 }
2187
2188 #[cfg(feature = "kotlin")]
2189 #[test]
2190 fn kotlin_lambda_nom() {
2191 check_metrics::<KotlinParser>(
2192 "fun f(list: List<Int>): Int {
2193 val double = { x: Int -> x * 2 }
2194 return list.sumOf(double)
2195 }",
2196 "foo.kt",
2197 |metric| {
2198 assert_eq!(metric.nom.functions_sum(), 1);
2199 assert_eq!(metric.nom.closures_sum(), 1);
2200 insta::assert_json_snapshot!(metric.nom);
2201 },
2202 );
2203 }
2204
2205 #[cfg(feature = "php")]
2206 #[test]
2207 fn php_nom() {
2208 check_metrics::<PhpParser>(
2211 "<?php
2212 function top(): void {}
2213 class A {
2214 public function m1(): void {}
2215 public function m2(): int {
2216 $f = function () { return 1; };
2217 $g = fn () => 2;
2218 return $f() + $g();
2219 }
2220 }",
2221 "foo.php",
2222 |metric| {
2223 insta::assert_json_snapshot!(
2224 metric.nom,
2225 @r#"
2226 {
2227 "functions": 3,
2228 "closures": 2,
2229 "functions_average": 0.42857142857142855,
2230 "closures_average": 0.2857142857142857,
2231 "total": 5,
2232 "average": 0.7142857142857143,
2233 "functions_min": 0,
2234 "functions_max": 1,
2235 "closures_min": 0,
2236 "closures_max": 1
2237 }
2238 "#
2239 );
2240 },
2241 );
2242 }
2243
2244 #[cfg(feature = "php")]
2245 #[test]
2246 fn php_nom_anonymous_class() {
2247 check_metrics::<PhpParser>(
2251 "<?php
2252 function f(): object {
2253 return new class {
2254 public function inner(): int { return 1; }
2255 };
2256 }",
2257 "foo.php",
2258 |metric| {
2259 insta::assert_json_snapshot!(
2260 metric.nom,
2261 @r#"
2262 {
2263 "functions": 2,
2264 "closures": 0,
2265 "functions_average": 0.5,
2266 "closures_average": 0.0,
2267 "total": 2,
2268 "average": 0.5,
2269 "functions_min": 0,
2270 "functions_max": 1,
2271 "closures_min": 0,
2272 "closures_max": 0
2273 }
2274 "#
2275 );
2276 },
2277 );
2278 }
2279
2280 #[cfg(feature = "php")]
2281 #[test]
2282 fn php_function_after_leading_backslash_group_use_is_counted() {
2283 check_metrics::<PhpParser>(
2287 "<?php\nuse \\Foo\\{Bar, Baz};\nfunction u() { return 1; }\n",
2288 "foo.php",
2289 |metric| {
2290 assert_eq!(metric.nom.functions_sum(), 1);
2291 },
2292 );
2293 }
2294
2295 #[cfg(feature = "elixir")]
2304 #[test]
2305 fn elixir_nom_counts_def_as_functions_and_fn_as_closures() {
2306 check_metrics::<ElixirParser>(
2307 "defmodule Foo do\n def public_fn(x), do: x + 1\n defp private_fn(x), do: x - 1\n def with_anon do\n inc = fn x -> x + 1 end\n dec = fn x -> x - 1 end\n {inc, dec}\n end\nend\n",
2308 "foo.ex",
2309 |metric| {
2310 assert_eq!(metric.nom.functions_sum(), 3);
2311 assert_eq!(metric.nom.closures_sum(), 2);
2312 assert_eq!(metric.nom.total(), 5);
2313 },
2314 );
2315 }
2316
2317 #[test]
2318 fn stats_display_commas_between_all_fields() {
2319 let stats = Stats {
2320 functions: 0,
2321 closures: 0,
2322 functions_sum: 3,
2323 closures_sum: 1,
2324 functions_min: 0,
2325 functions_max: 2,
2326 closures_min: 0,
2327 closures_max: 1,
2328 space_count: 2,
2329 };
2330 let formatted = format!("{stats}");
2331
2332 let expected_fragments = [
2335 "functions: 3, closures: 1",
2336 "closures: 1, functions_average:",
2337 "functions_average: 1.5, closures_average:",
2338 "closures_average: 0.5, total:",
2339 "total: 4, average:",
2340 "average: 2, functions_min:",
2341 "functions_min: 0, functions_max:",
2342 "functions_max: 2, closures_min:",
2343 "closures_min: 0, closures_max:",
2344 ];
2345
2346 for fragment in expected_fragments {
2347 assert!(
2348 formatted.contains(fragment),
2349 "missing fragment {fragment:?} in: {formatted}"
2350 );
2351 }
2352 }
2353
2354 #[cfg(feature = "ruby")]
2355 #[test]
2356 fn ruby_nom() {
2357 check_metrics::<RubyParser>(
2363 "class C\n def add(a, b)\n a + b\n end\n def mul(a, b)\n a * b\n end\n def self.factory\n new\n end\nend\n\n[1, 2, 3].each { |x| puts x }\n",
2364 "foo.rb",
2365 |metric| {
2366 assert_eq!(metric.nom.functions_sum(), 3);
2367 assert_eq!(metric.nom.closures_sum(), 1);
2368 assert_eq!(metric.nom.total(), 4);
2369 },
2370 );
2371 }
2372
2373 #[cfg(feature = "ruby")]
2374 #[test]
2375 fn ruby_stabby_lambda_single_closure() {
2376 check_metrics::<RubyParser>("f = ->(z) { z + 1 }\n", "stabby.rb", |metric| {
2381 assert_eq!(metric.nom.functions_sum(), 0);
2382 assert_eq!(metric.nom.closures_sum(), 1);
2383 });
2384 }
2385
2386 #[cfg(feature = "ruby")]
2387 #[test]
2388 fn ruby_stabby_lambda_multi_statement_single_closure() {
2389 check_metrics::<RubyParser>(
2392 "f = ->(z) {\n y = z + 1\n y * 2\n}\n",
2393 "stabby_multi.rb",
2394 |metric| {
2395 assert_eq!(metric.nom.closures_sum(), 1);
2396 },
2397 );
2398 }
2399
2400 #[cfg(feature = "ruby")]
2401 #[test]
2402 fn ruby_stabby_lambda_do_block_single_closure() {
2403 check_metrics::<RubyParser>("f = ->(z) do\n z + 1\nend\n", "stabby_do.rb", |metric| {
2406 assert_eq!(metric.nom.closures_sum(), 1);
2407 });
2408 }
2409
2410 #[cfg(feature = "ruby")]
2411 #[test]
2412 fn ruby_keyword_lambda_single_closure() {
2413 check_metrics::<RubyParser>(
2418 "g = lambda { |z| z + 1 }\nh = proc { |z| z + 1 }\n",
2419 "keyword.rb",
2420 |metric| {
2421 assert_eq!(metric.nom.closures_sum(), 2);
2422 },
2423 );
2424 }
2425
2426 #[cfg(feature = "irules")]
2432 #[test]
2433 fn irules_nom_handlers_and_procs() {
2434 check_metrics::<IrulesParser>(
2435 "when CLIENT_ACCEPTED {
2436 log local0. \"connected\"
2437}
2438when HTTP_REQUEST {
2439 log local0. [HTTP::uri]
2440}
2441proc helper { x } {
2442 return $x
2443}
2444",
2445 "foo.irule",
2446 |metric| {
2447 assert_eq!(metric.nom.functions_sum(), 3);
2448 assert_eq!(metric.nom.closures_sum(), 0);
2449 assert_eq!(metric.nom.total(), 3);
2450 },
2451 );
2452 }
2453
2454 #[cfg(feature = "irules")]
2460 #[test]
2461 fn irules_try_handlers_are_not_functions() {
2462 check_metrics::<IrulesParser>(
2463 "proc f {} {
2464 try {
2465 risky
2466 } on error {msg} {
2467 puts $msg
2468 } trap {POSIX} {msg} {
2469 puts $msg
2470 }
2471}
2472",
2473 "foo.irule",
2474 |metric| {
2475 assert_eq!(metric.nom.functions_sum(), 1);
2476 assert_eq!(metric.nom.closures_sum(), 0);
2477 assert_eq!(metric.nom.total(), 1);
2478 },
2479 );
2480 }
2481
2482 #[cfg(feature = "objc")]
2487 #[test]
2488 fn objc_nom() {
2489 check_metrics::<ObjcParser>(
2490 "@implementation Foo
2491- (void)one {
2492 void (^blk)(void) = ^{
2493 [self two];
2494 };
2495 blk();
2496}
2497- (void)two {
2498 [self one];
2499}
2500@end
2501",
2502 "foo.m",
2503 |metric| {
2504 assert_eq!(metric.nom.functions_sum(), 2);
2505 assert_eq!(metric.nom.closures_sum(), 1);
2506 assert_eq!(metric.nom.total(), 3);
2507 insta::assert_json_snapshot!(metric.nom, @r#"
2508 {
2509 "functions": 2,
2510 "closures": 1,
2511 "functions_average": 0.5,
2512 "closures_average": 0.25,
2513 "total": 3,
2514 "average": 0.75,
2515 "functions_min": 0,
2516 "functions_max": 1,
2517 "closures_min": 0,
2518 "closures_max": 1
2519 }
2520 "#);
2521 },
2522 );
2523 }
2524}