1use fxhash::FxHashMap;
2use serde::ser::{SerializeStruct, Serializer};
3use serde::Serialize;
4use std::fmt;
5
6use crate::checker::Checker;
7use crate::*;
8
9#[derive(Debug, Clone)]
20pub struct Stats {
21 structural: usize,
22 structural_sum: usize,
23 structural_min: usize,
24 structural_max: usize,
25 nesting: usize,
26 total_space_functions: usize,
27 boolean_seq: BoolSequence,
28}
29
30impl Default for Stats {
31 fn default() -> Self {
32 Self {
33 structural: 0,
34 structural_sum: 0,
35 structural_min: usize::MAX,
36 structural_max: 0,
37 nesting: 0,
38 total_space_functions: 1,
39 boolean_seq: BoolSequence::default(),
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("cognitive", 4)?;
50 st.serialize_field("sum", &self.cognitive_sum())?;
51 st.serialize_field("average", &self.cognitive_average())?;
52 st.serialize_field("min", &self.cognitive_min())?;
53 st.serialize_field("max", &self.cognitive_max())?;
54 st.end()
55 }
56}
57
58impl fmt::Display for Stats {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 write!(
61 f,
62 "sum: {}, average: {}, min:{}, max: {}",
63 self.cognitive(),
64 self.cognitive_average(),
65 self.cognitive_min(),
66 self.cognitive_max()
67 )
68 }
69}
70
71impl Stats {
72 pub fn merge(&mut self, other: &Stats) {
74 self.structural_min = self.structural_min.min(other.structural_min);
75 self.structural_max = self.structural_max.max(other.structural_max);
76 self.structural_sum += other.structural_sum;
77 }
78
79 pub fn cognitive(&self) -> f64 {
81 self.structural as f64
82 }
83 pub fn cognitive_sum(&self) -> f64 {
85 self.structural_sum as f64
86 }
87
88 pub fn cognitive_min(&self) -> f64 {
90 self.structural_min as f64
91 }
92 pub fn cognitive_max(&self) -> f64 {
94 self.structural_max as f64
95 }
96
97 pub fn cognitive_average(&self) -> f64 {
104 self.cognitive_sum() / self.total_space_functions as f64
105 }
106 #[inline(always)]
107 pub(crate) fn compute_sum(&mut self) {
108 self.structural_sum += self.structural;
109 }
110 #[inline(always)]
111 pub(crate) fn compute_minmax(&mut self) {
112 self.structural_min = self.structural_min.min(self.structural);
113 self.structural_max = self.structural_max.max(self.structural);
114 self.compute_sum();
115 }
116
117 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
118 self.total_space_functions = total_space_functions;
119 }
120}
121
122#[doc(hidden)]
123pub trait Cognitive
124where
125 Self: Checker,
126{
127 fn compute(
128 _node: &Node,
129 _stats: &mut Stats,
130 _nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>,
131 ) {
132 }
133}
134
135fn compute_booleans<T: std::cmp::PartialEq + std::convert::From<u16>>(
136 node: &Node,
137 stats: &mut Stats,
138 typs1: T,
139 typs2: T,
140) {
141 let mut cursor = node.object().walk();
142 for child in node.object().children(&mut cursor) {
143 if typs1 == child.kind_id().into() || typs2 == child.kind_id().into() {
144 stats.structural = stats
145 .boolean_seq
146 .eval_based_on_prev(child.kind_id(), stats.structural)
147 }
148 }
149}
150
151#[derive(Debug, Default, Clone)]
152struct BoolSequence {
153 boolean_op: Option<u16>,
154}
155
156impl BoolSequence {
157 fn reset(&mut self) {
158 self.boolean_op = None;
159 }
160
161 fn not_operator(&mut self, not_id: u16) {
162 self.boolean_op = Some(not_id);
163 }
164
165 fn eval_based_on_prev(&mut self, bool_id: u16, structural: usize) -> usize {
166 if let Some(prev) = self.boolean_op {
167 if prev != bool_id {
168 structural + 1
171 } else {
172 structural
175 }
176 } else {
177 self.boolean_op = Some(bool_id);
180 structural + 1
181 }
182 }
183}
184
185#[inline(always)]
186fn increment(stats: &mut Stats) {
187 stats.structural += stats.nesting + 1;
188}
189
190#[inline(always)]
191fn increment_by_one(stats: &mut Stats) {
192 stats.structural += 1;
193}
194
195fn get_nesting_from_map(
196 node: &Node,
197 nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>,
198) -> (usize, usize, usize) {
199 if let Some(parent) = node.object().parent() {
200 if let Some(n) = nesting_map.get(&parent.id()) {
201 *n
202 } else {
203 (0, 0, 0)
204 }
205 } else {
206 (0, 0, 0)
207 }
208}
209
210fn increment_function_depth<T: std::cmp::PartialEq + std::convert::From<u16>>(
211 depth: &mut usize,
212 node: &Node,
213 stop: T,
214) {
215 let mut child = node.object();
217 while let Some(parent) = child.parent() {
218 if stop == parent.kind_id().into() {
219 *depth += 1;
220 break;
221 }
222 child = parent;
223 }
224}
225
226#[inline(always)]
227fn increase_nesting(stats: &mut Stats, nesting: &mut usize, depth: usize, lambda: usize) {
228 stats.nesting = *nesting + depth + lambda;
229 increment(stats);
230 *nesting += 1;
231 stats.boolean_seq.reset();
232}
233
234impl Cognitive for PythonCode {
235 fn compute(
236 node: &Node,
237 stats: &mut Stats,
238 nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>,
239 ) {
240 use Python::*;
241
242 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
244
245 match node.object().kind_id().into() {
246 IfStatement | ForStatement | WhileStatement | ConditionalExpression => {
247 increase_nesting(stats, &mut nesting, depth, lambda);
248 }
249 ElifClause => {
250 increment_by_one(stats);
253 stats.boolean_seq.reset();
255 }
256 ElseClause | FinallyClause => {
257 increment_by_one(stats);
260 }
261 ExceptClause => {
262 nesting += 1;
263 increment(stats);
264 }
265 ExpressionList | ExpressionStatement | Tuple => {
266 stats.boolean_seq.reset();
267 }
268 NotOperator => {
269 stats.boolean_seq.not_operator(node.object().kind_id());
270 }
271 BooleanOperator => {
272 if count_specific_ancestors!(node, BooleanOperator, Lambda) == 0 {
273 stats.structural += count_specific_ancestors!(
274 node,
275 Lambda,
276 ExpressionList | IfStatement | ForStatement | WhileStatement
277 );
278 }
279 compute_booleans::<language_python::Python>(node, stats, And, Or);
280 }
281 Lambda => {
282 lambda += 1;
284 }
285 FunctionDefinition => {
286 increment_function_depth::<language_python::Python>(
288 &mut depth,
289 node,
290 FunctionDefinition,
291 );
292 }
293 _ => {}
294 }
295 nesting_map.insert(node.object().id(), (nesting, depth, lambda));
297 }
298}
299
300impl Cognitive for RustCode {
301 fn compute(
302 node: &Node,
303 stats: &mut Stats,
304 nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>,
305 ) {
306 use Rust::*;
307 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
309
310 match node.object().kind_id().into() {
311 IfExpression => {
312 if !Self::is_else_if(node) {
314 increase_nesting(stats,&mut nesting, depth, lambda);
315 }
316 }
317 ForExpression | WhileExpression | MatchExpression => {
318 increase_nesting(stats,&mut nesting, depth, lambda);
319 }
320 Else => {
321 increment_by_one(stats);
322 }
323 BreakExpression | ContinueExpression => {
324 if let Some(label_child) = node.object().child(1) {
325 if let LoopLabel = label_child.kind_id().into() {
326 increment_by_one(stats);
327 }
328 }
329 }
330 UnaryExpression => {
331 stats.boolean_seq.not_operator(node.object().kind_id());
332 }
333 BinaryExpression => {
334 compute_booleans::<language_rust::Rust>(node, stats, AMPAMP, PIPEPIPE);
335 }
336 FunctionItem => {
337 nesting = 0;
338 increment_function_depth::<language_rust::Rust>(&mut depth, node, FunctionItem);
340 }
341 ClosureExpression => {
342 lambda += 1;
343 }
344 _ => {}
345 }
346 nesting_map.insert(node.object().id(), (nesting, depth, lambda));
347 }
348}
349
350impl Cognitive for CppCode {
351 fn compute(
352 node: &Node,
353 stats: &mut Stats,
354 nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>,
355 ) {
356 use Cpp::*;
357
358 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
360
361 match node.object().kind_id().into() {
362 IfStatement => {
363 if !Self::is_else_if(node) {
364 increase_nesting(stats,&mut nesting, depth, lambda);
365 }
366 }
367 ForStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause => {
368 increase_nesting(stats,&mut nesting, depth, lambda);
369 }
370 GotoStatement | Else => {
371 increment_by_one(stats);
372 }
373 UnaryExpression2 => {
374 stats.boolean_seq.not_operator(node.object().kind_id());
375 }
376 BinaryExpression2 => {
377 compute_booleans::<language_cpp::Cpp>(node, stats, AMPAMP, PIPEPIPE);
378 }
379 LambdaExpression => {
380 lambda += 1;
381 }
382 _ => {}
383 }
384 nesting_map.insert(node.object().id(), (nesting, depth, lambda));
385 }
386}
387
388macro_rules! js_cognitive {
389 ($lang:ident) => {
390 fn compute(node: &Node, stats: &mut Stats, nesting_map: &mut FxHashMap<usize, (usize, usize, usize)>) {
391 use $lang::*;
392 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
393
394 match node.object().kind_id().into() {
395 IfStatement => {
396 if !Self::is_else_if(&node) {
397 increase_nesting(stats,&mut nesting, depth, lambda);
398 }
399 }
400 ForStatement | ForInStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause | TernaryExpression => {
401 increase_nesting(stats,&mut nesting, depth, lambda);
402 }
403 Else => {
404 increment_by_one(stats);
405 }
406 ExpressionStatement => {
407 stats.boolean_seq.reset();
409 }
410 UnaryExpression => {
411 stats.boolean_seq.not_operator(node.object().kind_id());
412 }
413 BinaryExpression => {
414 compute_booleans::<$lang>(node, stats, AMPAMP, PIPEPIPE);
415 }
416 FunctionDeclaration => {
417 nesting = 0;
419 lambda = 0;
420 increment_function_depth::<$lang>(&mut depth, node, FunctionDeclaration);
422 }
423 ArrowFunction => {
424 lambda += 1;
425 }
426 _ => {}
427 }
428 nesting_map.insert(node.object().id(), (nesting, depth, lambda));
429 }
430 };
431}
432
433impl Cognitive for MozjsCode {
434 js_cognitive!(Mozjs);
435}
436
437impl Cognitive for JavascriptCode {
438 js_cognitive!(Javascript);
439}
440
441impl Cognitive for TypescriptCode {
442 js_cognitive!(Typescript);
443}
444
445impl Cognitive for TsxCode {
446 js_cognitive!(Tsx);
447}
448
449impl Cognitive for PreprocCode {}
450impl Cognitive for CcommentCode {}
451impl Cognitive for JavaCode {}
452
453#[cfg(test)]
454mod tests {
455 use std::path::PathBuf;
456
457 use super::*;
458
459 #[test]
460 fn python_no_cognitive() {
461 check_metrics!(
462 "a = 42",
463 "foo.py",
464 PythonParser,
465 cognitive,
466 [
467 (cognitive_sum, 0, usize),
468 (cognitive_min, 0, usize),
469 (cognitive_max, 0, usize)
470 ],
471 [(cognitive_average, f64::NAN)]
472 );
473 }
474
475 #[test]
476 fn rust_no_cognitive() {
477 check_metrics!(
478 "let a = 42;",
479 "foo.rs",
480 RustParser,
481 cognitive,
482 [
483 (cognitive_sum, 0, usize),
484 (cognitive_min, 0, usize),
485 (cognitive_max, 0, usize)
486 ],
487 [(cognitive_average, f64::NAN)]
488 );
489 }
490
491 #[test]
492 fn c_no_cognitive() {
493 check_metrics!(
494 "int a = 42;",
495 "foo.c",
496 CppParser,
497 cognitive,
498 [
499 (cognitive_sum, 0, usize),
500 (cognitive_min, 0, usize),
501 (cognitive_max, 0, usize)
502 ],
503 [(cognitive_average, f64::NAN)]
504 );
505 }
506
507 #[test]
508 fn mozjs_no_cognitive() {
509 check_metrics!(
510 "var a = 42;",
511 "foo.js",
512 MozjsParser,
513 cognitive,
514 [
515 (cognitive_sum, 0, usize),
516 (cognitive_min, 0, usize),
517 (cognitive_max, 0, usize)
518 ],
519 [(cognitive_average, f64::NAN)]
520 );
521 }
522
523 #[test]
524 fn python_simple_function() {
525 check_metrics!(
526 "def f(a, b):
527 if a and b: # +2 (+1 and)
528 return 1
529 if c and d: # +2 (+1 and)
530 return 1",
531 "foo.py",
532 PythonParser,
533 cognitive,
534 [
535 (cognitive_sum, 4, usize),
536 (cognitive_min, 0, usize),
537 (cognitive_max, 4, usize)
538 ],
539 [(cognitive_average, 4.0)]
540 );
541 }
542
543 #[test]
544 fn python_expression_statement() {
545 check_metrics!(
548 "def f(a, b):
549 c = True and True",
550 "foo.py",
551 PythonParser,
552 cognitive,
553 [
554 (cognitive_sum, 1, usize),
555 (cognitive_min, 0, usize),
556 (cognitive_max, 1, usize)
557 ],
558 [(cognitive_average, 1.0)]
559 );
560 }
561
562 #[test]
563 fn python_tuple() {
564 check_metrics!(
567 "def f(a, b):
568 return \"%s%s\" % (a and \"Get\" or \"Set\", b)",
569 "foo.py",
570 PythonParser,
571 cognitive,
572 [
573 (cognitive_sum, 2, usize),
574 (cognitive_min, 0, usize),
575 (cognitive_max, 2, usize)
576 ],
577 [(cognitive_average, 2.0)]
578 );
579 }
580
581 #[test]
582 fn python_elif_function() {
583 check_metrics!(
586 "def f(a, b):
587 if a and b: # +2 (+1 and)
588 return 1
589 elif c and d: # +2 (+1 and)
590 return 1",
591 "foo.py",
592 PythonParser,
593 cognitive,
594 [
595 (cognitive_sum, 4, usize),
596 (cognitive_min, 0, usize),
597 (cognitive_max, 4, usize)
598 ],
599 [(cognitive_average, 4.0)]
600 );
601 }
602
603 #[test]
604 fn python_more_elifs_function() {
605 check_metrics!(
608 "def f(a, b):
609 if a and b: # +2 (+1 and)
610 return 1
611 elif c and d: # +2 (+1 and)
612 return 1
613 elif e and f: # +2 (+1 and)
614 return 1",
615 "foo.py",
616 PythonParser,
617 cognitive,
618 [
619 (cognitive_sum, 6, usize),
620 (cognitive_min, 0, usize),
621 (cognitive_max, 6, usize)
622 ],
623 [(cognitive_average, 6.0)]
624 );
625 }
626
627 #[test]
628 fn rust_simple_function() {
629 check_metrics!(
630 "fn f() {
631 if a && b { // +2 (+1 &&)
632 println!(\"test\");
633 }
634 if c && d { // +2 (+1 &&)
635 println!(\"test\");
636 }
637 }",
638 "foo.rs",
639 RustParser,
640 cognitive,
641 [
642 (cognitive_sum, 4, usize),
643 (cognitive_min, 0, usize),
644 (cognitive_max, 4, usize)
645 ],
646 [(cognitive_average, 4.0)]
647 );
648 }
649
650 #[test]
651 fn c_simple_function() {
652 check_metrics!(
653 "void f() {
654 if (a && b) { // +2 (+1 &&)
655 printf(\"test\");
656 }
657 if (c && d) { // +2 (+1 &&)
658 printf(\"test\");
659 }
660 }",
661 "foo.c",
662 CppParser,
663 cognitive,
664 [
665 (cognitive_sum, 4, usize),
666 (cognitive_min, 0, usize),
667 (cognitive_max, 4, usize)
668 ],
669 [(cognitive_average, 4.0)]
670 );
671 }
672
673 #[test]
674 fn mozjs_simple_function() {
675 check_metrics!(
676 "function f() {
677 if (a && b) { // +2 (+1 &&)
678 window.print(\"test\");
679 }
680 if (c && d) { // +2 (+1 &&)
681 window.print(\"test\");
682 }
683 }",
684 "foo.js",
685 MozjsParser,
686 cognitive,
687 [
688 (cognitive_sum, 4, usize),
689 (cognitive_min, 0, usize),
690 (cognitive_max, 4, usize)
691 ],
692 [(cognitive_average, 4.0)]
693 );
694 }
695
696 #[test]
697 fn python_sequence_same_booleans() {
698 check_metrics!(
699 "def f(a, b):
700 if a and b and True: # +2 (+1 sequence of and)
701 return 1",
702 "foo.py",
703 PythonParser,
704 cognitive,
705 [
706 (cognitive_sum, 2, usize),
707 (cognitive_min, 0, usize),
708 (cognitive_max, 2, usize)
709 ],
710 [(cognitive_average, 2.0)]
711 );
712 }
713
714 #[test]
715 fn rust_sequence_same_booleans() {
716 check_metrics!(
717 "fn f() {
718 if a && b && true { // +2 (+1 sequence of &&)
719 println!(\"test\");
720 }
721 }",
722 "foo.rs",
723 RustParser,
724 cognitive,
725 [
726 (cognitive_sum, 2, usize),
727 (cognitive_min, 0, usize),
728 (cognitive_max, 2, usize)
729 ],
730 [(cognitive_average, 2.0)]
731 );
732
733 check_metrics!(
734 "fn f() {
735 if a || b || c || d { // +2 (+1 sequence of ||)
736 println!(\"test\");
737 }
738 }",
739 "foo.rs",
740 RustParser,
741 cognitive,
742 [
743 (cognitive_sum, 2, usize),
744 (cognitive_min, 0, usize),
745 (cognitive_max, 2, usize)
746 ],
747 [(cognitive_average, 2.0)]
748 );
749 }
750
751 #[test]
752 fn c_sequence_same_booleans() {
753 check_metrics!(
754 "void f() {
755 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
756 printf(\"test\");
757 }
758 }",
759 "foo.c",
760 CppParser,
761 cognitive,
762 [
763 (cognitive_sum, 2, usize),
764 (cognitive_min, 0, usize),
765 (cognitive_max, 2, usize)
766 ],
767 [(cognitive_average, 2.0)]
768 );
769
770 check_metrics!(
771 "void f() {
772 if (a || b || c || d) { // +2 (+1 sequence of ||)
773 printf(\"test\");
774 }
775 }",
776 "foo.c",
777 CppParser,
778 cognitive,
779 [
780 (cognitive_sum, 2, usize),
781 (cognitive_min, 0, usize),
782 (cognitive_max, 2, usize)
783 ],
784 [(cognitive_average, 2.0)]
785 );
786 }
787
788 #[test]
789 fn mozjs_sequence_same_booleans() {
790 check_metrics!(
791 "function f() {
792 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
793 window.print(\"test\");
794 }
795 }",
796 "foo.js",
797 MozjsParser,
798 cognitive,
799 [
800 (cognitive_sum, 2, usize),
801 (cognitive_min, 0, usize),
802 (cognitive_max, 2, usize)
803 ],
804 [(cognitive_average, 2.0)]
805 );
806
807 check_metrics!(
808 "function f() {
809 if (a || b || c || d) { // +2 (+1 sequence of ||)
810 window.print(\"test\");
811 }
812 }",
813 "foo.js",
814 MozjsParser,
815 cognitive,
816 [
817 (cognitive_sum, 2, usize),
818 (cognitive_min, 0, usize),
819 (cognitive_max, 2, usize)
820 ],
821 [(cognitive_average, 2.0)]
822 );
823 }
824
825 #[test]
826 fn rust_not_booleans() {
827 check_metrics!(
828 "fn f() {
829 if !a && !b { // +2 (+1 &&)
830 println!(\"test\");
831 }
832 }",
833 "foo.rs",
834 RustParser,
835 cognitive,
836 [
837 (cognitive_sum, 2, usize),
838 (cognitive_min, 0, usize),
839 (cognitive_max, 2, usize)
840 ],
841 [(cognitive_average, 2.0)]
842 );
843
844 check_metrics!(
845 "fn f() {
846 if a && !(b && c) { // +3 (+1 &&, +1 &&)
847 println!(\"test\");
848 }
849 }",
850 "foo.rs",
851 RustParser,
852 cognitive,
853 [
854 (cognitive_sum, 3, usize),
855 (cognitive_min, 0, usize),
856 (cognitive_max, 3, usize)
857 ],
858 [(cognitive_average, 3.0)]
859 );
860
861 check_metrics!(
862 "fn f() {
863 if !(a || b) && !(c || d) { // +4 (+1 ||, +1 &&, +1 ||)
864 println!(\"test\");
865 }
866 }",
867 "foo.rs",
868 RustParser,
869 cognitive,
870 [
871 (cognitive_sum, 4, usize),
872 (cognitive_min, 0, usize),
873 (cognitive_max, 4, usize)
874 ],
875 [(cognitive_average, 4.0)]
876 );
877 }
878
879 #[test]
880 fn c_not_booleans() {
881 check_metrics!(
882 "void f() {
883 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
884 printf(\"test\");
885 }
886 }",
887 "foo.c",
888 CppParser,
889 cognitive,
890 [
891 (cognitive_sum, 3, usize),
892 (cognitive_min, 0, usize),
893 (cognitive_max, 3, usize)
894 ],
895 [(cognitive_average, 3.0)]
896 );
897
898 check_metrics!(
899 "void f() {
900 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
901 printf(\"test\");
902 }
903 }",
904 "foo.c",
905 CppParser,
906 cognitive,
907 [
908 (cognitive_sum, 4, usize),
909 (cognitive_min, 0, usize),
910 (cognitive_max, 4, usize)
911 ],
912 [(cognitive_average, 4.0)]
913 );
914 }
915
916 #[test]
917 fn mozjs_not_booleans() {
918 check_metrics!(
919 "function f() {
920 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
921 window.print(\"test\");
922 }
923 }",
924 "foo.js",
925 MozjsParser,
926 cognitive,
927 [
928 (cognitive_sum, 3, usize),
929 (cognitive_min, 0, usize),
930 (cognitive_max, 3, usize)
931 ],
932 [(cognitive_average, 3.0)]
933 );
934
935 check_metrics!(
936 "function f() {
937 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
938 window.print(\"test\");
939 }
940 }",
941 "foo.js",
942 MozjsParser,
943 cognitive,
944 [
945 (cognitive_sum, 4, usize),
946 (cognitive_min, 0, usize),
947 (cognitive_max, 4, usize)
948 ],
949 [(cognitive_average, 4.0)]
950 );
951 }
952
953 #[test]
954 fn python_sequence_different_booleans() {
955 check_metrics!(
956 "def f(a, b):
957 if a and b or True: # +3 (+1 and, +1 or)
958 return 1",
959 "foo.py",
960 PythonParser,
961 cognitive,
962 [
963 (cognitive_sum, 3, usize),
964 (cognitive_min, 0, usize),
965 (cognitive_max, 3, usize)
966 ],
967 [(cognitive_average, 3.0)]
968 );
969 }
970
971 #[test]
972 fn rust_sequence_different_booleans() {
973 check_metrics!(
974 "fn f() {
975 if a && b || true { // +3 (+1 &&, +1 ||)
976 println!(\"test\");
977 }
978 }",
979 "foo.rs",
980 RustParser,
981 cognitive,
982 [
983 (cognitive_sum, 3, usize),
984 (cognitive_min, 0, usize),
985 (cognitive_max, 3, usize)
986 ],
987 [(cognitive_average, 3.0)]
988 );
989 }
990
991 #[test]
992 fn c_sequence_different_booleans() {
993 check_metrics!(
994 "void f() {
995 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
996 printf(\"test\");
997 }
998 }",
999 "foo.c",
1000 CppParser,
1001 cognitive,
1002 [
1003 (cognitive_sum, 3, usize),
1004 (cognitive_min, 0, usize),
1005 (cognitive_max, 3, usize)
1006 ],
1007 [(cognitive_average, 3.0)]
1008 );
1009 }
1010
1011 #[test]
1012 fn mozjs_sequence_different_booleans() {
1013 check_metrics!(
1014 "function f() {
1015 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
1016 window.print(\"test\");
1017 }
1018 }",
1019 "foo.js",
1020 MozjsParser,
1021 cognitive,
1022 [
1023 (cognitive_sum, 3, usize),
1024 (cognitive_min, 0, usize),
1025 (cognitive_max, 3, usize)
1026 ],
1027 [(cognitive_average, 3.0)]
1028 );
1029 }
1030
1031 #[test]
1032 fn python_formatted_sequence_different_booleans() {
1033 check_metrics!(
1034 "def f(a, b):
1035 if ( # +1
1036 a and b and # +1
1037 (c or d) # +1
1038 ):
1039 return 1",
1040 "foo.py",
1041 PythonParser,
1042 cognitive,
1043 [
1044 (cognitive_sum, 3, usize),
1045 (cognitive_min, 0, usize),
1046 (cognitive_max, 3, usize)
1047 ],
1048 [(cognitive_average, 3.0)]
1049 );
1050 }
1051
1052 #[test]
1053 fn python_1_level_nesting() {
1054 check_metrics!(
1055 "def f(a, b):
1056 if a: # +1
1057 for i in range(b): # +2
1058 return 1",
1059 "foo.py",
1060 PythonParser,
1061 cognitive,
1062 [
1063 (cognitive_sum, 3, usize),
1064 (cognitive_min, 0, usize),
1065 (cognitive_max, 3, usize)
1066 ],
1067 [(cognitive_average, 3.0)]
1068 );
1069 }
1070
1071 #[test]
1072 fn rust_1_level_nesting() {
1073 check_metrics!(
1074 "fn f() {
1075 if true { // +1
1076 if true { // +2 (nesting = 1)
1077 println!(\"test\");
1078 } else if 1 == 1 { // +1
1079 if true { // +3 (nesting = 2)
1080 println!(\"test\");
1081 }
1082 } else { // +1
1083 if true { // +3 (nesting = 2)
1084 println!(\"test\");
1085 }
1086 }
1087 }
1088 }",
1089 "foo.rs",
1090 RustParser,
1091 cognitive,
1092 [
1093 (cognitive_sum, 11, usize),
1094 (cognitive_min, 0, usize),
1095 (cognitive_max, 11, usize)
1096 ],
1097 [(cognitive_average, 11.0)]
1098 );
1099
1100 check_metrics!(
1101 "fn f() {
1102 if true { // +1
1103 match true { // +2 (nesting = 1)
1104 true => println!(\"test\"),
1105 false => println!(\"test\"),
1106 }
1107 }
1108 }",
1109 "foo.rs",
1110 RustParser,
1111 cognitive,
1112 [
1113 (cognitive_sum, 3, usize),
1114 (cognitive_min, 0, usize),
1115 (cognitive_max, 3, usize)
1116 ],
1117 [(cognitive_average, 3.0)]
1118 );
1119 }
1120
1121 #[test]
1122 fn c_1_level_nesting() {
1123 check_metrics!(
1124 "void f() {
1125 if (1 == 1) { // +1
1126 if (1 == 1) { // +2 (nesting = 1)
1127 printf(\"test\");
1128 } else if (1 == 1) { // +1
1129 if (1 == 1) { // +3 (nesting = 2)
1130 printf(\"test\");
1131 }
1132 } else { // +1
1133 if (1 == 1) { // +3 (nesting = 2)
1134 printf(\"test\");
1135 }
1136 }
1137 }
1138 }",
1139 "foo.c",
1140 CppParser,
1141 cognitive,
1142 [
1143 (cognitive_sum, 11, usize),
1144 (cognitive_min, 0, usize),
1145 (cognitive_max, 11, usize)
1146 ],
1147 [(cognitive_average, 11.0)]
1148 );
1149 }
1150
1151 #[test]
1152 fn mozjs_1_level_nesting() {
1153 check_metrics!(
1154 "function f() {
1155 if (1 == 1) { // +1
1156 if (1 == 1) { // +2 (nesting = 1)
1157 window.print(\"test\");
1158 } else if (1 == 1) { // +1
1159 if (1 == 1) { // +3 (nesting = 2)
1160 window.print(\"test\");
1161 }
1162 } else { // +1
1163 if (1 == 1) { // +3 (nesting = 2)
1164 window.print(\"test\");
1165 }
1166 }
1167 }
1168 }",
1169 "foo.js",
1170 MozjsParser,
1171 cognitive,
1172 [
1173 (cognitive_sum, 11, usize),
1174 (cognitive_min, 0, usize),
1175 (cognitive_max, 11, usize)
1176 ],
1177 [(cognitive_average, 11.0)]
1178 );
1179 }
1180
1181 #[test]
1182 fn python_2_level_nesting() {
1183 check_metrics!(
1184 "def f(a, b):
1185 if a: # +1
1186 for i in range(b): # +2
1187 if b: # +3
1188 return 1",
1189 "foo.py",
1190 PythonParser,
1191 cognitive,
1192 [
1193 (cognitive_sum, 6, usize),
1194 (cognitive_min, 0, usize),
1195 (cognitive_max, 6, usize)
1196 ],
1197 [(cognitive_average, 6.0)]
1198 );
1199 }
1200
1201 #[test]
1202 fn rust_2_level_nesting() {
1203 check_metrics!(
1204 "fn f() {
1205 if true { // +1
1206 for i in 0..4 { // +2 (nesting = 1)
1207 match true { // +3 (nesting = 2)
1208 true => println!(\"test\"),
1209 false => println!(\"test\"),
1210 }
1211 }
1212 }
1213 }",
1214 "foo.rs",
1215 RustParser,
1216 cognitive,
1217 [
1218 (cognitive_sum, 6, usize),
1219 (cognitive_min, 0, usize),
1220 (cognitive_max, 6, usize)
1221 ],
1222 [(cognitive_average, 6.0)]
1223 );
1224 }
1225
1226 #[test]
1227 fn python_try_construct() {
1228 check_metrics!(
1229 "def f(a, b):
1230 try:
1231 for foo in bar: # +1
1232 return a
1233 except Exception: # +1
1234 if a < 0: # +2
1235 return a",
1236 "foo.py",
1237 PythonParser,
1238 cognitive,
1239 [
1240 (cognitive_sum, 4, usize),
1241 (cognitive_min, 0, usize),
1242 (cognitive_max, 4, usize)
1243 ],
1244 [(cognitive_average, 4.0)]
1245 );
1246 }
1247
1248 #[test]
1249 fn mozjs_try_construct() {
1250 check_metrics!(
1251 "function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
1252 for (const collector of this.collectors) {
1253 try {
1254 collector._onChannelRedirect(oldChannel, newChannel, flags);
1255 } catch (ex) {
1256 console.error(
1257 \"StackTraceCollector.onChannelRedirect threw an exception\",
1258 ex
1259 );
1260 }
1261 }
1262 callback.onRedirectVerifyCallback(Cr.NS_OK);
1263 }",
1264 "foo.js",
1265 MozjsParser,
1266 cognitive,
1267 [
1268 (cognitive_sum, 3, usize),
1269 (cognitive_min, 0, usize),
1270 (cognitive_max, 3, usize)
1271 ],
1272 [(cognitive_average, 3.0)]
1273 );
1274 }
1275
1276 #[test]
1277 fn rust_break_continue() {
1278 check_metrics!(
1280 "fn f() {
1281 'tens: for ten in 0..3 { // +1
1282 '_units: for unit in 0..=9 { // +2 (nesting = 1)
1283 if unit % 2 == 0 { // +3 (nesting = 2)
1284 continue;
1285 } else if unit == 5 { // +1
1286 continue 'tens; // +1
1287 } else if unit == 6 { // +1
1288 break;
1289 } else { // +1
1290 break 'tens; // +1
1291 }
1292 }
1293 }
1294 }",
1295 "foo.rs",
1296 RustParser,
1297 cognitive,
1298 [
1299 (cognitive_sum, 11, usize),
1300 (cognitive_min, 0, usize),
1301 (cognitive_max, 11, usize)
1302 ],
1303 [(cognitive_average, 11.0)]
1304 );
1305 }
1306
1307 #[test]
1308 fn c_goto() {
1309 check_metrics!(
1310 "void f() {
1311 OUT: for (int i = 1; i <= max; ++i) { // +1
1312 for (int j = 2; j < i; ++j) { // +2 (nesting = 1)
1313 if (i % j == 0) { // +3 (nesting = 2)
1314 goto OUT; // +1
1315 }
1316 }
1317 }
1318 }",
1319 "foo.c",
1320 CppParser,
1321 cognitive,
1322 [
1323 (cognitive_sum, 7, usize),
1324 (cognitive_min, 0, usize),
1325 (cognitive_max, 7, usize)
1326 ],
1327 [(cognitive_average, 7.0)]
1328 );
1329 }
1330
1331 #[test]
1332 fn c_switch() {
1333 check_metrics!(
1334 "void f() {
1335 switch (1) { // +1
1336 case 1:
1337 printf(\"one\");
1338 break;
1339 case 2:
1340 printf(\"two\");
1341 break;
1342 case 3:
1343 printf(\"three\");
1344 break;
1345 default:
1346 printf(\"all\");
1347 break;
1348 }
1349 }",
1350 "foo.c",
1351 CppParser,
1352 cognitive,
1353 [
1354 (cognitive_sum, 1, usize),
1355 (cognitive_min, 0, usize),
1356 (cognitive_max, 1, usize)
1357 ],
1358 [(cognitive_average, 1.0)]
1359 );
1360 }
1361
1362 #[test]
1363 fn mozjs_switch() {
1364 check_metrics!(
1365 "function f() {
1366 switch (1) { // +1
1367 case 1:
1368 window.print(\"one\");
1369 break;
1370 case 2:
1371 window.print(\"two\");
1372 break;
1373 case 3:
1374 window.print(\"three\");
1375 break;
1376 default:
1377 window.print(\"all\");
1378 break;
1379 }
1380 }",
1381 "foo.js",
1382 MozjsParser,
1383 cognitive,
1384 [
1385 (cognitive_sum, 1, usize),
1386 (cognitive_min, 0, usize),
1387 (cognitive_max, 1, usize)
1388 ],
1389 [(cognitive_average, 1.0)]
1390 );
1391 }
1392
1393 #[test]
1394 fn python_ternary_operator() {
1395 check_metrics!(
1396 "def f(a, b):
1397 if a % 2: # +1
1398 return 'c' if a else 'd' # +2
1399 return 'a' if a else 'b' # +1",
1400 "foo.py",
1401 PythonParser,
1402 cognitive,
1403 [
1404 (cognitive_sum, 4, usize),
1405 (cognitive_min, 0, usize),
1406 (cognitive_max, 4, usize)
1407 ],
1408 [(cognitive_average, 4.0)]
1409 );
1410 }
1411
1412 #[test]
1413 fn python_nested_functions_lambdas() {
1414 check_metrics!(
1415 "def f(a, b):
1416 def foo(a):
1417 if a: # +2 (+1 nesting)
1418 return 1
1419 # +3 (+1 for boolean sequence +2 for lambda nesting)
1420 bar = lambda a: lambda b: b or True or True
1421 return bar(foo(a))(a)",
1422 "foo.py",
1423 PythonParser,
1424 cognitive,
1425 [
1426 (cognitive_sum, 5, usize),
1427 (cognitive_min, 0, usize),
1428 (cognitive_max, 3, usize)
1429 ],
1430 [(cognitive_average, 1.25)] );
1432 }
1433
1434 #[test]
1435 fn python_real_function() {
1436 check_metrics!(
1437 "def process_raw_constant(constant, min_word_length):
1438 processed_words = []
1439 raw_camelcase_words = []
1440 for raw_word in re.findall(r'[a-z]+', constant): # +1
1441 word = raw_word.strip()
1442 if ( # +2 (+1 if and +1 nesting)
1443 len(word) >= min_word_length
1444 and not (word.startswith('-') or word.endswith('-')) # +2 operators
1445 ):
1446 if is_camel_case_word(word): # +3 (+1 if and +2 nesting)
1447 raw_camelcase_words.append(word)
1448 else: # +1 else
1449 processed_words.append(word.lower())
1450 return processed_words, raw_camelcase_words",
1451 "foo.py",
1452 PythonParser,
1453 cognitive,
1454 [
1455 (cognitive_sum, 9, usize),
1456 (cognitive_min, 0, usize),
1457 (cognitive_max, 9, usize)
1458 ],
1459 [(cognitive_average, 9.0)]
1460 );
1461 }
1462
1463 #[test]
1464 fn rust_if_let_else_if_else() {
1465 check_metrics!(
1466 "pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
1467 debugln!(\"usage::create_usage_no_title;\");
1468 if let Some(u) = p.meta.usage_str { // +1
1469 String::from(&*u)
1470 } else if used.is_empty() { // +1
1471 create_help_usage(p, true)
1472 } else { // +1
1473 create_smart_usage(p, used)
1474 }
1475 }",
1476 "foo.rs",
1477 RustParser,
1478 cognitive,
1479 [
1480 (cognitive_sum, 3, usize),
1481 (cognitive_min, 0, usize),
1482 (cognitive_max, 3, usize)
1483 ],
1484 [(cognitive_average, 3.0)]
1485 );
1486 }
1487
1488 #[test]
1489 fn typescript_if_else_if_else() {
1490 check_metrics!(
1491 "function foo() {
1492 if (this._closed) return Promise.resolve(); // +1
1493 if (this._tempDirectory) { // +1
1494 this.kill();
1495 } else if (this.connection) { // +1
1496 this.kill();
1497 } else { // +1
1498 throw new Error(`Error`);
1499 }
1500 helper.removeEventListeners(this._listeners);
1501 return this._processClosing;
1502 }",
1503 "foo.ts",
1504 TypescriptParser,
1505 cognitive,
1506 [
1507 (cognitive_sum, 4, usize),
1508 (cognitive_min, 0, usize),
1509 (cognitive_max, 4, usize)
1510 ],
1511 [(cognitive_average, 4.0)]
1512 );
1513 }
1514}