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 #[test]
314 fn python_nom() {
315 check_metrics::<PythonParser>(
316 "def a():
317 pass
318 def b():
319 pass
320 def c():
321 pass
322 x = lambda a : a + 42",
323 "foo.py",
324 |metric| {
325 assert_eq!(metric.nom.closures_sum(), 1);
331 insta::assert_json_snapshot!(
332 metric.nom,
333 @r#"
334 {
335 "functions": 3,
336 "closures": 1,
337 "functions_average": 0.75,
338 "closures_average": 0.25,
339 "total": 4,
340 "average": 1.0,
341 "functions_min": 0,
342 "functions_max": 1,
343 "closures_min": 0,
344 "closures_max": 1
345 }
346 "#
347 );
348 },
349 );
350 }
351
352 #[test]
353 fn rust_nom() {
354 check_metrics::<RustParser>(
355 "mod A { fn foo() {}}
356 mod B { fn foo() {}}
357 let closure = |i: i32| -> i32 { i + 42 };",
358 "foo.rs",
359 |metric| {
360 insta::assert_json_snapshot!(
362 metric.nom,
363 @r#"
364 {
365 "functions": 2,
366 "closures": 1,
367 "functions_average": 0.5,
368 "closures_average": 0.25,
369 "total": 3,
370 "average": 0.75,
371 "functions_min": 0,
372 "functions_max": 1,
373 "closures_min": 0,
374 "closures_max": 1
375 }
376 "#
377 );
378 },
379 );
380 }
381
382 #[test]
383 fn c_nom() {
384 check_metrics::<CParser>(
385 "int foo();
386
387 int foo() {
388 return 0;
389 }",
390 "foo.c",
391 |metric| {
392 insta::assert_json_snapshot!(
394 metric.nom,
395 @r#"
396 {
397 "functions": 1,
398 "closures": 0,
399 "functions_average": 0.5,
400 "closures_average": 0.0,
401 "total": 1,
402 "average": 0.5,
403 "functions_min": 0,
404 "functions_max": 1,
405 "closures_min": 0,
406 "closures_max": 0
407 }
408 "#
409 );
410 },
411 );
412 }
413
414 #[test]
415 fn cpp_nom() {
416 check_metrics::<CppParser>(
417 "struct A {
418 void foo(int) {}
419 void foo(double) {}
420 };
421 int b = [](int x) -> int { return x + 42; };",
422 "foo.cpp",
423 |metric| {
424 insta::assert_json_snapshot!(
426 metric.nom,
427 @r#"
428 {
429 "functions": 2,
430 "closures": 1,
431 "functions_average": 0.5,
432 "closures_average": 0.25,
433 "total": 3,
434 "average": 0.75,
435 "functions_min": 0,
436 "functions_max": 1,
437 "closures_min": 0,
438 "closures_max": 1
439 }
440 "#
441 );
442 },
443 );
444 }
445
446 #[test]
451 fn cpp_free_and_member_functions() {
452 check_metrics::<CppParser>(
453 "int free_fn(int x) { return x; }
454 struct S {
455 int member_fn(int x) { return x + 1; }
456 };",
457 "foo.cpp",
458 |metric| {
459 let s = &metric.nom;
461 assert_eq!(s.functions_sum(), 2);
462 assert_eq!(s.closures_sum(), 0);
463 assert_eq!(s.total(), 2);
464 insta::assert_json_snapshot!(metric.nom);
465 },
466 );
467 }
468
469 #[test]
473 fn cpp_static_member_function() {
474 check_metrics::<CppParser>(
475 "struct S {
476 static int factory(int x) { return x; }
477 int method(int x) { return x + 1; }
478 };",
479 "foo.cpp",
480 |metric| {
481 let s = &metric.nom;
482 assert_eq!(s.functions_sum(), 2);
483 assert_eq!(s.closures_sum(), 0);
484 insta::assert_json_snapshot!(metric.nom);
485 },
486 );
487 }
488
489 #[test]
494 fn cpp_constructor_and_destructor() {
495 check_metrics::<CppParser>(
496 "struct S {
497 S() {}
498 ~S() {}
499 int method() { return 0; }
500 };",
501 "foo.cpp",
502 |metric| {
503 let s = &metric.nom;
504 assert_eq!(s.functions_sum(), 3);
506 assert_eq!(s.closures_sum(), 0);
507 insta::assert_json_snapshot!(metric.nom);
508 },
509 );
510 }
511
512 #[test]
516 fn cpp_operator_overloads() {
517 check_metrics::<CppParser>(
518 "struct V {
519 int x;
520 V operator+(const V& o) const { return V{x + o.x}; }
521 bool operator==(const V& o) const { return x == o.x; }
522 };",
523 "foo.cpp",
524 |metric| {
525 let s = &metric.nom;
526 assert_eq!(s.functions_sum(), 2);
527 assert_eq!(s.closures_sum(), 0);
528 insta::assert_json_snapshot!(metric.nom);
529 },
530 );
531 }
532
533 #[test]
537 fn cpp_function_template() {
538 check_metrics::<CppParser>(
539 "template<typename T>
540 T identity(T x) { return x; }",
541 "foo.cpp",
542 |metric| {
543 let s = &metric.nom;
544 assert_eq!(s.functions_sum(), 1);
545 assert_eq!(s.closures_sum(), 0);
546 insta::assert_json_snapshot!(metric.nom);
547 },
548 );
549 }
550
551 #[test]
555 fn cpp_class_template_members() {
556 check_metrics::<CppParser>(
557 "template<typename T>
558 struct Box {
559 T value;
560 T get() const { return value; }
561 void set(T v) { value = v; }
562 };",
563 "foo.cpp",
564 |metric| {
565 let s = &metric.nom;
566 assert_eq!(s.functions_sum(), 2);
567 assert_eq!(s.closures_sum(), 0);
568 insta::assert_json_snapshot!(metric.nom);
569 },
570 );
571 }
572
573 #[test]
578 fn cpp_lambdas_inside_function() {
579 check_metrics::<CppParser>(
580 "int run() {
581 auto add = [](int a, int b) { return a + b; };
582 auto mul = [](int a, int b) { return a * b; };
583 return add(1, 2) + mul(3, 4);
584 }",
585 "foo.cpp",
586 |metric| {
587 let s = &metric.nom;
588 assert_eq!(s.functions_sum(), 1);
590 assert_eq!(s.closures_sum(), 2);
591 assert_eq!(s.total(), 3);
592 insta::assert_json_snapshot!(metric.nom);
593 },
594 );
595 }
596
597 #[test]
598 fn javascript_nom() {
599 check_metrics::<JavascriptParser>(
600 "function f(a, b) {
601 function foo(a) {
602 return a;
603 }
604 var bar = (function () {
605 var counter = 0;
606 return function () {
607 counter += 1;
608 return counter
609 }
610 })();
611 return bar(foo(a), a);
612 }",
613 "foo.js",
614 |metric| {
615 insta::assert_json_snapshot!(
626 metric.nom,
627 @r#"
628 {
629 "functions": 2,
630 "closures": 2,
631 "functions_average": 0.4,
632 "closures_average": 0.4,
633 "total": 4,
634 "average": 0.8,
635 "functions_min": 0,
636 "functions_max": 1,
637 "closures_min": 0,
638 "closures_max": 1
639 }
640 "#
641 );
642 },
643 );
644 }
645
646 fn check_js_binding_site_parity(lang: crate::LANG) {
659 let split = |source: &str| {
660 let m = crate::test_support::metrics_verbatim(
661 lang,
662 source.as_bytes(),
663 crate::MetricsOptions::default(),
664 );
665 (m.nom.functions_sum(), m.nom.closures_sum())
666 };
667
668 let cases: &[(&str, &str, &str, bool)] = &[
670 (
676 "bare IIFE",
677 "(function (c) { return c; })(1);",
678 "(() => 1)(1);",
679 false,
680 ),
681 (
682 "bound IIFE",
683 "const v = (function (c) { return c; })(1);",
684 "const v = (() => 1)(1);",
685 false,
686 ),
687 (
692 "string-keyed pair",
693 "({ \"k\": function () { return 1; } });",
694 "({ \"k\": () => 1 });",
695 true,
696 ),
697 (
701 "class field initialiser",
702 "class C { p = function () { return 1; }; }",
703 "class C { p = () => 1; }",
704 true,
705 ),
706 (
715 "computed-key class field",
716 "class C { [\"k\"] = function () { return 1; }; }",
717 "class C { [\"k\"] = () => 1; }",
718 true,
719 ),
720 (
721 "string-named class field",
722 "class C { \"s\" = function () { return 1; }; }",
723 "class C { \"s\" = () => 1; }",
724 true,
725 ),
726 (
727 "private class field",
728 "class C { #p = function () { return 1; }; }",
729 "class C { #p = () => 1; }",
730 true,
731 ),
732 (
734 "positional callback",
735 "run(function () { return 1; });",
736 "run(() => 1);",
737 false,
738 ),
739 (
750 "member-expression object",
751 "run(function () { return 1; }.bind(this));",
752 "run((() => 1).bind(this));",
753 false,
754 ),
755 ];
756
757 for &(label, func_form, arrow_form, expected) in cases {
758 for (form, source) in [("function", func_form), ("arrow", arrow_form)] {
759 let (functions, closures) = split(source);
760 assert_eq!(
761 functions == 1,
762 expected,
763 "{lang:?} {label} / {form}: expected a {}, got functions {functions} closures {closures}",
764 if expected { "function" } else { "closure" },
765 );
766 }
767 }
768
769 assert_eq!(
777 split("run(function g() { return 1; });").0,
778 1,
779 "{lang:?}: a named function expression carries its own name",
780 );
781 assert_eq!(
782 split("run(x => x);").1,
783 1,
784 "{lang:?}: an arrow's identifier child is its parameter, not a name",
785 );
786 }
787
788 #[test]
789 fn javascript_binding_site_parity() {
790 check_js_binding_site_parity(crate::LANG::Javascript);
791 }
792
793 #[test]
794 fn mozjs_binding_site_parity() {
795 check_js_binding_site_parity(crate::LANG::Mozjs);
796 }
797
798 #[test]
799 fn typescript_binding_site_parity() {
800 check_js_binding_site_parity(crate::LANG::Typescript);
801 }
802
803 #[test]
804 fn tsx_binding_site_parity() {
805 check_js_binding_site_parity(crate::LANG::Tsx);
806 }
807
808 #[test]
809 fn javascript_call_nom() {
810 check_metrics::<JavascriptParser>(
811 "add_task(async function test_safe_mode() {
812 gAppInfo.inSafeMode = true;
813 });",
814 "foo.js",
815 |metric| {
816 insta::assert_json_snapshot!(
819 metric.nom,
820 @r#"
821 {
822 "functions": 1,
823 "closures": 0,
824 "functions_average": 0.5,
825 "closures_average": 0.0,
826 "total": 1,
827 "average": 0.5,
828 "functions_min": 0,
829 "functions_max": 1,
830 "closures_min": 0,
831 "closures_max": 0
832 }
833 "#
834 );
835 },
836 );
837 }
838
839 #[test]
840 fn javascript_assignment_nom() {
841 check_metrics::<JavascriptParser>(
842 "AnimationTest.prototype.enableDisplay = function(element) {};",
843 "foo.js",
844 |metric| {
845 insta::assert_json_snapshot!(
847 metric.nom,
848 @r#"
849 {
850 "functions": 1,
851 "closures": 0,
852 "functions_average": 0.5,
853 "closures_average": 0.0,
854 "total": 1,
855 "average": 0.5,
856 "functions_min": 0,
857 "functions_max": 1,
858 "closures_min": 0,
859 "closures_max": 0
860 }
861 "#
862 );
863 },
864 );
865 }
866
867 #[test]
868 fn javascript_labeled_nom() {
869 check_metrics::<JavascriptParser>(
870 "toJSON: function() {
871 return this.inspect(true);
872 }",
873 "foo.js",
874 |metric| {
875 insta::assert_json_snapshot!(
877 metric.nom,
878 @r#"
879 {
880 "functions": 1,
881 "closures": 0,
882 "functions_average": 0.5,
883 "closures_average": 0.0,
884 "total": 1,
885 "average": 0.5,
886 "functions_min": 0,
887 "functions_max": 1,
888 "closures_min": 0,
889 "closures_max": 0
890 }
891 "#
892 );
893 },
894 );
895 }
896
897 #[test]
898 fn javascript_labeled_arrow_nom() {
899 check_metrics::<JavascriptParser>(
900 "const dimConverters = {
901 pt: x => x,
902 };",
903 "foo.js",
904 |metric| {
905 insta::assert_json_snapshot!(
907 metric.nom,
908 @r#"
909 {
910 "functions": 1,
911 "closures": 0,
912 "functions_average": 0.5,
913 "closures_average": 0.0,
914 "total": 1,
915 "average": 0.5,
916 "functions_min": 0,
917 "functions_max": 1,
918 "closures_min": 0,
919 "closures_max": 0
920 }
921 "#
922 );
923 },
924 );
925 }
926
927 #[test]
928 fn javascript_pair_nom() {
929 check_metrics::<JavascriptParser>(
930 "return {
931 initialize: function(object) {
932 this._object = object.toObject();
933 },
934 }",
935 "foo.js",
936 |metric| {
937 insta::assert_json_snapshot!(
939 metric.nom,
940 @r#"
941 {
942 "functions": 1,
943 "closures": 0,
944 "functions_average": 0.5,
945 "closures_average": 0.0,
946 "total": 1,
947 "average": 0.5,
948 "functions_min": 0,
949 "functions_max": 1,
950 "closures_min": 0,
951 "closures_max": 0
952 }
953 "#
954 );
955 },
956 );
957 }
958
959 fn check_returned_object_arrow_nom<T: ParserTrait>(file_name: &str) {
960 check_metrics::<T>(
961 "function f() { return { foo: x => x }; }",
962 file_name,
963 |metric| {
964 insta::allow_duplicates! {
965 insta::assert_json_snapshot!(
966 metric.nom,
967 @r#"
968 {
969 "functions": 2,
970 "closures": 0,
971 "functions_average": 0.6666666666666666,
972 "closures_average": 0.0,
973 "total": 2,
974 "average": 0.6666666666666666,
975 "functions_min": 0,
976 "functions_max": 1,
977 "closures_min": 0,
978 "closures_max": 0
979 }
980 "#
981 );
982 }
983 },
984 );
985 }
986
987 #[test]
988 fn javascript_returned_object_arrow_nom() {
989 check_returned_object_arrow_nom::<JavascriptParser>("foo.js");
990 }
991
992 #[test]
993 fn mozjs_returned_object_arrow_nom() {
994 check_returned_object_arrow_nom::<MozjsParser>("foo.js");
995 }
996
997 #[test]
998 fn typescript_returned_object_arrow_nom() {
999 check_returned_object_arrow_nom::<TypescriptParser>("foo.ts");
1000 }
1001
1002 #[test]
1003 fn tsx_returned_object_arrow_nom() {
1004 check_returned_object_arrow_nom::<TsxParser>("foo.tsx");
1005 }
1006
1007 #[test]
1008 fn javascript_unnamed_nom() {
1009 check_metrics::<JavascriptParser>(
1010 "Ajax.getTransport = Try.these(
1011 function() {
1012 return function(){ return new XMLHttpRequest()}
1013 }
1014 );",
1015 "foo.js",
1016 |metric| {
1017 insta::assert_json_snapshot!(
1019 metric.nom,
1020 @r#"
1021 {
1022 "functions": 0,
1023 "closures": 2,
1024 "functions_average": 0.0,
1025 "closures_average": 0.6666666666666666,
1026 "total": 2,
1027 "average": 0.6666666666666666,
1028 "functions_min": 0,
1029 "functions_max": 0,
1030 "closures_min": 0,
1031 "closures_max": 1
1032 }
1033 "#
1034 );
1035 },
1036 );
1037 }
1038
1039 #[test]
1040 fn javascript_arrow_nom() {
1041 check_metrics::<JavascriptParser>(
1042 "var materials = [\"Hydrogen\"];
1043 materials.map(material => material.length);
1044 let add = (a, b) => a + b;",
1045 "foo.js",
1046 |metric| {
1047 insta::assert_json_snapshot!(
1051 metric.nom,
1052 @r#"
1053 {
1054 "functions": 1,
1055 "closures": 1,
1056 "functions_average": 0.3333333333333333,
1057 "closures_average": 0.3333333333333333,
1058 "total": 2,
1059 "average": 0.6666666666666666,
1060 "functions_min": 0,
1061 "functions_max": 1,
1062 "closures_min": 0,
1063 "closures_max": 1
1064 }
1065 "#
1066 );
1067 },
1068 );
1069 }
1070
1071 #[test]
1072 fn javascript_arrow_assignment_nom() {
1073 check_metrics::<JavascriptParser>("sink.onPull = () => { };", "foo.js", |metric| {
1074 insta::assert_json_snapshot!(
1076 metric.nom,
1077 @r#"
1078 {
1079 "functions": 1,
1080 "closures": 0,
1081 "functions_average": 0.5,
1082 "closures_average": 0.0,
1083 "total": 1,
1084 "average": 0.5,
1085 "functions_min": 0,
1086 "functions_max": 1,
1087 "closures_min": 0,
1088 "closures_max": 0
1089 }
1090 "#
1091 );
1092 });
1093 }
1094
1095 #[test]
1096 fn javascript_arrow_new_nom() {
1097 check_metrics::<JavascriptParser>(
1098 "const response = new Promise(resolve => channel.port1.onmessage = resolve);",
1099 "foo.js",
1100 |metric| {
1101 insta::assert_json_snapshot!(
1103 metric.nom,
1104 @r#"
1105 {
1106 "functions": 0,
1107 "closures": 1,
1108 "functions_average": 0.0,
1109 "closures_average": 0.5,
1110 "total": 1,
1111 "average": 0.5,
1112 "functions_min": 0,
1113 "functions_max": 0,
1114 "closures_min": 0,
1115 "closures_max": 1
1116 }
1117 "#
1118 );
1119 },
1120 );
1121 }
1122
1123 #[test]
1124 fn javascript_arrow_call_nom() {
1125 check_metrics::<JavascriptParser>(
1126 "let notDisabled = TestUtils.waitForCondition(
1127 () => !backbutton.hasAttribute(\"disabled\")
1128 );",
1129 "foo.js",
1130 |metric| {
1131 insta::assert_json_snapshot!(
1133 metric.nom,
1134 @r#"
1135 {
1136 "functions": 0,
1137 "closures": 1,
1138 "functions_average": 0.0,
1139 "closures_average": 0.5,
1140 "total": 1,
1141 "average": 0.5,
1142 "functions_min": 0,
1143 "functions_max": 0,
1144 "closures_min": 0,
1145 "closures_max": 1
1146 }
1147 "#
1148 );
1149 },
1150 );
1151 }
1152
1153 #[test]
1154 fn java_nom() {
1155 check_metrics::<JavaParser>(
1156 "class A {
1157 public void foo(){
1158 return;
1159 }
1160 public void bar(){
1161 return;
1162 }
1163 }",
1164 "foo.java",
1165 |metric| {
1166 insta::assert_json_snapshot!(
1168 metric.nom,
1169 @r#"
1170 {
1171 "functions": 2,
1172 "closures": 0,
1173 "functions_average": 0.5,
1174 "closures_average": 0.0,
1175 "total": 2,
1176 "average": 0.5,
1177 "functions_min": 0,
1178 "functions_max": 1,
1179 "closures_min": 0,
1180 "closures_max": 0
1181 }
1182 "#
1183 );
1184 },
1185 );
1186 }
1187
1188 #[test]
1189 fn csharp_nom() {
1190 check_metrics::<CsharpParser>(
1191 "class A {
1192 public void Foo() {
1193 return;
1194 }
1195 public void Bar() {
1196 return;
1197 }
1198 public int X { get; set; }
1199 public void Outer() {
1200 void Local() { return; }
1201 Local();
1202 }
1203 }",
1204 "foo.cs",
1205 |metric| {
1206 insta::assert_json_snapshot!(
1211 metric.nom,
1212 @r#"
1213 {
1214 "functions": 6,
1215 "closures": 0,
1216 "functions_average": 0.75,
1217 "closures_average": 0.0,
1218 "total": 6,
1219 "average": 0.75,
1220 "functions_min": 0,
1221 "functions_max": 1,
1222 "closures_min": 0,
1223 "closures_max": 0
1224 }
1225 "#
1226 );
1227 },
1228 );
1229 }
1230
1231 #[test]
1232 fn csharp_closure_nom() {
1233 check_metrics::<CsharpParser>(
1234 "class A {
1235 public void Run() {
1236 System.Func<int, int> f = x => x + 1;
1237 System.Action g = delegate(int y) { System.Console.WriteLine(y); };
1238 }
1239 }",
1240 "foo.cs",
1241 |metric| {
1242 insta::assert_json_snapshot!(
1244 metric.nom,
1245 @r#"
1246 {
1247 "functions": 1,
1248 "closures": 2,
1249 "functions_average": 0.2,
1250 "closures_average": 0.4,
1251 "total": 3,
1252 "average": 0.6,
1253 "functions_min": 0,
1254 "functions_max": 1,
1255 "closures_min": 0,
1256 "closures_max": 1
1257 }
1258 "#
1259 );
1260 },
1261 );
1262 }
1263
1264 #[test]
1265 fn csharp_indexer_nom() {
1266 check_nom_and_npm::<CsharpParser>(
1272 "class A {
1273 private int[] _d;
1274 public int this[int i] { get => _d[i]; set => _d[i] = value; }
1275 }",
1276 "foo.cs",
1277 |metric| {
1278 assert_eq!(metric.nom.functions_sum(), 2);
1281 assert_eq!(metric.npm.class_nm_sum(), 2);
1282 insta::assert_json_snapshot!(
1283 metric.nom,
1284 @r#"
1285 {
1286 "functions": 2,
1287 "closures": 0,
1288 "functions_average": 0.5,
1289 "closures_average": 0.0,
1290 "total": 2,
1291 "average": 0.5,
1292 "functions_min": 0,
1293 "functions_max": 1,
1294 "closures_min": 0,
1295 "closures_max": 0
1296 }
1297 "#
1298 );
1299 },
1300 );
1301 }
1302
1303 #[test]
1304 fn csharp_expression_bodied_indexer_nom() {
1305 check_nom_and_npm::<CsharpParser>(
1312 "class A {
1313 private int[] _d;
1314 public int this[int i] => _d[i];
1315 }",
1316 "foo.cs",
1317 |metric| {
1318 assert_eq!(metric.nom.functions_sum(), 1);
1320 assert_eq!(metric.npm.class_nm_sum(), 1);
1321 insta::assert_json_snapshot!(
1322 metric.nom,
1323 @r#"
1324 {
1325 "functions": 1,
1326 "closures": 0,
1327 "functions_average": 0.3333333333333333,
1328 "closures_average": 0.0,
1329 "total": 1,
1330 "average": 0.3333333333333333,
1331 "functions_min": 0,
1332 "functions_max": 1,
1333 "closures_min": 0,
1334 "closures_max": 0
1335 }
1336 "#
1337 );
1338 },
1339 );
1340 }
1341
1342 #[test]
1343 fn csharp_property_nom() {
1344 check_nom_and_npm::<CsharpParser>(
1350 "class A {
1351 private int _w;
1352 public int W { get => _w; set => _w = value; }
1353 }",
1354 "foo.cs",
1355 |metric| {
1356 assert_eq!(metric.nom.functions_sum(), 2);
1359 assert_eq!(metric.npm.class_nm_sum(), 2);
1360 },
1361 );
1362 }
1363
1364 #[test]
1365 fn csharp_auto_property_nom() {
1366 check_nom_and_npm::<CsharpParser>(
1370 "class A {
1371 public int Y { get; set; }
1372 }",
1373 "foo.cs",
1374 |metric| {
1375 assert_eq!(metric.nom.functions_sum(), 2);
1377 assert_eq!(metric.npm.class_nm_sum(), 2);
1378 },
1379 );
1380 }
1381
1382 #[test]
1383 fn csharp_expression_bodied_property_nom() {
1384 check_nom_and_npm::<CsharpParser>(
1392 "class A {
1393 private int _w;
1394 public int W => _w;
1395 }",
1396 "foo.cs",
1397 |metric| {
1398 assert_eq!(metric.nom.functions_sum(), 1);
1400 assert_eq!(metric.npm.class_nm_sum(), 1);
1401 },
1402 );
1403 }
1404
1405 #[test]
1406 fn go_top_level_funcs() {
1407 check_metrics::<GoParser>(
1408 "package main
1409 func a() {}
1410 func b() {}
1411 func c() {}",
1412 "foo.go",
1413 |metric| {
1414 insta::assert_json_snapshot!(
1416 metric.nom,
1417 @r#"
1418 {
1419 "functions": 3,
1420 "closures": 0,
1421 "functions_average": 0.75,
1422 "closures_average": 0.0,
1423 "total": 3,
1424 "average": 0.75,
1425 "functions_min": 0,
1426 "functions_max": 1,
1427 "closures_min": 0,
1428 "closures_max": 0
1429 }
1430 "#
1431 );
1432 },
1433 );
1434 }
1435
1436 #[test]
1437 fn go_method_declaration() {
1438 check_metrics::<GoParser>(
1439 "package main
1440 type T struct{}
1441 func (r *T) M() {}",
1442 "foo.go",
1443 |metric| {
1444 insta::assert_json_snapshot!(
1446 metric.nom,
1447 @r#"
1448 {
1449 "functions": 1,
1450 "closures": 0,
1451 "functions_average": 0.5,
1452 "closures_average": 0.0,
1453 "total": 1,
1454 "average": 0.5,
1455 "functions_min": 0,
1456 "functions_max": 1,
1457 "closures_min": 0,
1458 "closures_max": 0
1459 }
1460 "#
1461 );
1462 },
1463 );
1464 }
1465
1466 #[test]
1467 fn go_func_literal_is_closure() {
1468 check_metrics::<GoParser>(
1469 "package main
1470 var f = func() {}",
1471 "foo.go",
1472 |metric| {
1473 insta::assert_json_snapshot!(
1475 metric.nom,
1476 @r#"
1477 {
1478 "functions": 0,
1479 "closures": 1,
1480 "functions_average": 0.0,
1481 "closures_average": 0.5,
1482 "total": 1,
1483 "average": 0.5,
1484 "functions_min": 0,
1485 "functions_max": 0,
1486 "closures_min": 0,
1487 "closures_max": 1
1488 }
1489 "#
1490 );
1491 },
1492 );
1493 }
1494
1495 #[test]
1496 fn go_nested_closures() {
1497 check_metrics::<GoParser>(
1498 "package main
1499 func f() {
1500 inner := func() {
1501 deeper := func() {}
1502 _ = deeper
1503 }
1504 _ = inner
1505 }",
1506 "foo.go",
1507 |metric| {
1508 insta::assert_json_snapshot!(
1510 metric.nom,
1511 @r#"
1512 {
1513 "functions": 1,
1514 "closures": 2,
1515 "functions_average": 0.25,
1516 "closures_average": 0.5,
1517 "total": 3,
1518 "average": 0.75,
1519 "functions_min": 0,
1520 "functions_max": 1,
1521 "closures_min": 0,
1522 "closures_max": 1
1523 }
1524 "#
1525 );
1526 },
1527 );
1528 }
1529
1530 #[test]
1539 fn java_record_compact_constructor_counts_as_a_function() {
1540 check_metrics::<JavaParser>(
1541 "record R(int a, int b) {
1542 R { if (a < 0) { throw new IllegalArgumentException(); } }
1543 R(int a) { this(a, 0); }
1544 int sum() { return a + b; }
1545 }",
1546 "R.java",
1547 |metric| {
1548 assert_eq!(metric.nom.functions_sum(), 3);
1551 assert_eq!(metric.nom.closures_sum(), 0);
1552 },
1553 );
1554 }
1555
1556 #[test]
1557 fn java_closure_nom() {
1558 check_metrics::<JavaParser>(
1559 "interface printable{
1560 void print();
1561 }
1562
1563 interface IntFunc {
1564 int func(int n);
1565 }
1566
1567 class Printer implements printable{
1568 public void print(){System.out.println(\"Hello\");}
1569
1570 public static void main(String args[]){
1571 Printer obj = new Printer();
1572 obj.print();
1573 IntFunc meaning = (i) -> i + 42;
1574 int i = meaning.func(1);
1575 }
1576 }",
1577 "foo.java",
1578 |metric| {
1579 insta::assert_json_snapshot!(
1581 metric.nom,
1582 @r#"
1583 {
1584 "functions": 4,
1585 "closures": 1,
1586 "functions_average": 0.5,
1587 "closures_average": 0.125,
1588 "total": 5,
1589 "average": 0.625,
1590 "functions_min": 0,
1591 "functions_max": 1,
1592 "closures_min": 0,
1593 "closures_max": 1
1594 }
1595 "#
1596 );
1597 },
1598 );
1599 }
1600
1601 #[test]
1602 fn groovy_nom() {
1603 check_metrics::<GroovyParser>(
1604 "class Printer {
1605 void print() {
1606 println 'hello'
1607 }
1608 static void main(String[] args) {
1609 def p = new Printer()
1610 p.print()
1611 def doubler = { x -> x * 2 }
1612 int r = doubler(21)
1613 }
1614 }",
1615 "foo.groovy",
1616 |metric| {
1617 assert_eq!(metric.nom.functions_sum(), 2);
1624 assert_eq!(metric.nom.closures_sum(), 1);
1625 },
1626 );
1627 }
1628
1629 #[test]
1630 fn groovy_nom_function_definition() {
1631 check_metrics::<GroovyParser>(
1634 "def greet(name) {
1635 println(name)
1636 }
1637 greet('world')",
1638 "foo.groovy",
1639 |metric| {
1640 assert_eq!(metric.nom.functions_sum(), 1);
1641 },
1642 );
1643 }
1644
1645 #[test]
1646 fn perl_nom() {
1647 check_metrics::<PerlParser>(
1648 "sub a { 1 }
1649 sub b { 2 }
1650 my $c = sub { 3 };
1651 sub outer {
1652 my $inner = sub { 4 };
1653 return $inner;
1654 }",
1655 "foo.pl",
1656 |metric| {
1657 insta::assert_json_snapshot!(
1658 metric.nom,
1659 @r#"
1660 {
1661 "functions": 3,
1662 "closures": 2,
1663 "functions_average": 0.5,
1664 "closures_average": 0.3333333333333333,
1665 "total": 5,
1666 "average": 0.8333333333333334,
1667 "functions_min": 0,
1668 "functions_max": 1,
1669 "closures_min": 0,
1670 "closures_max": 1
1671 }
1672 "#
1673 );
1674 },
1675 );
1676 }
1677
1678 #[test]
1679 fn tsx_named_and_arrow_functions() {
1680 check_metrics::<TsxParser>(
1681 "function greet(name: string): string {
1682 return `Hello, ${name}`;
1683 }
1684 const add = (a: number, b: number) => a + b;
1685 const log = () => { console.log('done'); };",
1686 "foo.tsx",
1687 |metric| {
1688 insta::assert_json_snapshot!(
1689 metric.nom,
1690 @r#"
1691 {
1692 "functions": 3,
1693 "closures": 0,
1694 "functions_average": 0.75,
1695 "closures_average": 0.0,
1696 "total": 3,
1697 "average": 0.75,
1698 "functions_min": 0,
1699 "functions_max": 1,
1700 "closures_min": 0,
1701 "closures_max": 0
1702 }
1703 "#
1704 );
1705 },
1706 );
1707 }
1708
1709 #[test]
1710 fn typescript_named_arrow_and_class_methods() {
1711 check_metrics::<TypescriptParser>(
1712 "function compute(x: number): number {
1713 return x * 2;
1714 }
1715 const double = (n: number): number => n * 2;
1716 class Calculator {
1717 add(a: number, b: number): number {
1718 return a + b;
1719 }
1720 }",
1721 "foo.ts",
1722 |metric| {
1723 insta::assert_json_snapshot!(
1724 metric.nom,
1725 @r#"
1726 {
1727 "functions": 3,
1728 "closures": 0,
1729 "functions_average": 0.6,
1730 "closures_average": 0.0,
1731 "total": 3,
1732 "average": 0.6,
1733 "functions_min": 0,
1734 "functions_max": 1,
1735 "closures_min": 0,
1736 "closures_max": 0
1737 }
1738 "#
1739 );
1740 },
1741 );
1742 }
1743
1744 #[test]
1745 fn mozjs_nom() {
1746 check_metrics::<MozjsParser>(
1747 "function f(a, b) {
1748 function foo(a) {
1749 return a;
1750 }
1751 var bar = (function () {
1752 var counter = 0;
1753 return function () {
1754 counter += 1;
1755 return counter
1756 }
1757 })();
1758 return bar(foo(a), a);
1759 }",
1760 "foo.js",
1761 |metric| {
1762 insta::assert_json_snapshot!(
1763 metric.nom,
1764 @r#"
1765 {
1766 "functions": 2,
1767 "closures": 2,
1768 "functions_average": 0.4,
1769 "closures_average": 0.4,
1770 "total": 4,
1771 "average": 0.8,
1772 "functions_min": 0,
1773 "functions_max": 1,
1774 "closures_min": 0,
1775 "closures_max": 1
1776 }
1777 "#
1778 );
1779 },
1780 );
1781 }
1782
1783 #[test]
1784 fn mozjs_arrow_and_method() {
1785 check_metrics::<MozjsParser>(
1786 "let add = (a, b) => a + b;
1787 class Counter {
1788 increment() {
1789 this.count++;
1790 }
1791 }",
1792 "foo.js",
1793 |metric| {
1794 insta::assert_json_snapshot!(
1795 metric.nom,
1796 @r#"
1797 {
1798 "functions": 2,
1799 "closures": 0,
1800 "functions_average": 0.5,
1801 "closures_average": 0.0,
1802 "total": 2,
1803 "average": 0.5,
1804 "functions_min": 0,
1805 "functions_max": 1,
1806 "closures_min": 0,
1807 "closures_max": 0
1808 }
1809 "#
1810 );
1811 },
1812 );
1813 }
1814
1815 #[test]
1816 fn kotlin_nom_class_with_methods() {
1817 check_metrics::<KotlinParser>(
1818 "class Calculator {
1819 fun add(a: Int, b: Int): Int {
1820 return a + b
1821 }
1822 fun subtract(a: Int, b: Int): Int {
1823 return a - b
1824 }
1825 }",
1826 "foo.kt",
1827 |metric| {
1828 insta::assert_json_snapshot!(
1829 metric.nom,
1830 @r#"
1831 {
1832 "functions": 2,
1833 "closures": 0,
1834 "functions_average": 0.5,
1835 "closures_average": 0.0,
1836 "total": 2,
1837 "average": 0.5,
1838 "functions_min": 0,
1839 "functions_max": 1,
1840 "closures_min": 0,
1841 "closures_max": 0
1842 }
1843 "#
1844 );
1845 },
1846 );
1847 }
1848
1849 #[test]
1850 fn lua_nom() {
1851 check_metrics::<LuaParser>(
1852 "function greet(name)
1853 return \"hello \" .. name
1854end
1855
1856local add = function(a, b)
1857 return a + b
1858end
1859
1860local function outer()
1861 local inner = function()
1862 return 42
1863 end
1864 return inner()
1865end",
1866 "foo.lua",
1867 |metric| {
1868 insta::assert_json_snapshot!(metric.nom, @r#"
1870 {
1871 "functions": 2,
1872 "closures": 2,
1873 "functions_average": 0.4,
1874 "closures_average": 0.4,
1875 "total": 4,
1876 "average": 0.8,
1877 "functions_min": 0,
1878 "functions_max": 1,
1879 "closures_min": 0,
1880 "closures_max": 1
1881 }
1882 "#);
1883 },
1884 );
1885 }
1886
1887 #[test]
1888 fn bash_nom() {
1889 check_metrics::<BashParser>(
1890 "#!/bin/bash
1891foo() {
1892 echo 'hello'
1893}
1894bar() {
1895 echo 'world'
1896}
1897foo
1898bar",
1899 "foo.sh",
1900 |metric| {
1901 insta::assert_json_snapshot!(
1902 metric.nom,
1903 @r#"
1904 {
1905 "functions": 2,
1906 "closures": 0,
1907 "functions_average": 0.6666666666666666,
1908 "closures_average": 0.0,
1909 "total": 2,
1910 "average": 0.6666666666666666,
1911 "functions_min": 0,
1912 "functions_max": 1,
1913 "closures_min": 0,
1914 "closures_max": 0
1915 }
1916 "#
1917 );
1918 },
1919 );
1920 }
1921
1922 #[test]
1923 fn tcl_nom() {
1924 check_metrics::<TclParser>(
1925 "proc foo {a} { puts $a }
1926proc bar {x y} { puts $x }
1927foo 1
1928bar 2 3",
1929 "foo.tcl",
1930 |metric| {
1931 assert_eq!(metric.nom.functions_sum(), 2);
1932 assert_eq!(metric.nom.closures_sum(), 0);
1933 insta::assert_json_snapshot!(metric.nom);
1934 },
1935 );
1936 }
1937
1938 #[test]
1939 fn tcl_nested_nom() {
1940 check_metrics::<TclParser>(
1941 "proc outer {a} {
1942 proc inner {x} { puts $x }
1943 inner $a
1944}",
1945 "foo.tcl",
1946 |metric| {
1947 assert_eq!(metric.nom.functions_sum(), 2);
1948 assert_eq!(metric.nom.closures_sum(), 0);
1949 insta::assert_json_snapshot!(metric.nom);
1950 },
1951 );
1952 }
1953
1954 #[test]
1955 fn typescript_class_methods() {
1956 check_metrics::<TypescriptParser>(
1957 "class Calc {
1958 add(a: number, b: number): number { return a + b; }
1959 sub(a: number, b: number): number { return a - b; }
1960 }",
1961 "foo.ts",
1962 |metric| {
1963 assert_eq!(metric.nom.functions_sum(), 2);
1964 assert_eq!(metric.nom.closures_sum(), 0);
1965 insta::assert_json_snapshot!(metric.nom);
1966 },
1967 );
1968 }
1969
1970 #[test]
1971 fn typescript_arrow_and_function() {
1972 check_metrics::<TypescriptParser>(
1973 "function f(): number { return 1; }
1974 const g = (): number => 2;
1975 const h = (x: number): number => x * 2;",
1976 "foo.ts",
1977 |metric| {
1978 assert_eq!(metric.nom.functions_sum(), 3);
1979 assert_eq!(metric.nom.closures_sum(), 0);
1980 insta::assert_json_snapshot!(metric.nom);
1981 },
1982 );
1983 }
1984
1985 #[test]
1986 fn tsx_class_methods() {
1987 check_metrics::<TsxParser>(
1988 "class Calc {
1989 add(a: number, b: number): number { return a + b; }
1990 sub(a: number, b: number): number { return a - b; }
1991 }",
1992 "foo.tsx",
1993 |metric| {
1994 assert_eq!(metric.nom.functions_sum(), 2);
1995 assert_eq!(metric.nom.closures_sum(), 0);
1996 insta::assert_json_snapshot!(metric.nom);
1997 },
1998 );
1999 }
2000
2001 #[test]
2002 fn tsx_arrow_and_function() {
2003 check_metrics::<TsxParser>(
2004 "function f(): number { return 1; }
2005 const g = (): number => 2;
2006 const h = (x: number): number => x * 2;",
2007 "foo.tsx",
2008 |metric| {
2009 assert_eq!(metric.nom.functions_sum(), 3);
2010 assert_eq!(metric.nom.closures_sum(), 0);
2011 insta::assert_json_snapshot!(metric.nom);
2012 },
2013 );
2014 }
2015
2016 #[test]
2017 fn bash_multiple_functions_nom() {
2018 check_metrics::<BashParser>(
2019 "#!/bin/bash
2020f() {
2021 echo hello
2022}
2023g() {
2024 echo world
2025}",
2026 "foo.sh",
2027 |metric| {
2028 assert_eq!(metric.nom.functions_sum(), 2);
2029 assert_eq!(metric.nom.closures_sum(), 0);
2030 insta::assert_json_snapshot!(metric.nom);
2031 },
2032 );
2033 }
2034
2035 #[test]
2036 fn bash_nested_functions_nom() {
2037 check_metrics::<BashParser>(
2038 "#!/bin/bash
2039outer() {
2040 inner() {
2041 echo inner
2042 }
2043 inner
2044}",
2045 "foo.sh",
2046 |metric| {
2047 assert_eq!(metric.nom.functions_sum(), 2);
2048 assert_eq!(metric.nom.closures_sum(), 0);
2049 insta::assert_json_snapshot!(metric.nom);
2050 },
2051 );
2052 }
2053
2054 #[test]
2055 fn mozjs_nested_function_nom() {
2056 check_metrics::<MozjsParser>(
2057 "function outer() {
2058 function inner() {
2059 return 1;
2060 }
2061 return inner();
2062 }",
2063 "foo.js",
2064 |metric| {
2065 assert_eq!(metric.nom.functions_sum(), 2);
2066 assert_eq!(metric.nom.closures_sum(), 0);
2067 insta::assert_json_snapshot!(metric.nom);
2068 },
2069 );
2070 }
2071
2072 #[test]
2073 fn mozjs_class_methods_nom() {
2074 check_metrics::<MozjsParser>(
2075 "class Calc {
2076 add(a, b) { return a + b; }
2077 sub(a, b) { return a - b; }
2078 }",
2079 "foo.js",
2080 |metric| {
2081 assert_eq!(metric.nom.functions_sum(), 2);
2082 assert_eq!(metric.nom.closures_sum(), 0);
2083 insta::assert_json_snapshot!(metric.nom);
2084 },
2085 );
2086 }
2087
2088 #[test]
2089 fn mozjs_iife_nom() {
2090 check_metrics::<MozjsParser>(
2091 "(function() {
2092 var x = 1;
2093 return x;
2094 })();",
2095 "foo.js",
2096 |metric| {
2097 assert_eq!(metric.nom.functions_sum(), 0);
2098 assert_eq!(metric.nom.closures_sum(), 1);
2099 insta::assert_json_snapshot!(metric.nom);
2100 },
2101 );
2102 }
2103
2104 #[test]
2105 fn kotlin_class_methods_nom() {
2106 check_metrics::<KotlinParser>(
2107 "class Calc {
2108 fun add(a: Int, b: Int): Int = a + b
2109 fun sub(a: Int, b: Int): Int = a - b
2110 }",
2111 "foo.kt",
2112 |metric| {
2113 assert_eq!(metric.nom.functions_sum(), 2);
2114 assert_eq!(metric.nom.closures_sum(), 0);
2115 insta::assert_json_snapshot!(metric.nom);
2116 },
2117 );
2118 }
2119
2120 #[test]
2121 fn kotlin_lambda_nom() {
2122 check_metrics::<KotlinParser>(
2123 "fun f(list: List<Int>): Int {
2124 val double = { x: Int -> x * 2 }
2125 return list.sumOf(double)
2126 }",
2127 "foo.kt",
2128 |metric| {
2129 assert_eq!(metric.nom.functions_sum(), 1);
2130 assert_eq!(metric.nom.closures_sum(), 1);
2131 insta::assert_json_snapshot!(metric.nom);
2132 },
2133 );
2134 }
2135
2136 #[test]
2137 fn php_nom() {
2138 check_metrics::<PhpParser>(
2141 "<?php
2142 function top(): void {}
2143 class A {
2144 public function m1(): void {}
2145 public function m2(): int {
2146 $f = function () { return 1; };
2147 $g = fn () => 2;
2148 return $f() + $g();
2149 }
2150 }",
2151 "foo.php",
2152 |metric| {
2153 insta::assert_json_snapshot!(
2154 metric.nom,
2155 @r#"
2156 {
2157 "functions": 3,
2158 "closures": 2,
2159 "functions_average": 0.42857142857142855,
2160 "closures_average": 0.2857142857142857,
2161 "total": 5,
2162 "average": 0.7142857142857143,
2163 "functions_min": 0,
2164 "functions_max": 1,
2165 "closures_min": 0,
2166 "closures_max": 1
2167 }
2168 "#
2169 );
2170 },
2171 );
2172 }
2173
2174 #[test]
2175 fn php_nom_anonymous_class() {
2176 check_metrics::<PhpParser>(
2180 "<?php
2181 function f(): object {
2182 return new class {
2183 public function inner(): int { return 1; }
2184 };
2185 }",
2186 "foo.php",
2187 |metric| {
2188 insta::assert_json_snapshot!(
2189 metric.nom,
2190 @r#"
2191 {
2192 "functions": 2,
2193 "closures": 0,
2194 "functions_average": 0.5,
2195 "closures_average": 0.0,
2196 "total": 2,
2197 "average": 0.5,
2198 "functions_min": 0,
2199 "functions_max": 1,
2200 "closures_min": 0,
2201 "closures_max": 0
2202 }
2203 "#
2204 );
2205 },
2206 );
2207 }
2208
2209 #[test]
2218 fn elixir_nom_counts_def_as_functions_and_fn_as_closures() {
2219 check_metrics::<ElixirParser>(
2220 "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",
2221 "foo.ex",
2222 |metric| {
2223 assert_eq!(metric.nom.functions_sum(), 3);
2224 assert_eq!(metric.nom.closures_sum(), 2);
2225 assert_eq!(metric.nom.total(), 5);
2226 },
2227 );
2228 }
2229
2230 #[test]
2231 fn stats_display_commas_between_all_fields() {
2232 let stats = Stats {
2233 functions: 0,
2234 closures: 0,
2235 functions_sum: 3,
2236 closures_sum: 1,
2237 functions_min: 0,
2238 functions_max: 2,
2239 closures_min: 0,
2240 closures_max: 1,
2241 space_count: 2,
2242 };
2243 let formatted = format!("{stats}");
2244
2245 let expected_fragments = [
2248 "functions: 3, closures: 1",
2249 "closures: 1, functions_average:",
2250 "functions_average: 1.5, closures_average:",
2251 "closures_average: 0.5, total:",
2252 "total: 4, average:",
2253 "average: 2, functions_min:",
2254 "functions_min: 0, functions_max:",
2255 "functions_max: 2, closures_min:",
2256 "closures_min: 0, closures_max:",
2257 ];
2258
2259 for fragment in expected_fragments {
2260 assert!(
2261 formatted.contains(fragment),
2262 "missing fragment {fragment:?} in: {formatted}"
2263 );
2264 }
2265 }
2266
2267 #[test]
2268 fn ruby_nom() {
2269 check_metrics::<RubyParser>(
2275 "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",
2276 "foo.rb",
2277 |metric| {
2278 assert_eq!(metric.nom.functions_sum(), 3);
2279 assert_eq!(metric.nom.closures_sum(), 1);
2280 assert_eq!(metric.nom.total(), 4);
2281 },
2282 );
2283 }
2284
2285 #[test]
2286 fn ruby_stabby_lambda_single_closure() {
2287 check_metrics::<RubyParser>("f = ->(z) { z + 1 }\n", "stabby.rb", |metric| {
2292 assert_eq!(metric.nom.functions_sum(), 0);
2293 assert_eq!(metric.nom.closures_sum(), 1);
2294 });
2295 }
2296
2297 #[test]
2298 fn ruby_stabby_lambda_multi_statement_single_closure() {
2299 check_metrics::<RubyParser>(
2302 "f = ->(z) {\n y = z + 1\n y * 2\n}\n",
2303 "stabby_multi.rb",
2304 |metric| {
2305 assert_eq!(metric.nom.closures_sum(), 1);
2306 },
2307 );
2308 }
2309
2310 #[test]
2311 fn ruby_stabby_lambda_do_block_single_closure() {
2312 check_metrics::<RubyParser>("f = ->(z) do\n z + 1\nend\n", "stabby_do.rb", |metric| {
2315 assert_eq!(metric.nom.closures_sum(), 1);
2316 });
2317 }
2318
2319 #[test]
2320 fn ruby_keyword_lambda_single_closure() {
2321 check_metrics::<RubyParser>(
2326 "g = lambda { |z| z + 1 }\nh = proc { |z| z + 1 }\n",
2327 "keyword.rb",
2328 |metric| {
2329 assert_eq!(metric.nom.closures_sum(), 2);
2330 },
2331 );
2332 }
2333
2334 #[test]
2339 fn irules_nom_handlers_and_procs() {
2340 check_metrics::<IrulesParser>(
2341 "when CLIENT_ACCEPTED {
2342 log local0. \"connected\"
2343}
2344when HTTP_REQUEST {
2345 log local0. [HTTP::uri]
2346}
2347proc helper { x } {
2348 return $x
2349}
2350",
2351 "foo.irule",
2352 |metric| {
2353 assert_eq!(metric.nom.functions_sum(), 3);
2354 assert_eq!(metric.nom.closures_sum(), 0);
2355 assert_eq!(metric.nom.total(), 3);
2356 },
2357 );
2358 }
2359
2360 #[test]
2365 fn objc_nom() {
2366 check_metrics::<ObjcParser>(
2367 "@implementation Foo
2368- (void)one {
2369 void (^blk)(void) = ^{
2370 [self two];
2371 };
2372 blk();
2373}
2374- (void)two {
2375 [self one];
2376}
2377@end
2378",
2379 "foo.m",
2380 |metric| {
2381 assert_eq!(metric.nom.functions_sum(), 2);
2382 assert_eq!(metric.nom.closures_sum(), 1);
2383 assert_eq!(metric.nom.total(), 3);
2384 insta::assert_json_snapshot!(metric.nom, @r#"
2385 {
2386 "functions": 2,
2387 "closures": 1,
2388 "functions_average": 0.5,
2389 "closures_average": 0.25,
2390 "total": 3,
2391 "average": 0.75,
2392 "functions_min": 0,
2393 "functions_max": 1,
2394 "closures_min": 0,
2395 "closures_max": 1
2396 }
2397 "#);
2398 },
2399 );
2400 }
2401}