1use serde::Serialize;
2use serde::ser::{SerializeStruct, Serializer};
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::macros::implement_metric_trait;
7use crate::*;
8
9#[derive(Debug, Clone)]
14pub struct Stats {
15 fn_nargs: usize,
16 closure_nargs: usize,
17 fn_nargs_sum: usize,
18 closure_nargs_sum: usize,
19 fn_nargs_min: usize,
20 closure_nargs_min: usize,
21 fn_nargs_max: usize,
22 closure_nargs_max: usize,
23 total_functions: usize,
24 total_closures: usize,
25}
26
27impl Default for Stats {
28 fn default() -> Self {
29 Self {
30 fn_nargs: 0,
31 closure_nargs: 0,
32 fn_nargs_sum: 0,
33 closure_nargs_sum: 0,
34 fn_nargs_min: usize::MAX,
35 closure_nargs_min: usize::MAX,
36 fn_nargs_max: 0,
37 closure_nargs_max: 0,
38 total_functions: 0,
39 total_closures: 0,
40 }
41 }
42}
43
44impl Serialize for Stats {
45 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46 where
47 S: Serializer,
48 {
49 let mut st = serializer.serialize_struct("nargs", 10)?;
50 st.serialize_field("total_functions", &self.fn_args_sum())?;
51 st.serialize_field("total_closures", &self.closure_args_sum())?;
52 st.serialize_field("average_functions", &self.fn_args_average())?;
53 st.serialize_field("average_closures", &self.closure_args_average())?;
54 st.serialize_field("total", &self.nargs_total())?;
55 st.serialize_field("average", &self.nargs_average())?;
56 st.serialize_field("functions_min", &self.fn_args_min())?;
57 st.serialize_field("functions_max", &self.fn_args_max())?;
58 st.serialize_field("closures_min", &self.closure_args_min())?;
59 st.serialize_field("closures_max", &self.closure_args_max())?;
60 st.end()
61 }
62}
63
64impl fmt::Display for Stats {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 write!(
67 f,
68 "total_functions: {}, total_closures: {}, average_functions: {}, average_closures: {}, total: {}, average: {}, functions_min: {}, functions_max: {}, closures_min: {}, closures_max: {}",
69 self.fn_args(),
70 self.closure_args(),
71 self.fn_args_average(),
72 self.closure_args_average(),
73 self.nargs_total(),
74 self.nargs_average(),
75 self.fn_args_min(),
76 self.fn_args_max(),
77 self.closure_args_min(),
78 self.closure_args_max()
79 )
80 }
81}
82
83impl Stats {
84 pub fn merge(&mut self, other: &Stats) {
86 self.closure_nargs_min = self.closure_nargs_min.min(other.closure_nargs_min);
87 self.closure_nargs_max = self.closure_nargs_max.max(other.closure_nargs_max);
88 self.fn_nargs_min = self.fn_nargs_min.min(other.fn_nargs_min);
89 self.fn_nargs_max = self.fn_nargs_max.max(other.fn_nargs_max);
90 self.fn_nargs_sum += other.fn_nargs_sum;
91 self.closure_nargs_sum += other.closure_nargs_sum;
92 }
93
94 #[inline(always)]
96 pub fn fn_args(&self) -> f64 {
97 self.fn_nargs as f64
98 }
99
100 #[inline(always)]
102 pub fn closure_args(&self) -> f64 {
103 self.closure_nargs as f64
104 }
105
106 #[inline(always)]
108 pub fn fn_args_sum(&self) -> f64 {
109 self.fn_nargs_sum as f64
110 }
111
112 #[inline(always)]
114 pub fn closure_args_sum(&self) -> f64 {
115 self.closure_nargs_sum as f64
116 }
117
118 #[inline(always)]
120 pub fn fn_args_average(&self) -> f64 {
121 self.fn_nargs_sum as f64 / self.total_functions.max(1) as f64
122 }
123
124 #[inline(always)]
126 pub fn closure_args_average(&self) -> f64 {
127 self.closure_nargs_sum as f64 / self.total_closures.max(1) as f64
128 }
129
130 #[inline(always)]
133 pub fn nargs_total(&self) -> f64 {
134 self.fn_args_sum() + self.closure_args_sum()
135 }
136
137 #[inline(always)]
142 pub fn nargs_average(&self) -> f64 {
143 self.nargs_total() / (self.total_functions + self.total_closures).max(1) as f64
144 }
145 #[inline(always)]
147 pub fn fn_args_min(&self) -> f64 {
148 self.fn_nargs_min as f64
149 }
150 #[inline(always)]
152 pub fn fn_args_max(&self) -> f64 {
153 self.fn_nargs_max as f64
154 }
155 #[inline(always)]
157 pub fn closure_args_min(&self) -> f64 {
158 self.closure_nargs_min as f64
159 }
160 #[inline(always)]
162 pub fn closure_args_max(&self) -> f64 {
163 self.closure_nargs_max as f64
164 }
165 #[inline(always)]
166 pub(crate) fn compute_sum(&mut self) {
167 self.closure_nargs_sum += self.closure_nargs;
168 self.fn_nargs_sum += self.fn_nargs;
169 }
170 #[inline(always)]
171 pub(crate) fn compute_minmax(&mut self) {
172 self.closure_nargs_min = self.closure_nargs_min.min(self.closure_nargs);
173 self.closure_nargs_max = self.closure_nargs_max.max(self.closure_nargs);
174 self.fn_nargs_min = self.fn_nargs_min.min(self.fn_nargs);
175 self.fn_nargs_max = self.fn_nargs_max.max(self.fn_nargs);
176 self.compute_sum();
177 }
178 pub(crate) fn finalize(&mut self, total_functions: usize, total_closures: usize) {
179 self.total_functions = total_functions;
180 self.total_closures = total_closures;
181 }
182}
183
184#[inline(always)]
185fn compute_args<T: Checker>(node: &Node, nargs: &mut usize) {
186 if let Some(params) = node.child_by_field_name("parameters") {
187 let node_params = params;
188 node_params.act_on_child(&mut |n| {
189 if !T::is_non_arg(n) {
190 *nargs += 1;
191 }
192 });
193 }
194}
195
196pub trait NArgs
197where
198 Self: Checker,
199 Self: std::marker::Sized,
200{
201 fn compute(node: &Node, stats: &mut Stats) {
202 if Self::is_func(node) {
203 compute_args::<Self>(node, &mut stats.fn_nargs);
204 return;
205 }
206
207 if Self::is_closure(node) {
208 compute_args::<Self>(node, &mut stats.closure_nargs);
209 }
210 }
211}
212
213impl NArgs for CppCode {
214 fn compute(node: &Node, stats: &mut Stats) {
215 if Self::is_func(node) {
216 if let Some(declarator) = node.child_by_field_name("declarator") {
217 let new_node = declarator;
218 compute_args::<Self>(&new_node, &mut stats.fn_nargs);
219 }
220 return;
221 }
222
223 if Self::is_closure(node)
224 && let Some(declarator) = node.child_by_field_name("declarator")
225 {
226 let new_node = declarator;
227 compute_args::<Self>(&new_node, &mut stats.closure_nargs);
228 }
229 }
230}
231
232implement_metric_trait!(
233 [NArgs],
234 PythonCode,
235 MozjsCode,
236 JavascriptCode,
237 TypescriptCode,
238 TsxCode,
239 RustCode,
240 PreprocCode,
241 CcommentCode,
242 JavaCode,
243 KotlinCode
244);
245
246#[cfg(test)]
247mod tests {
248 use crate::tools::check_metrics;
249
250 use super::*;
251
252 #[test]
253 fn python_no_functions_and_closures() {
254 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
255 insta::assert_json_snapshot!(
257 metric.nargs,
258 @r###"
259 {
260 "total_functions": 0.0,
261 "total_closures": 0.0,
262 "average_functions": 0.0,
263 "average_closures": 0.0,
264 "total": 0.0,
265 "average": 0.0,
266 "functions_min": 0.0,
267 "functions_max": 0.0,
268 "closures_min": 0.0,
269 "closures_max": 0.0
270 }"###
271 );
272 });
273 }
274
275 #[test]
276 fn rust_no_functions_and_closures() {
277 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
278 insta::assert_json_snapshot!(
280 metric.nargs,
281 @r###"
282 {
283 "total_functions": 0.0,
284 "total_closures": 0.0,
285 "average_functions": 0.0,
286 "average_closures": 0.0,
287 "total": 0.0,
288 "average": 0.0,
289 "functions_min": 0.0,
290 "functions_max": 0.0,
291 "closures_min": 0.0,
292 "closures_max": 0.0
293 }"###
294 );
295 });
296 }
297
298 #[test]
299 fn cpp_no_functions_and_closures() {
300 check_metrics::<CppParser>("int a = 42;", "foo.cpp", |metric| {
301 insta::assert_json_snapshot!(
303 metric.nargs,
304 @r###"
305 {
306 "total_functions": 0.0,
307 "total_closures": 0.0,
308 "average_functions": 0.0,
309 "average_closures": 0.0,
310 "total": 0.0,
311 "average": 0.0,
312 "functions_min": 0.0,
313 "functions_max": 0.0,
314 "closures_min": 0.0,
315 "closures_max": 0.0
316 }"###
317 );
318 });
319 }
320
321 #[test]
322 fn javascript_no_functions_and_closures() {
323 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
324 insta::assert_json_snapshot!(
326 metric.nargs,
327 @r###"
328 {
329 "total_functions": 0.0,
330 "total_closures": 0.0,
331 "average_functions": 0.0,
332 "average_closures": 0.0,
333 "total": 0.0,
334 "average": 0.0,
335 "functions_min": 0.0,
336 "functions_max": 0.0,
337 "closures_min": 0.0,
338 "closures_max": 0.0
339 }"###
340 );
341 });
342 }
343
344 #[test]
345 fn python_single_function() {
346 check_metrics::<PythonParser>(
347 "def f(a, b):
348 if a:
349 return a",
350 "foo.py",
351 |metric| {
352 insta::assert_json_snapshot!(
354 metric.nargs,
355 @r#"
356 {
357 "total_functions": 0.0,
358 "total_closures": 0.0,
359 "average_functions": 0.0,
360 "average_closures": 0.0,
361 "total": 0.0,
362 "average": 0.0,
363 "functions_min": 0.0,
364 "functions_max": 0.0,
365 "closures_min": 0.0,
366 "closures_max": 0.0
367 }
368 "#
369 );
370 },
371 );
372 }
373
374 #[test]
375 fn rust_single_function() {
376 check_metrics::<RustParser>(
377 "fn f(a: bool, b: usize) {
378 if a {
379 return a;
380 }
381 }",
382 "foo.rs",
383 |metric| {
384 insta::assert_json_snapshot!(
386 metric.nargs,
387 @r###"
388 {
389 "total_functions": 2.0,
390 "total_closures": 0.0,
391 "average_functions": 2.0,
392 "average_closures": 0.0,
393 "total": 2.0,
394 "average": 2.0,
395 "functions_min": 0.0,
396 "functions_max": 2.0,
397 "closures_min": 0.0,
398 "closures_max": 0.0
399 }"###
400 );
401 },
402 );
403 }
404
405 #[test]
406 fn c_single_function() {
407 check_metrics::<CppParser>(
408 "int f(int a, int b) {
409 if (a) {
410 return a;
411 }
412 }",
413 "foo.c",
414 |metric| {
415 insta::assert_json_snapshot!(
417 metric.nargs,
418 @r###"
419 {
420 "total_functions": 2.0,
421 "total_closures": 0.0,
422 "average_functions": 2.0,
423 "average_closures": 0.0,
424 "total": 2.0,
425 "average": 2.0,
426 "functions_min": 0.0,
427 "functions_max": 2.0,
428 "closures_min": 0.0,
429 "closures_max": 0.0
430 }"###
431 );
432 },
433 );
434 }
435
436 #[test]
437 fn javascript_single_function() {
438 check_metrics::<JavascriptParser>(
439 "function f(a, b) {
440 return a * b;
441 }",
442 "foo.js",
443 |metric| {
444 insta::assert_json_snapshot!(
446 metric.nargs,
447 @r#"
448 {
449 "total_functions": 0.0,
450 "total_closures": 4.0,
451 "average_functions": 0.0,
452 "average_closures": 4.0,
453 "total": 4.0,
454 "average": 4.0,
455 "functions_min": 0.0,
456 "functions_max": 0.0,
457 "closures_min": 4.0,
458 "closures_max": 4.0
459 }
460 "#
461 );
462 },
463 );
464 }
465
466 #[test]
467 fn python_single_lambda() {
468 check_metrics::<PythonParser>("bar = lambda a: True", "foo.py", |metric| {
469 insta::assert_json_snapshot!(
471 metric.nargs,
472 @r#"
473 {
474 "total_functions": 0.0,
475 "total_closures": 0.0,
476 "average_functions": 0.0,
477 "average_closures": 0.0,
478 "total": 0.0,
479 "average": 0.0,
480 "functions_min": 0.0,
481 "functions_max": 0.0,
482 "closures_min": 0.0,
483 "closures_max": 0.0
484 }
485 "#
486 );
487 });
488 }
489
490 #[test]
491 fn rust_single_closure() {
492 check_metrics::<RustParser>("let bar = |i: i32| -> i32 { i + 1 };", "foo.rs", |metric| {
493 insta::assert_json_snapshot!(
495 metric.nargs,
496 @r###"
497 {
498 "total_functions": 0.0,
499 "total_closures": 1.0,
500 "average_functions": 0.0,
501 "average_closures": 1.0,
502 "total": 1.0,
503 "average": 1.0,
504 "functions_min": 0.0,
505 "functions_max": 0.0,
506 "closures_min": 0.0,
507 "closures_max": 1.0
508 }"###
509 );
510 });
511 }
512
513 #[test]
514 fn cpp_single_lambda() {
515 check_metrics::<CppParser>(
516 "auto bar = [](int x, int y) -> int { return x + y; };",
517 "foo.cpp",
518 |metric| {
519 insta::assert_json_snapshot!(
521 metric.nargs,
522 @r###"
523 {
524 "total_functions": 0.0,
525 "total_closures": 2.0,
526 "average_functions": 0.0,
527 "average_closures": 2.0,
528 "total": 2.0,
529 "average": 2.0,
530 "functions_min": 0.0,
531 "functions_max": 0.0,
532 "closures_min": 2.0,
533 "closures_max": 2.0
534 }"###
535 );
536 },
537 );
538 }
539
540 #[test]
541 fn javascript_single_closure() {
542 check_metrics::<JavascriptParser>("function (a, b) {return a + b};", "foo.js", |metric| {
543 insta::assert_json_snapshot!(
545 metric.nargs,
546 @r#"
547 {
548 "total_functions": 0.0,
549 "total_closures": 8.0,
550 "average_functions": 0.0,
551 "average_closures": 4.0,
552 "total": 8.0,
553 "average": 4.0,
554 "functions_min": 0.0,
555 "functions_max": 0.0,
556 "closures_min": 4.0,
557 "closures_max": 4.0
558 }
559 "#
560 );
561 });
562 }
563
564 #[test]
565 fn python_functions() {
566 check_metrics::<PythonParser>(
567 "def f(a, b):
568 if a:
569 return a
570 def f(a, b):
571 if b:
572 return b",
573 "foo.py",
574 |metric| {
575 insta::assert_json_snapshot!(
577 metric.nargs,
578 @r#"
579 {
580 "total_functions": 0.0,
581 "total_closures": 0.0,
582 "average_functions": 0.0,
583 "average_closures": 0.0,
584 "total": 0.0,
585 "average": 0.0,
586 "functions_min": 0.0,
587 "functions_max": 0.0,
588 "closures_min": 0.0,
589 "closures_max": 0.0
590 }
591 "#
592 );
593 },
594 );
595
596 check_metrics::<PythonParser>(
597 "def f(a, b):
598 if a:
599 return a
600 def f(a, b, c):
601 if b:
602 return b",
603 "foo.py",
604 |metric| {
605 insta::assert_json_snapshot!(
607 metric.nargs,
608 @r#"
609 {
610 "total_functions": 0.0,
611 "total_closures": 0.0,
612 "average_functions": 0.0,
613 "average_closures": 0.0,
614 "total": 0.0,
615 "average": 0.0,
616 "functions_min": 0.0,
617 "functions_max": 0.0,
618 "closures_min": 0.0,
619 "closures_max": 0.0
620 }
621 "#
622 );
623 },
624 );
625 }
626
627 #[test]
628 fn rust_functions() {
629 check_metrics::<RustParser>(
630 "fn f(a: bool, b: usize) {
631 if a {
632 return a;
633 }
634 }
635 fn f1(a: bool, b: usize) {
636 if a {
637 return a;
638 }
639 }",
640 "foo.rs",
641 |metric| {
642 insta::assert_json_snapshot!(
644 metric.nargs,
645 @r###"
646 {
647 "total_functions": 4.0,
648 "total_closures": 0.0,
649 "average_functions": 2.0,
650 "average_closures": 0.0,
651 "total": 4.0,
652 "average": 2.0,
653 "functions_min": 0.0,
654 "functions_max": 2.0,
655 "closures_min": 0.0,
656 "closures_max": 0.0
657 }"###
658 );
659 },
660 );
661
662 check_metrics::<RustParser>(
663 "fn f(a: bool, b: usize) {
664 if a {
665 return a;
666 }
667 }
668 fn f1(a: bool, b: usize, c: usize) {
669 if a {
670 return a;
671 }
672 }",
673 "foo.rs",
674 |metric| {
675 insta::assert_json_snapshot!(
677 metric.nargs,
678 @r###"
679 {
680 "total_functions": 5.0,
681 "total_closures": 0.0,
682 "average_functions": 2.5,
683 "average_closures": 0.0,
684 "total": 5.0,
685 "average": 2.5,
686 "functions_min": 0.0,
687 "functions_max": 3.0,
688 "closures_min": 0.0,
689 "closures_max": 0.0
690 }"###
691 );
692 },
693 );
694 }
695
696 #[test]
697 fn c_functions() {
698 check_metrics::<CppParser>(
699 "int f(int a, int b) {
700 if (a) {
701 return a;
702 }
703 }
704 int f1(int a, int b) {
705 if (a) {
706 return a;
707 }
708 }",
709 "foo.c",
710 |metric| {
711 insta::assert_json_snapshot!(
713 metric.nargs,
714 @r###"
715 {
716 "total_functions": 4.0,
717 "total_closures": 0.0,
718 "average_functions": 2.0,
719 "average_closures": 0.0,
720 "total": 4.0,
721 "average": 2.0,
722 "functions_min": 0.0,
723 "functions_max": 2.0,
724 "closures_min": 0.0,
725 "closures_max": 0.0
726 }"###
727 );
728 },
729 );
730
731 check_metrics::<CppParser>(
732 "int f(int a, int b) {
733 if (a) {
734 return a;
735 }
736 }
737 int f1(int a, int b, int c) {
738 if (a) {
739 return a;
740 }
741 }",
742 "foo.c",
743 |metric| {
744 insta::assert_json_snapshot!(
746 metric.nargs,
747 @r###"
748 {
749 "total_functions": 5.0,
750 "total_closures": 0.0,
751 "average_functions": 2.5,
752 "average_closures": 0.0,
753 "total": 5.0,
754 "average": 2.5,
755 "functions_min": 0.0,
756 "functions_max": 3.0,
757 "closures_min": 0.0,
758 "closures_max": 0.0
759 }"###
760 );
761 },
762 );
763 }
764
765 #[test]
766 fn javascript_functions() {
767 check_metrics::<JavascriptParser>(
768 "function f(a, b) {
769 return a * b;
770 }
771 function f1(a, b) {
772 return a * b;
773 }",
774 "foo.js",
775 |metric| {
776 insta::assert_json_snapshot!(
778 metric.nargs,
779 @r#"
780 {
781 "total_functions": 0.0,
782 "total_closures": 12.0,
783 "average_functions": 0.0,
784 "average_closures": 4.0,
785 "total": 12.0,
786 "average": 4.0,
787 "functions_min": 0.0,
788 "functions_max": 0.0,
789 "closures_min": 4.0,
790 "closures_max": 4.0
791 }
792 "#
793 );
794 },
795 );
796
797 check_metrics::<JavascriptParser>(
798 "function f(a, b) {
799 return a * b;
800 }
801 function f1(a, b, c) {
802 return a * b;
803 }",
804 "foo.js",
805 |metric| {
806 insta::assert_json_snapshot!(
808 metric.nargs,
809 @r#"
810 {
811 "total_functions": 0.0,
812 "total_closures": 13.0,
813 "average_functions": 0.0,
814 "average_closures": 4.333333333333333,
815 "total": 13.0,
816 "average": 4.333333333333333,
817 "functions_min": 0.0,
818 "functions_max": 0.0,
819 "closures_min": 4.0,
820 "closures_max": 5.0
821 }
822 "#
823 );
824 },
825 );
826 }
827
828 #[test]
829 fn python_nested_functions() {
830 check_metrics::<PythonParser>(
831 "def f(a, b):
832 def foo(a):
833 if a:
834 return 1
835 bar = lambda a: lambda b: b or True or True
836 return bar(foo(a))(a)",
837 "foo.py",
838 |metric| {
839 insta::assert_json_snapshot!(
841 metric.nargs,
842 @r#"
843 {
844 "total_functions": 0.0,
845 "total_closures": 0.0,
846 "average_functions": 0.0,
847 "average_closures": 0.0,
848 "total": 0.0,
849 "average": 0.0,
850 "functions_min": 0.0,
851 "functions_max": 0.0,
852 "closures_min": 0.0,
853 "closures_max": 0.0
854 }
855 "#
856 );
857 },
858 );
859 }
860
861 #[test]
862 fn rust_nested_functions() {
863 check_metrics::<RustParser>(
864 "fn f(a: i32, b: i32) -> i32 {
865 fn foo(a: i32) -> i32 {
866 return a;
867 }
868 let bar = |a: i32, b: i32| -> i32 { a + 1 };
869 let bar1 = |b: i32| -> i32 { b + 1 };
870 return bar(foo(a), a);
871 }",
872 "foo.rs",
873 |metric| {
874 insta::assert_json_snapshot!(
876 metric.nargs,
877 @r###"
878 {
879 "total_functions": 3.0,
880 "total_closures": 3.0,
881 "average_functions": 1.5,
882 "average_closures": 1.5,
883 "total": 6.0,
884 "average": 1.5,
885 "functions_min": 0.0,
886 "functions_max": 2.0,
887 "closures_min": 0.0,
888 "closures_max": 2.0
889 }"###
890 );
891 },
892 );
893 }
894
895 #[test]
896 fn cpp_nested_functions() {
897 check_metrics::<CppParser>(
898 "int f(int a, int b, int c) {
899 auto foo = [](int x) -> int { return x; };
900 auto bar = [](int x, int y) -> int { return x + y; };
901 return bar(foo(a), a);
902 }",
903 "foo.cpp",
904 |metric| {
905 insta::assert_json_snapshot!(
907 metric.nargs,
908 @r###"
909 {
910 "total_functions": 3.0,
911 "total_closures": 3.0,
912 "average_functions": 3.0,
913 "average_closures": 1.5,
914 "total": 6.0,
915 "average": 2.0,
916 "functions_min": 0.0,
917 "functions_max": 3.0,
918 "closures_min": 0.0,
919 "closures_max": 3.0
920 }"###
921 );
922 },
923 );
924 }
925
926 #[test]
927 fn javascript_nested_functions() {
928 check_metrics::<JavascriptParser>(
929 "function f(a, b) {
930 function foo(a, c) {
931 return a;
932 }
933 var bar = function (a, b) {return a + b};
934 function (a) {return a};
935 return bar(foo(a), a);
936 }",
937 "foo.js",
938 |metric| {
939 insta::assert_json_snapshot!(
941 metric.nargs,
942 @r#"
943 {
944 "total_functions": 0.0,
945 "total_closures": 15.0,
946 "average_functions": 0.0,
947 "average_closures": 3.75,
948 "total": 15.0,
949 "average": 3.75,
950 "functions_min": 0.0,
951 "functions_max": 0.0,
952 "closures_min": 3.0,
953 "closures_max": 4.0
954 }
955 "#
956 );
957 },
958 );
959 }
960}