1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use crate::checker::Checker;
6
7use crate::*;
8
9#[derive(Clone, Debug)]
11pub struct Stats {
12 functions: usize,
13 closures: usize,
14 functions_sum: usize,
15 closures_sum: usize,
16 functions_min: usize,
17 functions_max: usize,
18 closures_min: usize,
19 closures_max: usize,
20 space_count: usize,
21}
22impl Default for Stats {
23 fn default() -> Self {
24 Self {
25 functions: 0,
26 closures: 0,
27 functions_sum: 0,
28 closures_sum: 0,
29 functions_min: usize::MAX,
30 functions_max: 0,
31 closures_min: usize::MAX,
32 closures_max: 0,
33 space_count: 1,
34 }
35 }
36}
37impl Serialize for Stats {
38 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39 where
40 S: Serializer,
41 {
42 let mut st = serializer.serialize_struct("nom", 10)?;
43 st.serialize_field("functions", &self.functions_sum())?;
44 st.serialize_field("closures", &self.closures_sum())?;
45 st.serialize_field("functions_average", &self.functions_average())?;
46 st.serialize_field("closures_average", &self.closures_average())?;
47 st.serialize_field("total", &self.total())?;
48 st.serialize_field("average", &self.average())?;
49 st.serialize_field("functions_min", &self.functions_min())?;
50 st.serialize_field("functions_max", &self.functions_max())?;
51 st.serialize_field("closures_min", &self.closures_min())?;
52 st.serialize_field("closures_max", &self.closures_max())?;
53 st.end()
54 }
55}
56
57impl fmt::Display for Stats {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 write!(
60 f,
61 "functions: {}, \
62 closures: {}, \
63 functions_average: {}, \
64 closures_average: {}, \
65 total: {} \
66 average: {} \
67 functions_min: {} \
68 functions_max: {} \
69 closures_min: {} \
70 closures_max: {}",
71 self.functions_sum(),
72 self.closures_sum(),
73 self.functions_average(),
74 self.closures_average(),
75 self.total(),
76 self.average(),
77 self.functions_min(),
78 self.functions_max(),
79 self.closures_min(),
80 self.closures_max(),
81 )
82 }
83}
84
85impl Stats {
86 pub fn merge(&mut self, other: &Stats) {
88 self.functions_min = self.functions_min.min(other.functions_min);
89 self.functions_max = self.functions_max.max(other.functions_max);
90 self.closures_min = self.closures_min.min(other.closures_min);
91 self.closures_max = self.closures_max.max(other.closures_max);
92 self.functions_sum += other.functions_sum;
93 self.closures_sum += other.closures_sum;
94 self.space_count += other.space_count;
95 }
96
97 #[inline(always)]
99 pub fn functions(&self) -> f64 {
100 self.functions as f64
102 }
103
104 #[inline(always)]
106 pub fn closures(&self) -> f64 {
107 self.closures as f64
108 }
109
110 #[inline(always)]
112 pub fn functions_sum(&self) -> f64 {
113 self.functions_sum as f64
115 }
116
117 #[inline(always)]
119 pub fn closures_sum(&self) -> f64 {
120 self.closures_sum as f64
121 }
122
123 #[inline(always)]
125 pub fn functions_average(&self) -> f64 {
126 self.functions_sum() / self.space_count as f64
127 }
128
129 #[inline(always)]
131 pub fn closures_average(&self) -> f64 {
132 self.closures_sum() / self.space_count as f64
133 }
134
135 #[inline(always)]
137 pub fn average(&self) -> f64 {
138 self.total() / self.space_count as f64
139 }
140
141 #[inline(always)]
143 pub fn functions_min(&self) -> f64 {
144 self.functions_min as f64
146 }
147
148 #[inline(always)]
150 pub fn closures_min(&self) -> f64 {
151 self.closures_min as f64
152 }
153 #[inline(always)]
155 pub fn functions_max(&self) -> f64 {
156 self.functions_max as f64
158 }
159
160 #[inline(always)]
162 pub fn closures_max(&self) -> f64 {
163 self.closures_max as f64
164 }
165 #[inline(always)]
168 pub fn total(&self) -> f64 {
169 self.functions_sum() + self.closures_sum()
170 }
171 #[inline(always)]
172 pub(crate) fn compute_sum(&mut self) {
173 self.functions_sum += self.functions;
174 self.closures_sum += self.closures;
175 }
176 #[inline(always)]
177 pub(crate) fn compute_minmax(&mut self) {
178 self.functions_min = self.functions_min.min(self.functions);
179 self.functions_max = self.functions_max.max(self.functions);
180 self.closures_min = self.closures_min.min(self.closures);
181 self.closures_max = self.closures_max.max(self.closures);
182 self.compute_sum();
183 }
184}
185
186#[doc(hidden)]
187pub trait Nom
188where
189 Self: Checker,
190{
191 fn compute(node: &Node, stats: &mut Stats) {
192 if Self::is_func(node) {
193 stats.functions += 1;
194 return;
195 }
196 if Self::is_closure(node) {
197 stats.closures += 1;
198 }
199 }
200}
201
202impl Nom for PythonCode {}
203impl Nom for MozjsCode {}
204impl Nom for JavascriptCode {}
205impl Nom for TypescriptCode {}
206impl Nom for TsxCode {}
207impl Nom for RustCode {}
208impl Nom for CppCode {}
209impl Nom for PreprocCode {}
210impl Nom for CcommentCode {}
211impl Nom for JavaCode {}
212
213#[cfg(test)]
214mod tests {
215 use std::path::PathBuf;
216
217 use super::*;
218
219 #[test]
220 fn python_nom() {
221 check_metrics!(
222 "def a():
223 pass
224 def b():
225 pass
226 def c():
227 pass
228 x = lambda a : a + 42",
229 "foo.py",
230 PythonParser,
231 nom,
232 [
233 (functions_sum, 3, usize),
234 (closures_sum, 1, usize),
235 (total, 4, usize),
236 (functions_max, 1, usize),
237 (functions_min, 0, usize),
238 (closures_min, 0, usize),
239 (closures_max, 1, usize),
240 ],
241 [
242 (functions_average, 0.75), (closures_average, 0.25),
244 (average, 1.0)
245 ]
246 );
247 }
248
249 #[test]
250 fn rust_nom() {
251 check_metrics!(
252 "mod A { fn foo() {}}
253 mod B { fn foo() {}}
254 let closure = |i: i32| -> i32 { i + 42 };",
255 "foo.rs",
256 RustParser,
257 nom,
258 [
259 (functions_sum, 2, usize),
260 (closures_sum, 1, usize),
261 (total, 3, usize),
262 (functions_max, 1, usize),
263 (functions_min, 0, usize),
264 (closures_min, 0, usize),
265 (closures_max, 1, usize),
266 ],
267 [
268 (functions_average, 0.5), (closures_average, 0.25),
270 (average, 0.75)
271 ]
272 );
273 }
274
275 #[test]
276 fn c_nom() {
277 check_metrics!(
278 "int foo();
279
280 int foo() {
281 return 0;
282 }",
283 "foo.c",
284 CppParser,
285 nom,
286 [
287 (functions_sum, 1, usize),
288 (closures_sum, 0, usize),
289 (total, 1, usize),
290 (functions_max, 1, usize),
291 (functions_min, 0, usize),
292 (closures_min, 0, usize),
293 (closures_max, 0, usize),
294 ],
295 [
296 (functions_average, 0.5), (closures_average, 0.0),
298 (average, 0.5)
299 ]
300 );
301 }
302
303 #[test]
304 fn cpp_nom() {
305 check_metrics!(
306 "struct A {
307 void foo(int) {}
308 void foo(double) {}
309 };
310 int b = [](int x) -> int { return x + 42; };",
311 "foo.cpp",
312 CppParser,
313 nom,
314 [
315 (functions_sum, 2, usize),
316 (closures_sum, 1, usize),
317 (total, 3, usize),
318 (functions_max, 1, usize),
319 (functions_min, 0, usize),
320 (closures_min, 0, usize),
321 (closures_max, 1, usize),
322 ],
323 [
324 (functions_average, 0.5), (closures_average, 0.25),
326 (average, 0.75)
327 ]
328 );
329 }
330
331 #[test]
332 fn javascript_nom() {
333 check_metrics!(
334 "function f(a, b) {
335 function foo(a) {
336 return a;
337 }
338 var bar = (function () {
339 var counter = 0;
340 return function () {
341 counter += 1;
342 return counter
343 }
344 })();
345 return bar(foo(a), a);
346 }",
347 "foo.js",
348 JavascriptParser,
349 nom,
350 [
351 (functions_sum, 3, usize), (closures_sum, 1, usize), (total, 4, usize),
354 (functions_max, 1, usize),
355 (functions_min, 0, usize),
356 (closures_min, 0, usize),
357 (closures_max, 1, usize),
358 ],
359 [
360 (functions_average, 0.6), (closures_average, 0.2),
362 (average, 0.8)
363 ]
364 );
365 }
366
367 #[test]
368 fn javascript_call_nom() {
369 check_metrics!(
370 "add_task(async function test_safe_mode() {
371 gAppInfo.inSafeMode = true;
372 });",
373 "foo.js",
374 JavascriptParser,
375 nom,
376 [
377 (functions_sum, 1, usize), (closures_sum, 0, usize),
379 (total, 1, usize),
380 (functions_max, 1, usize),
381 (functions_min, 0, usize),
382 (closures_min, 0, usize),
383 (closures_max, 0, usize),
384 ],
385 [
386 (functions_average, 0.5), (closures_average, 0.0),
388 (average, 0.5)
389 ]
390 );
391 }
392
393 #[test]
394 fn javascript_assignment_nom() {
395 check_metrics!(
396 "AnimationTest.prototype.enableDisplay = function(element) {};",
397 "foo.js",
398 JavascriptParser,
399 nom,
400 [
401 (functions_sum, 1, usize),
402 (closures_sum, 0, usize),
403 (total, 1, usize),
404 (functions_max, 1, usize),
405 (functions_min, 0, usize),
406 (closures_min, 0, usize),
407 (closures_max, 0, usize),
408 ],
409 [
410 (functions_average, 0.5), (closures_average, 0.0),
412 (average, 0.5)
413 ]
414 );
415 }
416
417 #[test]
418 fn javascript_labeled_nom() {
419 check_metrics!(
420 "toJSON: function() {
421 return this.inspect(true);
422 }",
423 "foo.js",
424 JavascriptParser,
425 nom,
426 [
427 (functions_sum, 1, usize),
428 (closures_sum, 0, usize),
429 (total, 1, usize),
430 (functions_max, 1, usize),
431 (functions_min, 0, usize),
432 (closures_min, 0, usize),
433 (closures_max, 0, usize),
434 ],
435 [
436 (functions_average, 0.5), (closures_average, 0.0),
438 (average, 0.5)
439 ]
440 );
441 }
442
443 #[test]
444 fn javascript_labeled_arrow_nom() {
445 check_metrics!(
446 "const dimConverters = {
447 pt: x => x,
448 };",
449 "foo.js",
450 JavascriptParser,
451 nom,
452 [
453 (functions_sum, 1, usize),
454 (closures_sum, 0, usize),
455 (total, 1, usize),
456 (functions_max, 1, usize),
457 (functions_min, 0, usize),
458 (closures_min, 0, usize),
459 (closures_max, 0, usize),
460 ],
461 [
462 (functions_average, 0.5), (closures_average, 0.0),
464 (average, 0.5)
465 ]
466 );
467 }
468
469 #[test]
470 fn javascript_pair_nom() {
471 check_metrics!(
472 "return {
473 initialize: function(object) {
474 this._object = object.toObject();
475 },
476 }",
477 "foo.js",
478 JavascriptParser,
479 nom,
480 [
481 (functions_sum, 1, usize),
482 (closures_sum, 0, usize),
483 (total, 1, usize),
484 (functions_max, 1, usize),
485 (functions_min, 0, usize),
486 (closures_min, 0, usize),
487 (closures_max, 0, usize),
488 ],
489 [
490 (functions_average, 0.5), (closures_average, 0.0),
492 (average, 0.5)
493 ]
494 );
495 }
496
497 #[test]
498 fn javascript_unnamed_nom() {
499 check_metrics!(
500 "Ajax.getTransport = Try.these(
501 function() {
502 return function(){ return new XMLHttpRequest()}
503 }
504 );",
505 "foo.js",
506 JavascriptParser,
507 nom,
508 [
509 (functions_sum, 0, usize),
510 (closures_sum, 2, usize),
511 (total, 2, usize),
512 (functions_max, 0, usize),
513 (functions_min, 0, usize),
514 (closures_min, 0, usize),
515 (closures_max, 1, usize),
516 ],
517 [
518 (functions_average, 0.0), (closures_average, 0.6666666666666666),
520 (average, 0.6666666666666666)
521 ]
522 );
523 }
524
525 #[test]
526 fn javascript_arrow_nom() {
527 check_metrics!(
528 "var materials = [\"Hydrogen\"];
529 materials.map(material => material.length);
530 let add = (a, b) => a + b;",
531 "foo.js",
532 JavascriptParser,
533 nom,
534 [
535 (functions_sum, 1, usize), (closures_sum, 1, usize), (total, 2, usize),
538 (functions_max, 1, usize),
539 (functions_min, 0, usize),
540 (closures_min, 0, usize),
541 (closures_max, 1, usize),
542 ],
543 [
544 (functions_average, 0.3333333333333333), (closures_average, 0.3333333333333333),
546 (average, 0.6666666666666666)
547 ]
548 );
549 }
550
551 #[test]
552 fn javascript_arrow_assignment_nom() {
553 check_metrics!(
554 "sink.onPull = () => { };",
555 "foo.js",
556 JavascriptParser,
557 nom,
558 [
559 (functions_sum, 1, usize),
560 (closures_sum, 0, usize),
561 (total, 1, usize),
562 (functions_max, 1, usize),
563 (functions_min, 0, usize),
564 (closures_min, 0, usize),
565 (closures_max, 0, usize),
566 ],
567 [
568 (functions_average, 0.5), (closures_average, 0.0),
570 (average, 0.5)
571 ]
572 );
573 }
574
575 #[test]
576 fn javascript_arrow_new_nom() {
577 check_metrics!(
578 "const response = new Promise(resolve => channel.port1.onmessage = resolve);",
579 "foo.js",
580 JavascriptParser,
581 nom,
582 [
583 (functions_sum, 0, usize),
584 (closures_sum, 1, usize),
585 (total, 1, usize),
586 (functions_max, 0, usize),
587 (functions_min, 0, usize),
588 (closures_min, 0, usize),
589 (closures_max, 1, usize),
590 ],
591 [
592 (functions_average, 0.0), (closures_average, 0.5),
594 (average, 0.5)
595 ]
596 );
597 }
598
599 #[test]
600 fn javascript_arrow_call_nom() {
601 check_metrics!(
602 "let notDisabled = TestUtils.waitForCondition(
603 () => !backbutton.hasAttribute(\"disabled\")
604 );",
605 "foo.js",
606 JavascriptParser,
607 nom,
608 [
609 (functions_sum, 0, usize),
610 (closures_sum, 1, usize),
611 (total, 1, usize),
612 (functions_max, 0, usize),
613 (functions_min, 0, usize),
614 (closures_min, 0, usize),
615 (closures_max, 1, usize),
616 ],
617 [
618 (functions_average, 0.0), (closures_average, 0.5),
620 (average, 0.5)
621 ]
622 );
623 }
624
625 #[test]
626 fn java_nom() {
627 check_metrics!(
628 "class A {
629 public void foo(){
630 return;
631 }
632 public void bar(){
633 return;
634 }
635 }",
636 "foo.java",
637 JavaParser,
638 nom,
639 [
640 (functions_sum, 2, usize),
641 (closures_sum, 0, usize),
642 (total, 2, usize),
643 (functions_max, 1, usize),
644 (functions_min, 0, usize),
645 (closures_max, 0, usize),
646 (closures_min, 0, usize),
647 ],
648 [
649 (functions_average, 0.5), (closures_average, 0.0),
651 (average, 0.5)
652 ]
653 );
654 }
655
656 #[test]
657 fn java_closure_nom() {
658 check_metrics!(
659 "interface printable{
660 void print();
661 }
662
663 interface IntFunc {
664 int func(int n);
665 }
666
667 class Printer implements printable{
668 public void print(){System.out.println(\"Hello\");}
669
670 public static void main(String args[]){
671 Printer obj = new Printer();
672 obj.print();
673 IntFunc meaning = (i) -> i + 42;
674 int i = meaning.func(1);
675 }
676 }",
677 "foo.java",
678 JavaParser,
679 nom,
680 [
681 (functions_sum, 4, usize),
682 (closures_sum, 1, usize),
683 (total, 5, usize),
684 (functions_max, 1, usize),
685 (functions_min, 0, usize),
686 (closures_max, 1, usize),
687 (closures_min, 0, usize),
688 ],
689 [
690 (functions_average, 0.5), (closures_average, 0.125),
692 (average, 0.625)
693 ]
694 );
695 }
696}