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