1use std::collections::HashMap;
2
3use serde::Serialize;
4use serde::ser::{SerializeStruct, Serializer};
5use std::fmt;
6
7use crate::checker::Checker;
8use crate::macros::implement_metric_trait;
9use crate::*;
10
11#[derive(Debug, Clone)]
22pub struct Stats {
23 structural: usize,
24 structural_sum: usize,
25 structural_min: usize,
26 structural_max: usize,
27 nesting: usize,
28 max_nesting: usize,
29 total_nesting: usize,
30 bool_ops: usize,
31 total_space_functions: usize,
32 boolean_seq: BoolSequence,
33}
34
35impl Default for Stats {
36 fn default() -> Self {
37 Self {
38 structural: 0,
39 structural_sum: 0,
40 structural_min: usize::MAX,
41 structural_max: 0,
42 nesting: 0,
43 max_nesting: 0,
44 total_nesting: 0,
45 bool_ops: 0,
46 total_space_functions: 1,
47 boolean_seq: BoolSequence::default(),
48 }
49 }
50}
51
52impl Serialize for Stats {
53 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
54 where
55 S: Serializer,
56 {
57 let mut st = serializer.serialize_struct("cognitive", 4)?;
58 st.serialize_field("sum", &self.cognitive_sum())?;
59 st.serialize_field("average", &self.cognitive_average())?;
60 st.serialize_field("min", &self.cognitive_min())?;
61 st.serialize_field("max", &self.cognitive_max())?;
62 st.end()
63 }
64}
65
66impl fmt::Display for Stats {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 write!(
69 f,
70 "sum: {}, average: {}, min:{}, max: {}",
71 self.cognitive(),
72 self.cognitive_average(),
73 self.cognitive_min(),
74 self.cognitive_max()
75 )
76 }
77}
78
79impl Stats {
80 pub fn merge(&mut self, other: &Stats) {
82 self.structural_min = self.structural_min.min(other.structural_min);
83 self.structural_max = self.structural_max.max(other.structural_max);
84 self.structural_sum += other.structural_sum;
85 }
86
87 pub fn cognitive(&self) -> f64 {
89 self.structural as f64
90 }
91 pub fn cognitive_sum(&self) -> f64 {
93 self.structural_sum as f64
94 }
95
96 pub fn cognitive_min(&self) -> f64 {
98 self.structural_min as f64
99 }
100 pub fn cognitive_max(&self) -> f64 {
102 self.structural_max as f64
103 }
104
105 pub fn cognitive_average(&self) -> f64 {
112 self.cognitive_sum() / self.total_space_functions as f64
113 }
114
115 pub fn max_nesting(&self) -> f64 {
122 self.max_nesting as f64
123 }
124
125 pub fn total_nesting(&self) -> f64 {
128 self.total_nesting as f64
129 }
130
131 pub fn bool_ops(&self) -> f64 {
137 self.bool_ops as f64
138 }
139 #[inline(always)]
140 pub(crate) fn compute_sum(&mut self) {
141 self.structural_sum += self.structural;
142 }
143 #[inline(always)]
144 pub(crate) fn compute_minmax(&mut self) {
145 self.structural_min = self.structural_min.min(self.structural);
146 self.structural_max = self.structural_max.max(self.structural);
147 self.compute_sum();
148 }
149
150 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
151 self.total_space_functions = total_space_functions;
152 }
153}
154
155pub trait Cognitive
156where
157 Self: Checker,
158{
159 fn compute(
160 node: &Node,
161 stats: &mut Stats,
162 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
163 );
164}
165
166fn compute_booleans<T: std::cmp::PartialEq + std::convert::From<u16>>(
167 node: &Node,
168 stats: &mut Stats,
169 typs1: T,
170 typs2: T,
171) {
172 for child in node.children() {
173 if typs1 == child.kind_id().into() || typs2 == child.kind_id().into() {
174 let prev_structural = stats.structural;
175 stats.structural = stats
176 .boolean_seq
177 .eval_based_on_prev(child.kind_id(), stats.structural);
178 stats.bool_ops += stats.structural - prev_structural;
185 }
186 }
187}
188
189#[derive(Debug, Default, Clone)]
190struct BoolSequence {
191 boolean_op: Option<u16>,
192}
193
194impl BoolSequence {
195 fn reset(&mut self) {
196 self.boolean_op = None;
197 }
198
199 fn not_operator(&mut self, not_id: u16) {
200 self.boolean_op = Some(not_id);
201 }
202
203 fn eval_based_on_prev(&mut self, bool_id: u16, structural: usize) -> usize {
204 if let Some(prev) = self.boolean_op {
205 if prev != bool_id {
206 structural + 1
209 } else {
210 structural
213 }
214 } else {
215 self.boolean_op = Some(bool_id);
218 structural + 1
219 }
220 }
221}
222
223#[inline(always)]
224fn increment(stats: &mut Stats) {
225 stats.structural += stats.nesting + 1;
226}
227
228#[inline(always)]
229fn increment_by_one(stats: &mut Stats) {
230 stats.structural += 1;
231}
232
233fn get_nesting_from_map(
234 node: &Node,
235 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
236) -> (usize, usize, usize) {
237 if let Some(parent) = node.parent() {
238 if let Some(n) = nesting_map.get(&parent.id()) {
239 *n
240 } else {
241 (0, 0, 0)
242 }
243 } else {
244 (0, 0, 0)
245 }
246}
247
248fn increment_function_depth<T: std::cmp::PartialEq + std::convert::From<u16>>(
249 depth: &mut usize,
250 node: &Node,
251 stop: T,
252) {
253 let mut child = *node;
255 while let Some(parent) = child.parent() {
256 if stop == parent.kind_id().into() {
257 *depth += 1;
258 break;
259 }
260 child = parent;
261 }
262}
263
264#[inline(always)]
265fn increase_nesting(stats: &mut Stats, nesting: &mut usize, depth: usize, lambda: usize) {
266 stats.nesting = *nesting + depth + lambda;
267 let depth_count = stats.nesting + 1;
270 stats.max_nesting = stats.max_nesting.max(depth_count);
271 stats.total_nesting += depth_count;
272 increment(stats);
273 *nesting += 1;
274 stats.boolean_seq.reset();
275}
276
277impl Cognitive for PythonCode {
278 fn compute(
279 node: &Node,
280 stats: &mut Stats,
281 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
282 ) {
283 use Python::*;
284
285 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
287
288 match node.kind_id().into() {
289 IfStatement
290 | ForStatement
291 | WhileStatement
292 | MatchStatement
293 | ConditionalExpression => {
294 increase_nesting(stats, &mut nesting, depth, lambda);
295 }
296 ElifClause => {
297 increment_by_one(stats);
300 stats.boolean_seq.reset();
302 }
303 ElseClause => {
304 increment_by_one(stats);
308 stats.boolean_seq.reset();
313 }
314 ExceptClause => {
315 stats.nesting = nesting + depth + lambda;
321 increment(stats);
322 nesting += 1;
323 }
324 ExpressionList | ExpressionStatement | Tuple => {
325 stats.boolean_seq.reset();
326 }
327 NotOperator => {
328 stats.boolean_seq.not_operator(node.kind_id());
329 }
330 BooleanOperator => {
331 if node.count_specific_ancestors::<PythonParser>(
332 |node| node.kind_id() == BooleanOperator,
333 |node| node.kind_id() == Lambda,
334 ) == 0
335 {
336 stats.structural += node.count_specific_ancestors::<PythonParser>(
337 |node| node.kind_id() == Lambda,
338 |node| {
339 matches!(
340 node.kind_id().into(),
341 ExpressionList | IfStatement | ForStatement | WhileStatement
342 )
343 },
344 );
345 }
346 compute_booleans::<language_python::Python>(node, stats, And, Or);
347 }
348 Lambda => {
349 lambda += 1;
351 }
352 FunctionDefinition => {
353 increment_function_depth::<language_python::Python>(
355 &mut depth,
356 node,
357 FunctionDefinition,
358 );
359 }
360 _ => {}
361 }
362 nesting_map.insert(node.id(), (nesting, depth, lambda));
364 }
365}
366
367impl Cognitive for RustCode {
368 fn compute(
369 node: &Node,
370 stats: &mut Stats,
371 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
372 ) {
373 use Rust::*;
374 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
376
377 match node.kind_id().into() {
378 IfExpression => {
379 if !Self::is_else_if(node) {
381 increase_nesting(stats,&mut nesting, depth, lambda);
382 }
383 }
384 ForExpression | WhileExpression | LoopExpression | MatchExpression => {
385 increase_nesting(stats,&mut nesting, depth, lambda);
386 }
387 Else => {
388 increment_by_one(stats);
389 stats.boolean_seq.reset();
393 }
394 BreakExpression | ContinueExpression => {
395 if let Some(label_child) = node.child(1)
396 && let Label = label_child.kind_id().into()
397 {
398 increment_by_one(stats);
399 }
400 }
401 UnaryExpression => {
402 stats.boolean_seq.not_operator(node.kind_id());
403 }
404 BinaryExpression => {
405 compute_booleans::<language_rust::Rust>(node, stats, AMPAMP, PIPEPIPE);
406 }
407 FunctionItem => {
408 nesting = 0;
409 increment_function_depth::<language_rust::Rust>(&mut depth, node, FunctionItem);
411 }
412 ClosureExpression => {
413 lambda += 1;
414 }
415 _ => {}
416 }
417 nesting_map.insert(node.id(), (nesting, depth, lambda));
418 }
419}
420
421impl Cognitive for CppCode {
422 fn compute(
423 node: &Node,
424 stats: &mut Stats,
425 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
426 ) {
427 use Cpp::*;
428
429 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
431
432 match node.kind_id().into() {
433 IfStatement => {
434 if !Self::is_else_if(node) {
435 increase_nesting(stats,&mut nesting, depth, lambda);
436 }
437 }
438 ForStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause => {
439 increase_nesting(stats,&mut nesting, depth, lambda);
440 }
441 GotoStatement => {
442 increment_by_one(stats);
443 }
444 Else => {
445 increment_by_one(stats);
446 stats.boolean_seq.reset();
450 }
451 UnaryExpression2 => {
452 stats.boolean_seq.not_operator(node.kind_id());
453 }
454 BinaryExpression2 => {
455 compute_booleans::<language_cpp::Cpp>(node, stats, AMPAMP, PIPEPIPE);
456 }
457 LambdaExpression => {
458 lambda += 1;
459 }
460 _ => {}
461 }
462 nesting_map.insert(node.id(), (nesting, depth, lambda));
463 }
464}
465
466macro_rules! js_cognitive {
467 ($lang:ident) => {
468 fn compute(node: &Node, stats: &mut Stats, nesting_map: &mut HashMap<usize, (usize, usize, usize)>) {
469 use $lang::*;
470 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
471
472 match node.kind_id().into() {
473 IfStatement => {
474 if !Self::is_else_if(&node) {
475 increase_nesting(stats,&mut nesting, depth, lambda);
476 }
477 }
478 ForStatement | ForInStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause | TernaryExpression => {
479 increase_nesting(stats,&mut nesting, depth, lambda);
480 }
481 Else => {
482 increment_by_one(stats);
483 stats.boolean_seq.reset();
487 }
488 ExpressionStatement => {
489 stats.boolean_seq.reset();
491 }
492 UnaryExpression => {
493 stats.boolean_seq.not_operator(node.kind_id());
494 }
495 BinaryExpression => {
496 compute_booleans::<$lang>(node, stats, AMPAMP, PIPEPIPE);
497 }
498 FunctionDeclaration => {
499 nesting = 0;
501 lambda = 0;
502 increment_function_depth::<$lang>(&mut depth, node, FunctionDeclaration);
504 }
505 ArrowFunction => {
506 lambda += 1;
507 }
508 _ => {}
509 }
510 nesting_map.insert(node.id(), (nesting, depth, lambda));
511 }
512 };
513}
514
515impl Cognitive for JavascriptCode {
516 js_cognitive!(Javascript);
517}
518
519impl Cognitive for TypescriptCode {
520 js_cognitive!(Typescript);
521}
522
523impl Cognitive for TsxCode {
524 js_cognitive!(Tsx);
525}
526
527impl Cognitive for JavaCode {
528 fn compute(
529 node: &Node,
530 stats: &mut Stats,
531 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
532 ) {
533 use Java::*;
534
535 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
536
537 match node.kind_id().into() {
538 IfStatement => {
539 if !Self::is_else_if(node) {
540 increase_nesting(stats,&mut nesting, depth, lambda);
541 }
542 }
543 ForStatement | EnhancedForStatement | WhileStatement | DoStatement | SwitchBlock
544 | CatchClause | TernaryExpression => {
545 increase_nesting(stats,&mut nesting, depth, lambda);
546 }
547 Else => {
548 increment_by_one(stats);
549 stats.boolean_seq.reset();
553 }
554 UnaryExpression => {
555 stats.boolean_seq.not_operator(node.kind_id());
556 }
557 BinaryExpression => {
558 compute_booleans::<language_java::Java>(node, stats, AMPAMP, PIPEPIPE);
559 }
560 LambdaExpression => {
561 lambda += 1;
562 }
563 _ => {}
564 }
565 nesting_map.insert(node.id(), (nesting, depth, lambda));
566 }
567}
568
569implement_metric_trait!(Cognitive, PreprocCode, CcommentCode, KotlinCode);
570
571#[cfg(test)]
572mod tests {
573 use crate::tools::check_metrics;
574
575 use super::*;
576
577 #[test]
578 fn python_no_cognitive() {
579 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
580 insta::assert_json_snapshot!(
581 metric.cognitive,
582 @r###"
583 {
584 "sum": 0.0,
585 "average": null,
586 "min": 0.0,
587 "max": 0.0
588 }"###
589 );
590 });
591 }
592
593 #[test]
594 fn rust_no_cognitive() {
595 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
596 insta::assert_json_snapshot!(
597 metric.cognitive,
598 @r###"
599 {
600 "sum": 0.0,
601 "average": null,
602 "min": 0.0,
603 "max": 0.0
604 }"###
605 );
606 });
607 }
608
609 #[test]
610 fn c_no_cognitive() {
611 check_metrics::<CppParser>("int a = 42;", "foo.c", |metric| {
612 insta::assert_json_snapshot!(
613 metric.cognitive,
614 @r###"
615 {
616 "sum": 0.0,
617 "average": null,
618 "min": 0.0,
619 "max": 0.0
620 }"###
621 );
622 });
623 }
624
625 #[test]
626 fn mozjs_no_cognitive() {
627 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
628 insta::assert_json_snapshot!(
629 metric.cognitive,
630 @r###"
631 {
632 "sum": 0.0,
633 "average": null,
634 "min": 0.0,
635 "max": 0.0
636 }"###
637 );
638 });
639 }
640
641 #[test]
642 fn python_simple_function() {
643 check_metrics::<PythonParser>(
644 "def f(a, b):
645 if a and b: # +2 (+1 and)
646 return 1
647 if c and d: # +2 (+1 and)
648 return 1",
649 "foo.py",
650 |metric| {
651 insta::assert_json_snapshot!(
652 metric.cognitive,
653 @r###"
654 {
655 "sum": 4.0,
656 "average": 4.0,
657 "min": 0.0,
658 "max": 4.0
659 }"###
660 );
661 },
662 );
663 }
664
665 #[test]
666 fn python_expression_statement() {
667 check_metrics::<PythonParser>(
670 "def f(a, b):
671 c = True and True",
672 "foo.py",
673 |metric| {
674 insta::assert_json_snapshot!(
675 metric.cognitive,
676 @r###"
677 {
678 "sum": 1.0,
679 "average": 1.0,
680 "min": 0.0,
681 "max": 1.0
682 }"###
683 );
684 },
685 );
686 }
687
688 #[test]
689 fn python_tuple() {
690 check_metrics::<PythonParser>(
693 "def f(a, b):
694 return \"%s%s\" % (a and \"Get\" or \"Set\", b)",
695 "foo.py",
696 |metric| {
697 insta::assert_json_snapshot!(
698 metric.cognitive,
699 @r###"
700 {
701 "sum": 2.0,
702 "average": 2.0,
703 "min": 0.0,
704 "max": 2.0
705 }"###
706 );
707 },
708 );
709 }
710
711 #[test]
712 fn python_elif_function() {
713 check_metrics::<PythonParser>(
716 "def f(a, b):
717 if a and b: # +2 (+1 and)
718 return 1
719 elif c and d: # +2 (+1 and)
720 return 1",
721 "foo.py",
722 |metric| {
723 insta::assert_json_snapshot!(
724 metric.cognitive,
725 @r###"
726 {
727 "sum": 4.0,
728 "average": 4.0,
729 "min": 0.0,
730 "max": 4.0
731 }"###
732 );
733 },
734 );
735 }
736
737 #[test]
738 fn python_more_elifs_function() {
739 check_metrics::<PythonParser>(
742 "def f(a, b):
743 if a and b: # +2 (+1 and)
744 return 1
745 elif c and d: # +2 (+1 and)
746 return 1
747 elif e and f: # +2 (+1 and)
748 return 1",
749 "foo.py",
750 |metric| {
751 insta::assert_json_snapshot!(
752 metric.cognitive,
753 @r###"
754 {
755 "sum": 6.0,
756 "average": 6.0,
757 "min": 0.0,
758 "max": 6.0
759 }"###
760 );
761 },
762 );
763 }
764
765 #[test]
766 fn rust_simple_function() {
767 check_metrics::<RustParser>(
768 "fn f() {
769 if a && b { // +2 (+1 &&)
770 println!(\"test\");
771 }
772 if c && d { // +2 (+1 &&)
773 println!(\"test\");
774 }
775 }",
776 "foo.rs",
777 |metric| {
778 insta::assert_json_snapshot!(
779 metric.cognitive,
780 @r###"
781 {
782 "sum": 4.0,
783 "average": 4.0,
784 "min": 0.0,
785 "max": 4.0
786 }"###
787 );
788 },
789 );
790 }
791
792 #[test]
793 fn c_simple_function() {
794 check_metrics::<CppParser>(
795 "void f() {
796 if (a && b) { // +2 (+1 &&)
797 printf(\"test\");
798 }
799 if (c && d) { // +2 (+1 &&)
800 printf(\"test\");
801 }
802 }",
803 "foo.c",
804 |metric| {
805 insta::assert_json_snapshot!(
806 metric.cognitive,
807 @r###"
808 {
809 "sum": 4.0,
810 "average": 4.0,
811 "min": 0.0,
812 "max": 4.0
813 }"###
814 );
815 },
816 );
817 }
818
819 #[test]
820 fn mozjs_simple_function() {
821 check_metrics::<JavascriptParser>(
822 "function f() {
823 if (a && b) { // +2 (+1 &&)
824 window.print(\"test\");
825 }
826 if (c && d) { // +2 (+1 &&)
827 window.print(\"test\");
828 }
829 }",
830 "foo.js",
831 |metric| {
832 insta::assert_json_snapshot!(
833 metric.cognitive,
834 @r###"
835 {
836 "sum": 4.0,
837 "average": 4.0,
838 "min": 0.0,
839 "max": 4.0
840 }"###
841 );
842 },
843 );
844 }
845
846 #[test]
847 fn python_sequence_same_booleans() {
848 check_metrics::<PythonParser>(
849 "def f(a, b):
850 if a and b and True: # +2 (+1 sequence of and)
851 return 1",
852 "foo.py",
853 |metric| {
854 insta::assert_json_snapshot!(
855 metric.cognitive,
856 @r###"
857 {
858 "sum": 2.0,
859 "average": 2.0,
860 "min": 0.0,
861 "max": 2.0
862 }"###
863 );
864 },
865 );
866 }
867
868 #[test]
869 fn rust_sequence_same_booleans() {
870 check_metrics::<RustParser>(
871 "fn f() {
872 if a && b && true { // +2 (+1 sequence of &&)
873 println!(\"test\");
874 }
875 }",
876 "foo.rs",
877 |metric| {
878 insta::assert_json_snapshot!(
879 metric.cognitive,
880 @r###"
881 {
882 "sum": 2.0,
883 "average": 2.0,
884 "min": 0.0,
885 "max": 2.0
886 }"###
887 );
888 },
889 );
890
891 check_metrics::<RustParser>(
892 "fn f() {
893 if a || b || c || d { // +2 (+1 sequence of ||)
894 println!(\"test\");
895 }
896 }",
897 "foo.rs",
898 |metric| {
899 insta::assert_json_snapshot!(
900 metric.cognitive,
901 @r###"
902 {
903 "sum": 2.0,
904 "average": 2.0,
905 "min": 0.0,
906 "max": 2.0
907 }"###
908 );
909 },
910 );
911 }
912
913 #[test]
914 fn c_sequence_same_booleans() {
915 check_metrics::<CppParser>(
916 "void f() {
917 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
918 printf(\"test\");
919 }
920 }",
921 "foo.c",
922 |metric| {
923 insta::assert_json_snapshot!(
924 metric.cognitive,
925 @r###"
926 {
927 "sum": 2.0,
928 "average": 2.0,
929 "min": 0.0,
930 "max": 2.0
931 }"###
932 );
933 },
934 );
935
936 check_metrics::<CppParser>(
937 "void f() {
938 if (a || b || c || d) { // +2 (+1 sequence of ||)
939 printf(\"test\");
940 }
941 }",
942 "foo.c",
943 |metric| {
944 insta::assert_json_snapshot!(
945 metric.cognitive,
946 @r###"
947 {
948 "sum": 2.0,
949 "average": 2.0,
950 "min": 0.0,
951 "max": 2.0
952 }"###
953 );
954 },
955 );
956 }
957
958 #[test]
959 fn mozjs_sequence_same_booleans() {
960 check_metrics::<JavascriptParser>(
961 "function f() {
962 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
963 window.print(\"test\");
964 }
965 }",
966 "foo.js",
967 |metric| {
968 insta::assert_json_snapshot!(
969 metric.cognitive,
970 @r###"
971 {
972 "sum": 2.0,
973 "average": 2.0,
974 "min": 0.0,
975 "max": 2.0
976 }"###
977 );
978 },
979 );
980
981 check_metrics::<JavascriptParser>(
982 "function f() {
983 if (a || b || c || d) { // +2 (+1 sequence of ||)
984 window.print(\"test\");
985 }
986 }",
987 "foo.js",
988 |metric| {
989 insta::assert_json_snapshot!(
990 metric.cognitive,
991 @r###"
992 {
993 "sum": 2.0,
994 "average": 2.0,
995 "min": 0.0,
996 "max": 2.0
997 }"###
998 );
999 },
1000 );
1001 }
1002
1003 #[test]
1004 fn rust_not_booleans() {
1005 check_metrics::<RustParser>(
1006 "fn f() {
1007 if !a && !b { // +2 (+1 &&)
1008 println!(\"test\");
1009 }
1010 }",
1011 "foo.rs",
1012 |metric| {
1013 insta::assert_json_snapshot!(
1014 metric.cognitive,
1015 @r###"
1016 {
1017 "sum": 2.0,
1018 "average": 2.0,
1019 "min": 0.0,
1020 "max": 2.0
1021 }"###
1022 );
1023 },
1024 );
1025
1026 check_metrics::<RustParser>(
1027 "fn f() {
1028 if a && !(b && c) { // +3 (+1 &&, +1 &&)
1029 println!(\"test\");
1030 }
1031 }",
1032 "foo.rs",
1033 |metric| {
1034 insta::assert_json_snapshot!(
1035 metric.cognitive,
1036 @r###"
1037 {
1038 "sum": 3.0,
1039 "average": 3.0,
1040 "min": 0.0,
1041 "max": 3.0
1042 }"###
1043 );
1044 },
1045 );
1046
1047 check_metrics::<RustParser>(
1048 "fn f() {
1049 if !(a || b) && !(c || d) { // +4 (+1 ||, +1 &&, +1 ||)
1050 println!(\"test\");
1051 }
1052 }",
1053 "foo.rs",
1054 |metric| {
1055 insta::assert_json_snapshot!(
1056 metric.cognitive,
1057 @r###"
1058 {
1059 "sum": 4.0,
1060 "average": 4.0,
1061 "min": 0.0,
1062 "max": 4.0
1063 }"###
1064 );
1065 },
1066 );
1067 }
1068
1069 #[test]
1070 fn c_not_booleans() {
1071 check_metrics::<CppParser>(
1072 "void f() {
1073 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
1074 printf(\"test\");
1075 }
1076 }",
1077 "foo.c",
1078 |metric| {
1079 insta::assert_json_snapshot!(
1080 metric.cognitive,
1081 @r###"
1082 {
1083 "sum": 3.0,
1084 "average": 3.0,
1085 "min": 0.0,
1086 "max": 3.0
1087 }"###
1088 );
1089 },
1090 );
1091
1092 check_metrics::<CppParser>(
1093 "void f() {
1094 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
1095 printf(\"test\");
1096 }
1097 }",
1098 "foo.c",
1099 |metric| {
1100 insta::assert_json_snapshot!(
1101 metric.cognitive,
1102 @r###"
1103 {
1104 "sum": 4.0,
1105 "average": 4.0,
1106 "min": 0.0,
1107 "max": 4.0
1108 }"###
1109 );
1110 },
1111 );
1112 }
1113
1114 #[test]
1115 fn mozjs_not_booleans() {
1116 check_metrics::<JavascriptParser>(
1117 "function f() {
1118 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
1119 window.print(\"test\");
1120 }
1121 }",
1122 "foo.js",
1123 |metric| {
1124 insta::assert_json_snapshot!(
1125 metric.cognitive,
1126 @r###"
1127 {
1128 "sum": 3.0,
1129 "average": 3.0,
1130 "min": 0.0,
1131 "max": 3.0
1132 }"###
1133 );
1134 },
1135 );
1136
1137 check_metrics::<JavascriptParser>(
1138 "function f() {
1139 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
1140 window.print(\"test\");
1141 }
1142 }",
1143 "foo.js",
1144 |metric| {
1145 insta::assert_json_snapshot!(
1146 metric.cognitive,
1147 @r###"
1148 {
1149 "sum": 4.0,
1150 "average": 4.0,
1151 "min": 0.0,
1152 "max": 4.0
1153 }"###
1154 );
1155 },
1156 );
1157 }
1158
1159 #[test]
1160 fn python_sequence_different_booleans() {
1161 check_metrics::<PythonParser>(
1162 "def f(a, b):
1163 if a and b or True: # +3 (+1 and, +1 or)
1164 return 1",
1165 "foo.py",
1166 |metric| {
1167 insta::assert_json_snapshot!(
1168 metric.cognitive,
1169 @r###"
1170 {
1171 "sum": 3.0,
1172 "average": 3.0,
1173 "min": 0.0,
1174 "max": 3.0
1175 }"###
1176 );
1177 },
1178 );
1179 }
1180
1181 #[test]
1182 fn rust_sequence_different_booleans() {
1183 check_metrics::<RustParser>(
1184 "fn f() {
1185 if a && b || true { // +3 (+1 &&, +1 ||)
1186 println!(\"test\");
1187 }
1188 }",
1189 "foo.rs",
1190 |metric| {
1191 insta::assert_json_snapshot!(
1192 metric.cognitive,
1193 @r###"
1194 {
1195 "sum": 3.0,
1196 "average": 3.0,
1197 "min": 0.0,
1198 "max": 3.0
1199 }"###
1200 );
1201 },
1202 );
1203 }
1204
1205 #[test]
1206 fn c_sequence_different_booleans() {
1207 check_metrics::<CppParser>(
1208 "void f() {
1209 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
1210 printf(\"test\");
1211 }
1212 }",
1213 "foo.c",
1214 |metric| {
1215 insta::assert_json_snapshot!(
1216 metric.cognitive,
1217 @r###"
1218 {
1219 "sum": 3.0,
1220 "average": 3.0,
1221 "min": 0.0,
1222 "max": 3.0
1223 }"###
1224 );
1225 },
1226 );
1227 }
1228
1229 #[test]
1230 fn mozjs_sequence_different_booleans() {
1231 check_metrics::<JavascriptParser>(
1232 "function f() {
1233 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
1234 window.print(\"test\");
1235 }
1236 }",
1237 "foo.js",
1238 |metric| {
1239 insta::assert_json_snapshot!(
1240 metric.cognitive,
1241 @r###"
1242 {
1243 "sum": 3.0,
1244 "average": 3.0,
1245 "min": 0.0,
1246 "max": 3.0
1247 }"###
1248 );
1249 },
1250 );
1251 }
1252
1253 #[test]
1254 fn python_formatted_sequence_different_booleans() {
1255 check_metrics::<PythonParser>(
1256 "def f(a, b):
1257 if ( # +1
1258 a and b and # +1
1259 (c or d) # +1
1260 ):
1261 return 1",
1262 "foo.py",
1263 |metric| {
1264 insta::assert_json_snapshot!(
1265 metric.cognitive,
1266 @r###"
1267 {
1268 "sum": 3.0,
1269 "average": 3.0,
1270 "min": 0.0,
1271 "max": 3.0
1272 }"###
1273 );
1274 },
1275 );
1276 }
1277
1278 #[test]
1279 fn python_1_level_nesting() {
1280 check_metrics::<PythonParser>(
1281 "def f(a, b):
1282 if a: # +1
1283 for i in range(b): # +2
1284 return 1",
1285 "foo.py",
1286 |metric| {
1287 insta::assert_json_snapshot!(
1288 metric.cognitive,
1289 @r###"
1290 {
1291 "sum": 3.0,
1292 "average": 3.0,
1293 "min": 0.0,
1294 "max": 3.0
1295 }"###
1296 );
1297 },
1298 );
1299 }
1300
1301 #[test]
1302 fn rust_1_level_nesting() {
1303 check_metrics::<RustParser>(
1304 "fn f() {
1305 if true { // +1
1306 if true { // +2 (nesting = 1)
1307 println!(\"test\");
1308 } else if 1 == 1 { // +1
1309 if true { // +3 (nesting = 2)
1310 println!(\"test\");
1311 }
1312 } else { // +1
1313 if true { // +3 (nesting = 2)
1314 println!(\"test\");
1315 }
1316 }
1317 }
1318 }",
1319 "foo.rs",
1320 |metric| {
1321 insta::assert_json_snapshot!(
1322 metric.cognitive,
1323 @r###"
1324 {
1325 "sum": 11.0,
1326 "average": 11.0,
1327 "min": 0.0,
1328 "max": 11.0
1329 }"###
1330 );
1331 },
1332 );
1333
1334 check_metrics::<RustParser>(
1335 "fn f() {
1336 if true { // +1
1337 match true { // +2 (nesting = 1)
1338 true => println!(\"test\"),
1339 false => println!(\"test\"),
1340 }
1341 }
1342 }",
1343 "foo.rs",
1344 |metric| {
1345 insta::assert_json_snapshot!(
1346 metric.cognitive,
1347 @r###"
1348 {
1349 "sum": 3.0,
1350 "average": 3.0,
1351 "min": 0.0,
1352 "max": 3.0
1353 }"###
1354 );
1355 },
1356 );
1357 }
1358
1359 #[test]
1360 fn c_1_level_nesting() {
1361 check_metrics::<CppParser>(
1362 "void f() {
1363 if (1 == 1) { // +1
1364 if (1 == 1) { // +2 (nesting = 1)
1365 printf(\"test\");
1366 } else if (1 == 1) { // +1
1367 if (1 == 1) { // +3 (nesting = 2)
1368 printf(\"test\");
1369 }
1370 } else { // +1
1371 if (1 == 1) { // +3 (nesting = 2)
1372 printf(\"test\");
1373 }
1374 }
1375 }
1376 }",
1377 "foo.c",
1378 |metric| {
1379 insta::assert_json_snapshot!(
1380 metric.cognitive,
1381 @r###"
1382 {
1383 "sum": 11.0,
1384 "average": 11.0,
1385 "min": 0.0,
1386 "max": 11.0
1387 }"###
1388 );
1389 },
1390 );
1391 }
1392
1393 #[test]
1394 fn mozjs_1_level_nesting() {
1395 check_metrics::<JavascriptParser>(
1396 "function f() {
1397 if (1 == 1) { // +1
1398 if (1 == 1) { // +2 (nesting = 1)
1399 window.print(\"test\");
1400 } else if (1 == 1) { // +1
1401 if (1 == 1) { // +3 (nesting = 2)
1402 window.print(\"test\");
1403 }
1404 } else { // +1
1405 if (1 == 1) { // +3 (nesting = 2)
1406 window.print(\"test\");
1407 }
1408 }
1409 }
1410 }",
1411 "foo.js",
1412 |metric| {
1413 insta::assert_json_snapshot!(
1414 metric.cognitive,
1415 @r###"
1416 {
1417 "sum": 11.0,
1418 "average": 11.0,
1419 "min": 0.0,
1420 "max": 11.0
1421 }"###
1422 );
1423 },
1424 );
1425 }
1426
1427 #[test]
1428 fn python_2_level_nesting() {
1429 check_metrics::<PythonParser>(
1430 "def f(a, b):
1431 if a: # +1
1432 for i in range(b): # +2
1433 if b: # +3
1434 return 1",
1435 "foo.py",
1436 |metric| {
1437 insta::assert_json_snapshot!(
1438 metric.cognitive,
1439 @r###"
1440 {
1441 "sum": 6.0,
1442 "average": 6.0,
1443 "min": 0.0,
1444 "max": 6.0
1445 }"###
1446 );
1447 },
1448 );
1449 }
1450
1451 #[test]
1452 fn rust_2_level_nesting() {
1453 check_metrics::<RustParser>(
1454 "fn f() {
1455 if true { // +1
1456 for i in 0..4 { // +2 (nesting = 1)
1457 match true { // +3 (nesting = 2)
1458 true => println!(\"test\"),
1459 false => println!(\"test\"),
1460 }
1461 }
1462 }
1463 }",
1464 "foo.rs",
1465 |metric| {
1466 insta::assert_json_snapshot!(
1467 metric.cognitive,
1468 @r###"
1469 {
1470 "sum": 6.0,
1471 "average": 6.0,
1472 "min": 0.0,
1473 "max": 6.0
1474 }"###
1475 );
1476 },
1477 );
1478 }
1479
1480 #[test]
1481 fn python_try_construct() {
1482 check_metrics::<PythonParser>(
1483 "def f(a, b):
1484 try:
1485 for foo in bar: # +1
1486 return a
1487 except Exception: # +1
1488 if a < 0: # +2
1489 return a",
1490 "foo.py",
1491 |metric| {
1492 insta::assert_json_snapshot!(
1493 metric.cognitive,
1494 @r###"
1495 {
1496 "sum": 4.0,
1497 "average": 4.0,
1498 "min": 0.0,
1499 "max": 4.0
1500 }"###
1501 );
1502 },
1503 );
1504 }
1505
1506 #[test]
1507 fn mozjs_try_construct() {
1508 check_metrics::<JavascriptParser>(
1509 "function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
1510 for (const collector of this.collectors) {
1511 try {
1512 collector._onChannelRedirect(oldChannel, newChannel, flags);
1513 } catch (ex) {
1514 console.error(
1515 \"StackTraceCollector.onChannelRedirect threw an exception\",
1516 ex
1517 );
1518 }
1519 }
1520 callback.onRedirectVerifyCallback(Cr.NS_OK);
1521 }",
1522 "foo.js",
1523 |metric| {
1524 insta::assert_json_snapshot!(
1525 metric.cognitive,
1526 @r###"
1527 {
1528 "sum": 3.0,
1529 "average": 3.0,
1530 "min": 0.0,
1531 "max": 3.0
1532 }"###
1533 );
1534 },
1535 );
1536 }
1537
1538 #[test]
1539 fn rust_break_continue() {
1540 check_metrics::<RustParser>(
1542 "fn f() {
1543 'tens: for ten in 0..3 { // +1
1544 '_units: for unit in 0..=9 { // +2 (nesting = 1)
1545 if unit % 2 == 0 { // +3 (nesting = 2)
1546 continue;
1547 } else if unit == 5 { // +1
1548 continue 'tens; // +1
1549 } else if unit == 6 { // +1
1550 break;
1551 } else { // +1
1552 break 'tens; // +1
1553 }
1554 }
1555 }
1556 }",
1557 "foo.rs",
1558 |metric| {
1559 insta::assert_json_snapshot!(
1560 metric.cognitive,
1561 @r###"
1562 {
1563 "sum": 11.0,
1564 "average": 11.0,
1565 "min": 0.0,
1566 "max": 11.0
1567 }"###
1568 );
1569 },
1570 );
1571 }
1572
1573 #[test]
1574 fn c_goto() {
1575 check_metrics::<CppParser>(
1576 "void f() {
1577 OUT: for (int i = 1; i <= max; ++i) { // +1
1578 for (int j = 2; j < i; ++j) { // +2 (nesting = 1)
1579 if (i % j == 0) { // +3 (nesting = 2)
1580 goto OUT; // +1
1581 }
1582 }
1583 }
1584 }",
1585 "foo.c",
1586 |metric| {
1587 insta::assert_json_snapshot!(
1588 metric.cognitive,
1589 @r###"
1590 {
1591 "sum": 7.0,
1592 "average": 7.0,
1593 "min": 0.0,
1594 "max": 7.0
1595 }"###
1596 );
1597 },
1598 );
1599 }
1600
1601 #[test]
1602 fn c_switch() {
1603 check_metrics::<CppParser>(
1604 "void f() {
1605 switch (1) { // +1
1606 case 1:
1607 printf(\"one\");
1608 break;
1609 case 2:
1610 printf(\"two\");
1611 break;
1612 case 3:
1613 printf(\"three\");
1614 break;
1615 default:
1616 printf(\"all\");
1617 break;
1618 }
1619 }",
1620 "foo.c",
1621 |metric| {
1622 insta::assert_json_snapshot!(
1623 metric.cognitive,
1624 @r###"
1625 {
1626 "sum": 1.0,
1627 "average": 1.0,
1628 "min": 0.0,
1629 "max": 1.0
1630 }"###
1631 );
1632 },
1633 );
1634 }
1635
1636 #[test]
1637 fn mozjs_switch() {
1638 check_metrics::<JavascriptParser>(
1639 "function f() {
1640 switch (1) { // +1
1641 case 1:
1642 window.print(\"one\");
1643 break;
1644 case 2:
1645 window.print(\"two\");
1646 break;
1647 case 3:
1648 window.print(\"three\");
1649 break;
1650 default:
1651 window.print(\"all\");
1652 break;
1653 }
1654 }",
1655 "foo.js",
1656 |metric| {
1657 insta::assert_json_snapshot!(
1658 metric.cognitive,
1659 @r###"
1660 {
1661 "sum": 1.0,
1662 "average": 1.0,
1663 "min": 0.0,
1664 "max": 1.0
1665 }"###
1666 );
1667 },
1668 );
1669 }
1670
1671 #[test]
1672 fn python_ternary_operator() {
1673 check_metrics::<PythonParser>(
1674 "def f(a, b):
1675 if a % 2: # +1
1676 return 'c' if a else 'd' # +2
1677 return 'a' if a else 'b' # +1",
1678 "foo.py",
1679 |metric| {
1680 insta::assert_json_snapshot!(
1681 metric.cognitive,
1682 @r###"
1683 {
1684 "sum": 4.0,
1685 "average": 4.0,
1686 "min": 0.0,
1687 "max": 4.0
1688 }"###
1689 );
1690 },
1691 );
1692 }
1693
1694 #[test]
1695 fn python_nested_functions_lambdas() {
1696 check_metrics::<PythonParser>(
1697 "def f(a, b):
1698 def foo(a):
1699 if a: # +2 (+1 nesting)
1700 return 1
1701 # +3 (+1 for boolean sequence +2 for lambda nesting)
1702 bar = lambda a: lambda b: b or True or True
1703 return bar(foo(a))(a)",
1704 "foo.py",
1705 |metric| {
1706 insta::assert_json_snapshot!(
1708 metric.cognitive,
1709 @r###"
1710 {
1711 "sum": 5.0,
1712 "average": 1.25,
1713 "min": 0.0,
1714 "max": 3.0
1715 }"###
1716 );
1717 },
1718 );
1719 }
1720
1721 #[test]
1722 fn python_real_function() {
1723 check_metrics::<PythonParser>(
1724 "def process_raw_constant(constant, min_word_length):
1725 processed_words = []
1726 raw_camelcase_words = []
1727 for raw_word in re.findall(r'[a-z]+', constant): # +1
1728 word = raw_word.strip()
1729 if ( # +2 (+1 if and +1 nesting)
1730 len(word) >= min_word_length
1731 and not (word.startswith('-') or word.endswith('-')) # +2 operators
1732 ):
1733 if is_camel_case_word(word): # +3 (+1 if and +2 nesting)
1734 raw_camelcase_words.append(word)
1735 else: # +1 else
1736 processed_words.append(word.lower())
1737 return processed_words, raw_camelcase_words",
1738 "foo.py",
1739 |metric| {
1740 insta::assert_json_snapshot!(
1741 metric.cognitive,
1742 @r###"
1743 {
1744 "sum": 9.0,
1745 "average": 9.0,
1746 "min": 0.0,
1747 "max": 9.0
1748 }"###
1749 );
1750 },
1751 );
1752 }
1753
1754 #[test]
1755 fn rust_if_let_else_if_else() {
1756 check_metrics::<RustParser>(
1757 "pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
1758 debugln!(\"usage::create_usage_no_title;\");
1759 if let Some(u) = p.meta.usage_str { // +1
1760 String::from(&*u)
1761 } else if used.is_empty() { // +1
1762 create_help_usage(p, true)
1763 } else { // +1
1764 create_smart_usage(p, used)
1765 }
1766 }",
1767 "foo.rs",
1768 |metric| {
1769 insta::assert_json_snapshot!(
1770 metric.cognitive,
1771 @r###"
1772 {
1773 "sum": 3.0,
1774 "average": 3.0,
1775 "min": 0.0,
1776 "max": 3.0
1777 }"###
1778 );
1779 },
1780 );
1781 }
1782
1783 #[test]
1784 fn typescript_if_else_if_else() {
1785 check_metrics::<TypescriptParser>(
1786 "function foo() {
1787 if (this._closed) return Promise.resolve(); // +1
1788 if (this._tempDirectory) { // +1
1789 this.kill();
1790 } else if (this.connection) { // +1
1791 this.kill();
1792 } else { // +1
1793 throw new Error(`Error`);
1794 }
1795 helper.removeEventListeners(this._listeners);
1796 return this._processClosing;
1797 }",
1798 "foo.ts",
1799 |metric| {
1800 insta::assert_json_snapshot!(
1801 metric.cognitive,
1802 @r###"
1803 {
1804 "sum": 4.0,
1805 "average": 4.0,
1806 "min": 0.0,
1807 "max": 4.0
1808 }"###
1809 );
1810 },
1811 );
1812 }
1813
1814 #[test]
1815 fn rust_loop() {
1816 check_metrics::<RustParser>(
1817 "fn f(a: bool) {
1818 loop { // +1
1819 if a { // +2 (nesting = 1)
1820 break;
1821 }
1822 }
1823 }",
1824 "foo.rs",
1825 |metric| {
1826 insta::assert_json_snapshot!(
1827 metric.cognitive,
1828 @r###"
1829 {
1830 "sum": 3.0,
1831 "average": 3.0,
1832 "min": 0.0,
1833 "max": 3.0
1834 }"###
1835 );
1836 },
1837 );
1838 }
1839
1840 #[test]
1841 fn javascript_if_else_if_else() {
1842 check_metrics::<JavascriptParser>(
1843 "function foo(a, b) {
1844 if (a) { // +1
1845 g();
1846 } else if (b) { // +1
1847 g();
1848 } else { // +1
1849 g();
1850 }
1851 }",
1852 "foo.js",
1853 |metric| {
1854 insta::assert_json_snapshot!(
1855 metric.cognitive,
1856 @r###"
1857 {
1858 "sum": 3.0,
1859 "average": 3.0,
1860 "min": 0.0,
1861 "max": 3.0
1862 }"###
1863 );
1864 },
1865 );
1866 }
1867
1868 #[test]
1869 fn tsx_if_else_if_else() {
1870 check_metrics::<TsxParser>(
1871 "function foo(a, b) {
1872 if (a) { // +1
1873 g();
1874 } else if (b) { // +1
1875 g();
1876 } else { // +1
1877 g();
1878 }
1879 }",
1880 "foo.tsx",
1881 |metric| {
1882 insta::assert_json_snapshot!(
1883 metric.cognitive,
1884 @r###"
1885 {
1886 "sum": 3.0,
1887 "average": 3.0,
1888 "min": 0.0,
1889 "max": 3.0
1890 }"###
1891 );
1892 },
1893 );
1894 }
1895
1896 #[test]
1897 fn java_enhanced_for() {
1898 check_metrics::<JavaParser>(
1899 "class X {
1900 void f(int[] xs) {
1901 for (int x : xs) { // +1
1902 if (x > 0) { // +2 (nesting = 1)
1903 g(x);
1904 }
1905 }
1906 }
1907 }",
1908 "foo.java",
1909 |metric| {
1910 insta::assert_json_snapshot!(
1911 metric.cognitive,
1912 @r###"
1913 {
1914 "sum": 3.0,
1915 "average": 3.0,
1916 "min": 0.0,
1917 "max": 3.0
1918 }"###
1919 );
1920 },
1921 );
1922 }
1923
1924 #[test]
1925 fn java_ternary() {
1926 check_metrics::<JavaParser>(
1927 "class X {
1928 int f(boolean a) {
1929 return a ? 1 : 2; // +1
1930 }
1931 }",
1932 "foo.java",
1933 |metric| {
1934 insta::assert_json_snapshot!(
1935 metric.cognitive,
1936 @r###"
1937 {
1938 "sum": 1.0,
1939 "average": 1.0,
1940 "min": 0.0,
1941 "max": 1.0
1942 }"###
1943 );
1944 },
1945 );
1946 }
1947
1948 #[test]
1949 fn java_else_if() {
1950 check_metrics::<JavaParser>(
1951 "class X {
1952 void f(int a) {
1953 if (a == 1) { // +1
1954 g();
1955 } else if (a == 2) { // +1
1956 g();
1957 } else { // +1
1958 g();
1959 }
1960 }
1961 }",
1962 "foo.java",
1963 |metric| {
1964 insta::assert_json_snapshot!(
1965 metric.cognitive,
1966 @r###"
1967 {
1968 "sum": 3.0,
1969 "average": 3.0,
1970 "min": 0.0,
1971 "max": 3.0
1972 }"###
1973 );
1974 },
1975 );
1976 }
1977
1978 #[test]
1979 fn python_match() {
1980 check_metrics::<PythonParser>(
1981 "def f(x):
1982 match x: # +1
1983 case 1:
1984 if x: # +2 (nesting = 1)
1985 g(x)",
1986 "foo.py",
1987 |metric| {
1988 insta::assert_json_snapshot!(
1989 metric.cognitive,
1990 @r###"
1991 {
1992 "sum": 3.0,
1993 "average": 3.0,
1994 "min": 0.0,
1995 "max": 3.0
1996 }"###
1997 );
1998 },
1999 );
2000 }
2001
2002 #[test]
2003 fn python_finally_no_increment() {
2004 check_metrics::<PythonParser>(
2005 "def f():
2006 try:
2007 g()
2008 finally: # +0 (finally is not a branch)
2009 h()",
2010 "foo.py",
2011 |metric| {
2012 insta::assert_json_snapshot!(
2013 metric.cognitive,
2014 @r###"
2015 {
2016 "sum": 0.0,
2017 "average": 0.0,
2018 "min": 0.0,
2019 "max": 0.0
2020 }"###
2021 );
2022 },
2023 );
2024 }
2025
2026 #[test]
2027 fn java_no_cognitive() {
2028 check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
2029 insta::assert_json_snapshot!(
2030 metric.cognitive,
2031 @r###"
2032 {
2033 "sum": 0.0,
2034 "average": null,
2035 "min": 0.0,
2036 "max": 0.0
2037 }
2038 "###
2039 );
2040 });
2041 }
2042
2043 #[test]
2044 fn java_single_branch_function() {
2045 check_metrics::<JavaParser>(
2046 "class X {
2047 public static void print(boolean a){
2048 if(a){ // +1
2049 System.out.println(\"test1\");
2050 }
2051 }
2052 }",
2053 "foo.java",
2054 |metric| {
2055 insta::assert_json_snapshot!(
2056 metric.cognitive,
2057 @r###"
2058 {
2059 "sum": 1.0,
2060 "average": 1.0,
2061 "min": 0.0,
2062 "max": 1.0
2063 }
2064 "###
2065 );
2066 },
2067 );
2068 }
2069
2070 #[test]
2071 fn java_multiple_branch_function() {
2072 check_metrics::<JavaParser>(
2073 "class X {
2074 public static void print(boolean a, boolean b){
2075 if(a){ // +1
2076 System.out.println(\"test1\");
2077 }
2078 if(b){ // +1
2079 System.out.println(\"test2\");
2080 }
2081 else { // +1
2082 System.out.println(\"test3\");
2083 }
2084 }
2085 }",
2086 "foo.java",
2087 |metric| {
2088 insta::assert_json_snapshot!(
2089 metric.cognitive,
2090 @r###"
2091 {
2092 "sum": 3.0,
2093 "average": 3.0,
2094 "min": 0.0,
2095 "max": 3.0
2096 }
2097 "###
2098 );
2099 },
2100 );
2101 }
2102
2103 #[test]
2104 fn java_compound_conditions() {
2105 check_metrics::<JavaParser>(
2106 "class X {
2107 public static void print(boolean a, boolean b, boolean c, boolean d){
2108 if(a && b){ // +2 (+1 &&)
2109 System.out.println(\"test1\");
2110 }
2111 if(c && d){ // +2 (+1 &&)
2112 System.out.println(\"test2\");
2113 }
2114 }
2115 }",
2116 "foo.java",
2117 |metric| {
2118 insta::assert_json_snapshot!(
2119 metric.cognitive,
2120 @r###"
2121 {
2122 "sum": 4.0,
2123 "average": 4.0,
2124 "min": 0.0,
2125 "max": 4.0
2126 }"###
2127 );
2128 },
2129 );
2130 }
2131
2132 #[test]
2133 fn java_switch_statement() {
2134 check_metrics::<JavaParser>(
2135 "class X {
2136 public static void print(boolean a, boolean b, boolean c, boolean d){
2137 switch(expr){ //+1
2138 case 1:
2139 System.out.println(\"test1\");
2140 break;
2141 case 2:
2142 System.out.println(\"test2\");
2143 break;
2144 default:
2145 System.out.println(\"test\");
2146 }
2147 }
2148 }",
2149 "foo.java",
2150 |metric| {
2151 insta::assert_json_snapshot!(
2152 metric.cognitive,
2153 @r###"
2154 {
2155 "sum": 1.0,
2156 "average": 1.0,
2157 "min": 0.0,
2158 "max": 1.0
2159 }"###
2160 );
2161 },
2162 );
2163 }
2164
2165 #[test]
2166 fn java_switch_expression() {
2167 check_metrics::<JavaParser>(
2168 "class X {
2169 public static void print(boolean a, boolean b, boolean c, boolean d){
2170 switch(expr){ // +1
2171 case 1 -> System.out.println(\"test1\");
2172 case 2 -> System.out.println(\"test2\");
2173 default -> System.out.println(\"test\");
2174 }
2175 }
2176 }",
2177 "foo.java",
2178 |metric| {
2179 insta::assert_json_snapshot!(
2180 metric.cognitive,
2181 @r###"
2182 {
2183 "sum": 1.0,
2184 "average": 1.0,
2185 "min": 0.0,
2186 "max": 1.0
2187 }"###
2188 );
2189 },
2190 );
2191 }
2192
2193 #[test]
2194 fn java_not_booleans() {
2195 check_metrics::<JavaParser>(
2196 "class X {
2197 public static void print(boolean a, boolean b, boolean c, boolean d){
2198 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
2199 printf(\"test\");
2200 }
2201 }
2202 }",
2203 "foo.java",
2204 |metric| {
2205 insta::assert_json_snapshot!(
2206 metric.cognitive,
2207 @r###"
2208 {
2209 "sum": 3.0,
2210 "average": 3.0,
2211 "min": 0.0,
2212 "max": 3.0
2213 }"###
2214 );
2215 },
2216 );
2217 }
2218
2219 #[test]
2220 fn rust_boolean_sequence_across_else_if() {
2221 check_metrics::<RustParser>(
2226 "fn f() {
2227 if a && b { // +2 (+1 if, +1 &&)
2228 foo();
2229 } else if c && d { // +2 (+1 else, +1 &&)
2230 bar();
2231 }
2232 }",
2233 "foo.rs",
2234 |metric| {
2235 insta::assert_json_snapshot!(
2236 metric.cognitive,
2237 @r###"
2238 {
2239 "sum": 4.0,
2240 "average": 4.0,
2241 "min": 0.0,
2242 "max": 4.0
2243 }"###
2244 );
2245 },
2246 );
2247 }
2248
2249 #[test]
2250 fn c_boolean_sequence_across_else_if() {
2251 check_metrics::<CppParser>(
2252 "void f() {
2253 if (a && b) { // +2 (+1 if, +1 &&)
2254 g();
2255 } else if (c && d) { // +2 (+1 else, +1 &&)
2256 h();
2257 }
2258 }",
2259 "foo.c",
2260 |metric| {
2261 insta::assert_json_snapshot!(
2262 metric.cognitive,
2263 @r###"
2264 {
2265 "sum": 4.0,
2266 "average": 4.0,
2267 "min": 0.0,
2268 "max": 4.0
2269 }"###
2270 );
2271 },
2272 );
2273 }
2274
2275 #[test]
2276 fn java_boolean_sequence_across_else_if() {
2277 check_metrics::<JavaParser>(
2278 "class X {
2279 void f(boolean a, boolean b, boolean c, boolean d) {
2280 if (a && b) { // +2 (+1 if, +1 &&)
2281 g();
2282 } else if (c && d) { // +2 (+1 else, +1 &&)
2283 h();
2284 }
2285 }
2286 }",
2287 "foo.java",
2288 |metric| {
2289 insta::assert_json_snapshot!(
2290 metric.cognitive,
2291 @r###"
2292 {
2293 "sum": 4.0,
2294 "average": 4.0,
2295 "min": 0.0,
2296 "max": 4.0
2297 }"###
2298 );
2299 },
2300 );
2301 }
2302
2303 #[test]
2304 fn javascript_boolean_sequence_across_else_if() {
2305 check_metrics::<JavascriptParser>(
2310 "function f(a, b, c, d) {
2311 if (a && b) { // +2 (+1 if, +1 &&)
2312 } else if (c && d) { // +2 (+1 else, +1 &&)
2313 }
2314 }",
2315 "foo.js",
2316 |metric| {
2317 insta::assert_json_snapshot!(
2318 metric.cognitive,
2319 @r###"
2320 {
2321 "sum": 4.0,
2322 "average": 4.0,
2323 "min": 0.0,
2324 "max": 4.0
2325 }"###
2326 );
2327 },
2328 );
2329 }
2330
2331 #[test]
2332 fn typescript_boolean_sequence_across_else_if() {
2333 check_metrics::<TypescriptParser>(
2334 "function f(a, b, c, d) {
2335 if (a && b) { // +2 (+1 if, +1 &&)
2336 } else if (c && d) { // +2 (+1 else, +1 &&)
2337 }
2338 }",
2339 "foo.ts",
2340 |metric| {
2341 insta::assert_json_snapshot!(
2342 metric.cognitive,
2343 @r###"
2344 {
2345 "sum": 4.0,
2346 "average": 4.0,
2347 "min": 0.0,
2348 "max": 4.0
2349 }"###
2350 );
2351 },
2352 );
2353 }
2354
2355 #[test]
2356 fn tsx_boolean_sequence_across_else_if() {
2357 check_metrics::<TsxParser>(
2358 "function f(a, b, c, d) {
2359 if (a && b) { // +2 (+1 if, +1 &&)
2360 } else if (c && d) { // +2 (+1 else, +1 &&)
2361 }
2362 }",
2363 "foo.tsx",
2364 |metric| {
2365 insta::assert_json_snapshot!(
2366 metric.cognitive,
2367 @r###"
2368 {
2369 "sum": 4.0,
2370 "average": 4.0,
2371 "min": 0.0,
2372 "max": 4.0
2373 }"###
2374 );
2375 },
2376 );
2377 }
2378
2379 #[test]
2380 fn python_boolean_sequence_across_else_if() {
2381 check_metrics::<PythonParser>(
2393 "def f(a, b, c, d):
2394 if a and b: # +2 (+1 if, +1 and)
2395 pass
2396 else: # +1 else
2397 return c and d # +1 and",
2398 "foo.py",
2399 |metric| {
2400 insta::assert_json_snapshot!(
2401 metric.cognitive,
2402 @r###"
2403 {
2404 "sum": 4.0,
2405 "average": 4.0,
2406 "min": 0.0,
2407 "max": 4.0
2408 }"###
2409 );
2410 },
2411 );
2412 }
2413
2414 #[test]
2415 fn python_except_nested_in_if() {
2416 check_metrics::<PythonParser>(
2420 "def f(a):
2421 if a: # +1
2422 try:
2423 g()
2424 except Exception: # +2 (+1 base, +1 nesting)
2425 h()",
2426 "foo.py",
2427 |metric| {
2428 insta::assert_json_snapshot!(
2429 metric.cognitive,
2430 @r###"
2431 {
2432 "sum": 3.0,
2433 "average": 3.0,
2434 "min": 0.0,
2435 "max": 3.0
2436 }"###
2437 );
2438 },
2439 );
2440 }
2441
2442 #[test]
2443 fn python_except_not_inflated_by_sibling_nesting() {
2444 check_metrics::<PythonParser>(
2447 "def f():
2448 try:
2449 for i in x: # +1
2450 for j in y: # +2 (nesting = 1)
2451 g()
2452 except Exception: # +1 (base only, nesting = 0)
2453 h()",
2454 "foo.py",
2455 |metric| {
2456 insta::assert_json_snapshot!(
2457 metric.cognitive,
2458 @r###"
2459 {
2460 "sum": 4.0,
2461 "average": 4.0,
2462 "min": 0.0,
2463 "max": 4.0
2464 }"###
2465 );
2466 },
2467 );
2468 }
2469}