1#![allow(
8 clippy::enum_glob_use,
9 clippy::match_same_arms,
10 clippy::needless_pass_by_value,
11 clippy::wildcard_imports
12)]
13#![allow(
20 clippy::cast_precision_loss,
21 clippy::cast_possible_truncation,
22 clippy::cast_sign_loss
23)]
24
25use std::collections::HashMap;
26
27use serde::Serialize;
28use serde::ser::{SerializeStruct, Serializer};
29use std::fmt;
30
31use crate::checker::Checker;
32use crate::macros::{csharp_prefix_unary_expr_kinds, implement_metric_trait};
33use crate::*;
34
35#[derive(Debug, Clone)]
46pub struct Stats {
47 structural: usize,
48 structural_sum: usize,
49 structural_min: usize,
50 structural_max: usize,
51 nesting: usize,
52 total_space_functions: usize,
53 boolean_seq: BoolSequence,
54}
55
56impl Default for Stats {
57 fn default() -> Self {
58 Self {
59 structural: 0,
60 structural_sum: 0,
61 structural_min: usize::MAX,
62 structural_max: 0,
63 nesting: 0,
64 total_space_functions: 1,
65 boolean_seq: BoolSequence::default(),
66 }
67 }
68}
69
70impl Serialize for Stats {
71 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72 where
73 S: Serializer,
74 {
75 let mut st = serializer.serialize_struct("cognitive", 4)?;
76 st.serialize_field("sum", &self.cognitive_sum())?;
77 st.serialize_field("average", &self.cognitive_average())?;
78 st.serialize_field("min", &self.cognitive_min())?;
79 st.serialize_field("max", &self.cognitive_max())?;
80 st.end()
81 }
82}
83
84impl fmt::Display for Stats {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 write!(
87 f,
88 "sum: {}, average: {}, min:{}, max: {}",
89 self.cognitive(),
90 self.cognitive_average(),
91 self.cognitive_min(),
92 self.cognitive_max()
93 )
94 }
95}
96
97impl Stats {
98 pub fn merge(&mut self, other: &Stats) {
100 self.structural_min = self.structural_min.min(other.structural_min);
101 self.structural_max = self.structural_max.max(other.structural_max);
102 self.structural_sum += other.structural_sum;
103 }
104
105 #[must_use]
107 pub fn cognitive(&self) -> f64 {
108 self.structural as f64
109 }
110 #[must_use]
112 pub fn cognitive_sum(&self) -> f64 {
113 self.structural_sum as f64
114 }
115
116 #[must_use]
122 pub fn cognitive_min(&self) -> f64 {
123 if self.structural_min == usize::MAX {
124 0.0
125 } else {
126 self.structural_min as f64
127 }
128 }
129 #[must_use]
131 pub fn cognitive_max(&self) -> f64 {
132 self.structural_max as f64
133 }
134
135 #[must_use]
142 pub fn cognitive_average(&self) -> f64 {
143 self.cognitive_sum() / self.total_space_functions as f64
144 }
145 #[inline]
146 pub(crate) fn compute_sum(&mut self) {
147 self.structural_sum += self.structural;
148 }
149 #[inline]
150 pub(crate) fn compute_minmax(&mut self) {
151 self.structural_min = self.structural_min.min(self.structural);
152 self.structural_max = self.structural_max.max(self.structural);
153 self.compute_sum();
154 }
155
156 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
157 self.total_space_functions = total_space_functions;
158 }
159}
160
161#[doc(hidden)]
162pub trait Cognitive
164where
165 Self: Checker,
166{
167 fn compute<'a>(
180 node: &Node<'a>,
181 code: &'a [u8],
182 stats: &mut Stats,
183 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
184 );
185}
186
187fn compute_booleans_with<F: Fn(u16) -> bool>(node: &Node, stats: &mut Stats, is_op: F) {
194 let enclosing_end = node.end_byte();
195 for child in node.children() {
196 let id = child.kind_id();
197 if is_op(id) {
198 stats.structural =
199 stats
200 .boolean_seq
201 .eval_based_on_prev(id, enclosing_end, stats.structural);
202 }
203 }
204}
205
206fn compute_booleans<T: PartialEq + From<u16>>(node: &Node, stats: &mut Stats, typs1: T, typs2: T) {
211 compute_booleans_with(node, stats, |id| {
212 let converted: T = id.into();
213 typs1 == converted || typs2 == converted
214 });
215}
216
217fn compute_ruby_booleans(node: &Node, stats: &mut Stats) {
221 compute_booleans_with(node, stats, |id| {
222 matches!(
223 id.into(),
224 Ruby::AMPAMP | Ruby::PIPEPIPE | Ruby::And | Ruby::Or
225 )
226 });
227}
228
229fn compute_perl_booleans(node: &Node, stats: &mut Stats) {
236 compute_booleans_with(node, stats, |id| {
237 matches!(
238 id.into(),
239 Perl::AMPAMP
240 | Perl::PIPEPIPE
241 | Perl::SLASHSLASH
242 | Perl::And
243 | Perl::Or
244 | Perl::AMPAMPEQ
245 | Perl::PIPEPIPEEQ
246 | Perl::SLASHSLASHEQ
247 )
248 });
249}
250
251fn compute_elixir_booleans(node: &Node, stats: &mut Stats) {
256 compute_booleans_with(node, stats, |id| {
257 matches!(
258 id.into(),
259 Elixir::AMPAMP | Elixir::PIPEPIPE | Elixir::And | Elixir::Or
260 )
261 });
262}
263
264#[derive(Debug, Default, Clone)]
265struct BoolSequence {
266 boolean_op: Option<(u16, usize)>,
267}
268
269impl BoolSequence {
270 fn reset(&mut self) {
271 self.boolean_op = None;
273 }
274
275 fn not_operator(&mut self) {
276 self.reset();
278 }
279
280 fn eval_based_on_prev(
281 &mut self,
282 bool_id: u16,
283 enclosing_end: usize,
284 structural: usize,
285 ) -> usize {
286 match self.boolean_op {
287 Some((prev_id, prev_end)) if prev_id == bool_id && enclosing_end <= prev_end => {
291 structural
292 }
293 _ => {
294 self.boolean_op = Some((bool_id, enclosing_end));
295 structural + 1
296 }
297 }
298 }
299}
300
301#[inline]
302fn increment(stats: &mut Stats) {
303 stats.structural += stats.nesting + 1;
304}
305
306#[inline]
307fn increment_by_one(stats: &mut Stats) {
308 stats.structural += 1;
309}
310
311#[inline]
312fn increment_branch_extension(stats: &mut Stats) {
313 stats.structural += 1;
314 stats.boolean_seq.reset();
315}
316
317fn get_nesting_from_map(
318 node: &Node,
319 nesting_map: &HashMap<usize, (usize, usize, usize)>,
320) -> (usize, usize, usize) {
321 node.parent()
322 .and_then(|parent| nesting_map.get(&parent.id()))
323 .copied()
324 .unwrap_or((0, 0, 0))
325}
326
327fn increment_function_depth<T: PartialEq + From<u16>>(depth: &mut usize, node: &Node, stops: &[T]) {
328 let mut child = *node;
329 while let Some(parent) = child.parent() {
330 if stops.contains(&T::from(parent.kind_id())) {
331 *depth += 1;
332 break;
333 }
334 child = parent;
335 }
336}
337
338#[inline]
339fn increase_nesting(stats: &mut Stats, nesting: &mut usize, depth: usize, lambda: usize) {
340 stats.nesting = *nesting + depth + lambda;
341 increment(stats);
342 *nesting += 1;
343 stats.boolean_seq.reset();
344}
345
346impl Cognitive for PythonCode {
347 fn compute<'a>(
348 node: &Node<'a>,
349 _code: &'a [u8],
350 stats: &mut Stats,
351 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
352 ) {
353 use Python::*;
354
355 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
357
358 match node.kind_id().into() {
359 IfStatement if !Self::is_else_if(node) => {
364 increase_nesting(stats, &mut nesting, depth, lambda);
365 }
366 ForStatement | WhileStatement | ConditionalExpression | MatchStatement => {
367 increase_nesting(stats, &mut nesting, depth, lambda);
368 }
369 ElifClause => {
370 increment_branch_extension(stats);
373 }
374 ElseClause | FinallyClause => {
375 increment_by_one(stats);
378 }
379 ExceptClause => {
380 increase_nesting(stats, &mut nesting, depth, lambda);
381 }
382 ExpressionList | ExpressionStatement | Tuple => {
383 stats.boolean_seq.reset();
384 }
385 NotOperator => {
386 stats.boolean_seq.not_operator();
387 }
388 BooleanOperator => {
389 if node.count_specific_ancestors::<PythonParser>(
390 |node| node.kind_id() == BooleanOperator,
391 |node| node.kind_id() == Lambda,
392 ) == 0
393 {
394 stats.structural += node.count_specific_ancestors::<PythonParser>(
395 |node| node.kind_id() == Lambda,
396 |node| {
397 matches!(
398 node.kind_id().into(),
399 ExpressionList | IfStatement | ForStatement | WhileStatement
400 )
401 },
402 );
403 }
404 compute_booleans(node, stats, And, Or);
405 }
406 Lambda => {
407 lambda += 1;
409 }
410 FunctionDefinition => {
411 increment_function_depth(&mut depth, node, &[FunctionDefinition]);
413 }
414 _ => {}
415 }
416 nesting_map.insert(node.id(), (nesting, depth, lambda));
418 }
419}
420
421impl Cognitive for RustCode {
422 fn compute<'a>(
423 node: &Node<'a>,
424 _code: &'a [u8],
425 stats: &mut Stats,
426 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
427 ) {
428 use Rust::*;
429 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
431
432 match node.kind_id().into() {
433 IfExpression if !Self::is_else_if(node) => {
434 increase_nesting(stats, &mut nesting, depth, lambda);
435 }
436 ForExpression | WhileExpression | MatchExpression => {
437 increase_nesting(stats, &mut nesting, depth, lambda);
438 }
439 Else => {
440 increment_by_one(stats);
441 }
442 BreakExpression | ContinueExpression => {
443 if let Some(label_child) = node.child(1)
444 && let Label = label_child.kind_id().into()
445 {
446 increment_by_one(stats);
447 }
448 }
449 UnaryExpression => {
450 stats.boolean_seq.not_operator();
451 }
452 BinaryExpression => {
453 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
454 }
455 FunctionItem => {
456 nesting = 0;
457 increment_function_depth(&mut depth, node, &[FunctionItem]);
459 }
460 ClosureExpression => {
461 lambda += 1;
462 }
463 _ => {}
464 }
465 nesting_map.insert(node.id(), (nesting, depth, lambda));
466 }
467}
468
469impl Cognitive for CppCode {
470 fn compute<'a>(
471 node: &Node<'a>,
472 _code: &'a [u8],
473 stats: &mut Stats,
474 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
475 ) {
476 use Cpp::*;
477
478 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
480
481 match node.kind_id().into() {
482 IfStatement if !Self::is_else_if(node) => {
483 increase_nesting(stats, &mut nesting, depth, lambda);
484 }
485 ForStatement
486 | ForRangeLoop
487 | WhileStatement
488 | DoStatement
489 | SwitchStatement
490 | CatchClause
491 | ConditionalExpression => {
492 increase_nesting(stats, &mut nesting, depth, lambda);
493 }
494 GotoStatement | Else => {
495 increment_by_one(stats);
496 }
497 UnaryExpression2 => {
498 stats.boolean_seq.not_operator();
499 }
500 BinaryExpression2 => {
501 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
502 }
503 LambdaExpression => {
504 lambda += 1;
505 }
506 _ => {}
507 }
508 nesting_map.insert(node.id(), (nesting, depth, lambda));
509 }
510}
511
512macro_rules! js_cognitive {
513 ($lang:ident) => {
514 fn compute<'a>(
515 node: &Node<'a>,
516 _code: &'a [u8],
517 stats: &mut Stats,
518 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
519 ) {
520 use $lang::*;
521 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
522
523 match node.kind_id().into() {
524 IfStatement if !Self::is_else_if(node) => {
525 increase_nesting(stats, &mut nesting, depth, lambda);
526 }
527 ForStatement | ForInStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause | TernaryExpression => {
528 increase_nesting(stats, &mut nesting, depth, lambda);
529 }
530 Else => {
531 increment_by_one(stats);
532 }
533 ExpressionStatement => {
534 stats.boolean_seq.reset();
536 }
537 UnaryExpression => {
538 stats.boolean_seq.not_operator();
539 }
540 BinaryExpression => {
541 compute_booleans_with(node, stats, |id| {
545 matches!(id.into(), AMPAMP | PIPEPIPE | QMARKQMARK)
546 });
547 }
548 AugmentedAssignmentExpression => {
549 compute_booleans_with(node, stats, |id| {
556 matches!(id.into(), AMPAMPEQ | PIPEPIPEEQ | QMARKQMARKEQ)
557 });
558 }
559 FunctionDeclaration => {
560 nesting = 0;
562 lambda = 0;
563 increment_function_depth(&mut depth, node, &[FunctionDeclaration]);
565 }
566 ArrowFunction => {
567 lambda += 1;
568 }
569 _ => {}
570 }
571 nesting_map.insert(node.id(), (nesting, depth, lambda));
572 }
573 };
574}
575
576impl Cognitive for MozjsCode {
577 js_cognitive!(Mozjs);
578}
579
580impl Cognitive for JavascriptCode {
581 js_cognitive!(Javascript);
582}
583
584impl Cognitive for TypescriptCode {
585 js_cognitive!(Typescript);
586}
587
588impl Cognitive for TsxCode {
589 js_cognitive!(Tsx);
590}
591
592impl Cognitive for JavaCode {
593 fn compute<'a>(
594 node: &Node<'a>,
595 _code: &'a [u8],
596 stats: &mut Stats,
597 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
598 ) {
599 use Java::*;
600
601 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
602
603 match node.kind_id().into() {
604 IfStatement if !Self::is_else_if(node) => {
605 increase_nesting(stats, &mut nesting, depth, lambda);
606 }
607 ForStatement
608 | EnhancedForStatement
609 | WhileStatement
610 | DoStatement
611 | SwitchBlock
612 | CatchClause
613 | TernaryExpression => {
614 increase_nesting(stats, &mut nesting, depth, lambda);
615 }
616 Else => {
617 increment_by_one(stats);
618 }
619 BreakStatement | ContinueStatement
623 if node.is_child(Identifier as u16) =>
624 {
625 increment_by_one(stats);
626 }
627 UnaryExpression => {
628 stats.boolean_seq.not_operator();
629 }
630 BinaryExpression => {
631 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
632 }
633 LambdaExpression => {
634 lambda += 1;
635 }
636 _ => {}
637 }
638 nesting_map.insert(node.id(), (nesting, depth, lambda));
639 }
640}
641
642impl Cognitive for GroovyCode {
643 fn compute<'a>(
644 node: &Node<'a>,
645 _code: &'a [u8],
646 stats: &mut Stats,
647 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
648 ) {
649 use Groovy::*;
650
651 let (mut nesting, depth, lambda) = get_nesting_from_map(node, nesting_map);
652
653 match node.kind_id().into() {
654 IfStatement if !Self::is_else_if(node) => {
655 increase_nesting(stats, &mut nesting, depth, lambda);
656 }
657 ForStatement | ForInStatement | WhileStatement | DoWhileStatement | SwitchBlock
662 | CatchClause | TernaryExpression => {
663 increase_nesting(stats, &mut nesting, depth, lambda);
664 }
665 Else => {
670 increment_by_one(stats);
671 }
672 BreakStatement | ContinueStatement if node.is_child(Identifier as u16) => {
675 increment_by_one(stats);
676 }
677 UnaryExpression => {
678 stats.boolean_seq.not_operator();
679 }
680 BinaryExpression => {
681 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
682 }
683 ElvisExpression => {
692 compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKCOLON));
693 }
694 _ => {}
695 }
696 nesting_map.insert(node.id(), (nesting, depth, lambda));
697 }
698}
699
700impl Cognitive for CsharpCode {
701 fn compute<'a>(
702 node: &Node<'a>,
703 _code: &'a [u8],
704 stats: &mut Stats,
705 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
706 ) {
707 use Csharp::*;
708
709 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
710
711 match node.kind_id().into() {
712 IfStatement if !Self::is_else_if(node) => {
713 increase_nesting(stats, &mut nesting, depth, lambda);
714 }
715 ForStatement
716 | ForeachStatement
717 | WhileStatement
718 | DoStatement
719 | SwitchStatement
720 | SwitchExpression
721 | CatchClause
722 | ConditionalExpression => {
723 increase_nesting(stats, &mut nesting, depth, lambda);
724 }
725 Else => {
729 increment_by_one(stats);
730 }
731 GotoStatement => {
737 increment_by_one(stats);
738 }
739 csharp_prefix_unary_expr_kinds!() => {
743 stats.boolean_seq.not_operator();
744 }
745 BinaryExpression => {
746 compute_booleans_with(node, stats, |id| {
750 matches!(id.into(), AMPAMP | PIPEPIPE | QMARKQMARK)
751 });
752 }
753 AssignmentExpression => {
754 compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKQMARKEQ));
763 }
764 LambdaExpression | AnonymousMethodExpression => {
765 lambda += 1;
766 }
767 _ => {}
768 }
769 nesting_map.insert(node.id(), (nesting, depth, lambda));
770 }
771}
772
773impl Cognitive for PerlCode {
774 fn compute<'a>(
775 node: &Node<'a>,
776 _code: &'a [u8],
777 stats: &mut Stats,
778 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
779 ) {
780 use Perl as P;
781
782 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
783
784 match node.kind_id().into() {
785 P::IfStatement
790 | P::UnlessStatement
791 | P::WhileStatement
792 | P::UntilStatement
793 | P::ForStatement1
794 | P::ForStatement2
795 | P::TernaryExpression
796 | P::IfSimpleStatement
800 | P::UnlessSimpleStatement
801 | P::WhileSimpleStatement
802 | P::UntilSimpleStatement
803 | P::ForSimpleStatement => {
804 increase_nesting(stats, &mut nesting, depth, lambda);
805 }
806 P::Else | P::ElsifClause => {
808 increment_by_one(stats);
809 }
810 P::Goto | P::GotoExpression => {
812 increment_by_one(stats);
813 }
814 P::LoopControlStatement if node.is_child(P::Label as u16) => {
818 increment_by_one(stats);
819 }
820 P::UnaryExpression => {
821 stats.boolean_seq.not_operator();
822 }
823 P::BinaryExpression => {
824 compute_perl_booleans(node, stats);
825 }
826 P::FunctionDefinition | P::FunctionDefinitionWithoutSub => {
827 nesting = 0;
828 increment_function_depth(
829 &mut depth,
830 node,
831 &[P::FunctionDefinition, P::FunctionDefinitionWithoutSub],
832 );
833 }
834 P::AnonymousFunction => {
835 lambda += 1;
836 }
837 _ => {}
838 }
839 nesting_map.insert(node.id(), (nesting, depth, lambda));
840 }
841}
842
843impl Cognitive for KotlinCode {
844 fn compute<'a>(
845 node: &Node<'a>,
846 _code: &'a [u8],
847 stats: &mut Stats,
848 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
849 ) {
850 use Kotlin::*;
851
852 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
853
854 match node.kind_id().into() {
855 IfExpression if !Self::is_else_if(node) => {
856 increase_nesting(stats, &mut nesting, depth, lambda);
857 }
858 ForStatement | WhileStatement | DoWhileStatement | WhenExpression | CatchBlock => {
859 increase_nesting(stats, &mut nesting, depth, lambda);
860 }
861 Else => {
862 let in_when = node.parent().is_some_and(|p| p.kind_id() == WhenEntry);
866 if !in_when {
867 increment_by_one(stats);
868 }
869 }
870 UnaryExpression => {
871 stats.boolean_seq.not_operator();
872 }
873 BinaryExpression => {
874 compute_booleans_with(node, stats, |id| {
879 matches!(id.into(), AMPAMP | PIPEPIPE | QMARKCOLON)
880 });
881 }
882 FunctionDeclaration | SecondaryConstructor => {
883 nesting = 0;
884 increment_function_depth(
885 &mut depth,
886 node,
887 &[FunctionDeclaration, SecondaryConstructor],
888 );
889 }
890 LambdaLiteral | AnonymousFunction => {
891 lambda += 1;
892 }
893 _ => {}
894 }
895 nesting_map.insert(node.id(), (nesting, depth, lambda));
896 }
897}
898
899impl Cognitive for GoCode {
900 fn compute<'a>(
901 node: &Node<'a>,
902 _code: &'a [u8],
903 stats: &mut Stats,
904 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
905 ) {
906 use Go as G;
907
908 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
909
910 match node.kind_id().into() {
911 G::IfStatement if !Self::is_else_if(node) => {
912 increase_nesting(stats, &mut nesting, depth, lambda);
913 }
914 G::ForStatement
915 | G::ExpressionSwitchStatement
916 | G::TypeSwitchStatement
917 | G::SelectStatement => {
918 increase_nesting(stats, &mut nesting, depth, lambda);
919 }
920 G::Else | G::GotoStatement => {
921 increment_by_one(stats);
922 }
923 G::BreakStatement | G::ContinueStatement if node.is_child(G::LabelName as u16) => {
924 increment_by_one(stats);
925 }
926 G::UnaryExpression => {
927 stats.boolean_seq.not_operator();
928 }
929 G::BinaryExpression => {
930 compute_booleans(node, stats, G::AMPAMP, G::PIPEPIPE);
931 }
932 G::FunctionDeclaration | G::MethodDeclaration => {
933 nesting = 0;
934 increment_function_depth(
935 &mut depth,
936 node,
937 &[G::FunctionDeclaration, G::MethodDeclaration],
938 );
939 }
940 G::FuncLiteral => {
941 lambda += 1;
942 }
943 _ => {}
944 }
945 nesting_map.insert(node.id(), (nesting, depth, lambda));
946 }
947}
948
949impl Cognitive for BashCode {
950 fn compute<'a>(
951 node: &Node<'a>,
952 _code: &'a [u8],
953 stats: &mut Stats,
954 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
955 ) {
956 use Bash::*;
957
958 let (mut nesting, mut depth, lambda) = get_nesting_from_map(node, nesting_map);
959
960 match node.kind_id().into() {
961 IfStatement | WhileStatement | ForStatement | CStyleForStatement | CaseStatement => {
966 increase_nesting(stats, &mut nesting, depth, lambda);
967 }
968 ElifClause | ElseClause => {
969 increment_branch_extension(stats);
970 }
971 List | BinaryExpression3 => {
979 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
980 }
981 FunctionDefinition => {
982 nesting = 0;
983 increment_function_depth(&mut depth, node, &[FunctionDefinition]);
984 }
985 _ => {}
986 }
987 nesting_map.insert(node.id(), (nesting, depth, lambda));
988 }
989}
990
991impl Cognitive for TclCode {
992 fn compute<'a>(
993 node: &Node<'a>,
994 _code: &'a [u8],
995 stats: &mut Stats,
996 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
997 ) {
998 use Tcl::*;
999
1000 let (mut nesting, mut depth, lambda) = get_nesting_from_map(node, nesting_map);
1001
1002 match node.kind_id().into() {
1003 If if !Self::is_else_if(node) => {
1006 increase_nesting(stats, &mut nesting, depth, lambda);
1007 }
1008 Elseif => {
1010 increment_branch_extension(stats);
1011 }
1012 Else => {
1013 increment_by_one(stats);
1014 }
1015 While | Foreach | TernaryExpr => {
1016 increase_nesting(stats, &mut nesting, depth, lambda);
1017 }
1018 Catch => {
1020 increase_nesting(stats, &mut nesting, depth, lambda);
1021 }
1022 UnaryExpr if node.child(0).is_some_and(|c| c.kind_id() == Tcl::BANG) => {
1024 stats.boolean_seq.not_operator();
1025 }
1026 BinopExpr => {
1027 compute_booleans(node, stats, AMPAMP, PIPEPIPE);
1028 }
1029 Procedure => {
1030 nesting = 0;
1031 increment_function_depth(&mut depth, node, &[Procedure]);
1032 }
1033 _ => {}
1034 }
1035 nesting_map.insert(node.id(), (nesting, depth, lambda));
1036 }
1037}
1038
1039impl Cognitive for LuaCode {
1040 fn compute<'a>(
1041 node: &Node<'a>,
1042 _code: &'a [u8],
1043 stats: &mut Stats,
1044 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
1045 ) {
1046 use Lua::*;
1047
1048 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
1049
1050 match node.kind_id().into() {
1051 IfStatement if !Self::is_else_if(node) => {
1055 increase_nesting(stats, &mut nesting, depth, lambda);
1056 }
1057 ElseifStatement => {
1060 increment_branch_extension(stats);
1061 }
1062 ForStatement | WhileStatement | RepeatStatement => {
1063 increase_nesting(stats, &mut nesting, depth, lambda);
1064 }
1065 ElseStatement | BreakStatement | GotoStatement => {
1068 increment_by_one(stats);
1069 }
1070 UnaryExpression => {
1071 stats.boolean_seq.not_operator();
1072 }
1073 BinaryExpression => {
1074 compute_booleans(node, stats, And, Or);
1075 }
1076 FunctionDeclaration | FunctionDeclaration2 | FunctionDeclaration3 => {
1077 nesting = 0;
1078 increment_function_depth(
1079 &mut depth,
1080 node,
1081 &[
1082 FunctionDeclaration,
1083 FunctionDeclaration2,
1084 FunctionDeclaration3,
1085 ],
1086 );
1087 }
1088 FunctionDefinition => {
1089 lambda += 1;
1090 }
1091 _ => {}
1092 }
1093 nesting_map.insert(node.id(), (nesting, depth, lambda));
1094 }
1095}
1096
1097impl Cognitive for PhpCode {
1098 fn compute<'a>(
1099 node: &Node<'a>,
1100 _code: &'a [u8],
1101 stats: &mut Stats,
1102 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
1103 ) {
1104 use Php::*;
1105
1106 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
1107
1108 match node.kind_id().into() {
1109 IfStatement
1110 | ForStatement
1111 | ForeachStatement
1112 | WhileStatement
1113 | DoStatement
1114 | SwitchStatement
1115 | MatchExpression
1116 | CatchClause
1117 | ConditionalExpression => {
1118 increase_nesting(stats, &mut nesting, depth, lambda);
1119 }
1120 ElseClause | ElseClause2 | ElseIfClause | ElseIfClause2 => {
1121 increment_branch_extension(stats);
1122 }
1123 UnaryOpExpression | UnaryOpExpression2 => {
1124 stats.boolean_seq.not_operator();
1125 }
1126 BinaryExpression => {
1127 compute_booleans_with(node, stats, |id| {
1133 matches!(id.into(), AMPAMP | PIPEPIPE | And | Or | Xor | QMARKQMARK)
1134 });
1135 }
1136 AugmentedAssignmentExpression => {
1137 compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKQMARKEQ));
1144 }
1145 AnonymousFunction | ArrowFunction => {
1146 lambda += 1;
1147 }
1148 _ => {}
1149 }
1150 nesting_map.insert(node.id(), (nesting, depth, lambda));
1151 }
1152}
1153
1154pub(crate) fn elixir_call_keyword<'a>(node: &'a Node<'a>, code: &'a [u8]) -> Option<&'a str> {
1168 if node.kind_id() != Elixir::Call as u16 {
1169 return None;
1170 }
1171 let target = node.child_by_field_name("target")?;
1172 if target.kind_id() != Elixir::Identifier as u16 {
1173 return None;
1174 }
1175 target.utf8_text(code)
1176}
1177
1178#[inline]
1183pub(crate) fn elixir_is_method_macro(kw: &str) -> bool {
1184 matches!(kw, "def" | "defp" | "defmacro" | "defmacrop")
1185}
1186
1187#[inline]
1190pub(crate) fn elixir_is_class_macro(kw: &str) -> bool {
1191 kw == "defmodule"
1192}
1193
1194pub(crate) fn elixir_is_inside_quote_block(node: &Node<'_>, code: &[u8]) -> bool {
1206 let mut current = node.parent();
1207 while let Some(n) = current {
1208 if elixir_call_keyword(&n, code) == Some("quote") {
1209 return true;
1210 }
1211 current = n.parent();
1212 }
1213 false
1214}
1215
1216pub(crate) fn elixir_do_block_call_children<'a>(
1222 node: &'a Node<'a>,
1223) -> impl Iterator<Item = Node<'a>> + 'a {
1224 node.children()
1225 .filter(|child| child.kind_id() == Elixir::DoBlock as u16)
1226 .flat_map(|do_block| do_block.children())
1227 .filter(|stmt| stmt.kind_id() == Elixir::Call as u16)
1228}
1229
1230impl Cognitive for ElixirCode {
1231 fn compute<'a>(
1266 node: &Node<'a>,
1267 code: &'a [u8],
1268 stats: &mut Stats,
1269 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
1270 ) {
1271 use Elixir as E;
1272
1273 let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
1274
1275 match node.kind_id().into() {
1276 E::Call => match elixir_call_keyword(node, code) {
1277 Some("if" | "unless" | "for" | "while" | "case" | "cond" | "with") => {
1278 increase_nesting(stats, &mut nesting, depth, lambda);
1279 }
1280 Some(kw) if elixir_is_method_macro(kw) => {
1286 nesting = 0;
1301 }
1302 _ => {}
1303 },
1304 E::Else => {
1308 increment_by_one(stats);
1309 }
1310 E::RescueBlock | E::CatchBlock => {
1313 increase_nesting(stats, &mut nesting, depth, lambda);
1314 }
1315 E::AnonymousFunction => {
1319 lambda += 1;
1320 }
1321 E::BinaryOperator | E::BinaryOperator2 | E::BinaryOperator3 => {
1329 compute_elixir_booleans(node, stats);
1330 }
1331 E::UnaryOperator => {
1335 stats.boolean_seq.not_operator();
1336 }
1337 _ => {}
1338 }
1339 nesting_map.insert(node.id(), (nesting, depth, lambda));
1340 }
1341}
1342
1343implement_metric_trait!(Cognitive, PreprocCode, CcommentCode);
1344
1345impl Cognitive for RubyCode {
1346 fn compute<'a>(
1347 node: &Node<'a>,
1348 _code: &'a [u8],
1349 stats: &mut Stats,
1350 nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
1351 ) {
1352 use Ruby as R;
1353
1354 let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
1355
1356 match node.kind_id().into() {
1357 R::If if !Self::is_else_if(node) => {
1364 increase_nesting(stats, &mut nesting, depth, lambda);
1365 }
1366 R::Unless
1367 | R::While
1368 | R::Until
1369 | R::For
1370 | R::Case
1371 | R::CaseMatch
1372 | R::Conditional
1373 | R::IfModifier
1374 | R::UnlessModifier
1375 | R::WhileModifier
1376 | R::UntilModifier
1377 | R::Rescue
1378 | R::RescueModifier
1379 | R::RescueModifier2
1380 | R::RescueModifier3 => {
1381 increase_nesting(stats, &mut nesting, depth, lambda);
1382 }
1383 R::Elsif | R::Else => {
1387 increment_branch_extension(stats);
1388 }
1389 R::Break | R::Break2 | R::Next | R::Next2 | R::Redo | R::Retry => {
1392 increment_by_one(stats);
1393 }
1394 R::Unary | R::Unary2 | R::Unary3 | R::Unary4 | R::Unary5
1400 if node
1401 .child(0)
1402 .is_some_and(|c| matches!(c.kind_id().into(), R::BANG | R::Not)) =>
1403 {
1404 stats.boolean_seq.not_operator();
1405 }
1406 R::Binary | R::Binary2 | R::Binary3 => {
1407 compute_ruby_booleans(node, stats);
1412 }
1413 R::Method | R::SingletonMethod => {
1414 nesting = 0;
1415 increment_function_depth(&mut depth, node, &[R::Method, R::SingletonMethod]);
1416 }
1417 R::Block | R::DoBlock | R::Lambda => {
1419 lambda += 1;
1420 }
1421 _ => {}
1422 }
1423 nesting_map.insert(node.id(), (nesting, depth, lambda));
1424 }
1425}
1426
1427#[cfg(test)]
1428#[allow(
1429 clippy::float_cmp,
1430 clippy::cast_precision_loss,
1431 clippy::cast_possible_truncation,
1432 clippy::cast_sign_loss,
1433 clippy::similar_names,
1434 clippy::doc_markdown,
1435 clippy::needless_raw_string_hashes,
1436 clippy::too_many_lines
1437)]
1438mod tests {
1439 use crate::tools::check_metrics;
1440
1441 use super::*;
1442
1443 #[test]
1448 fn cognitive_empty_file_min_is_zero() {
1449 let stats = Stats::default();
1450 assert_eq!(stats.cognitive_min(), 0.0);
1451 }
1452
1453 #[test]
1454 fn python_no_cognitive() {
1455 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
1456 insta::assert_json_snapshot!(
1457 metric.cognitive,
1458 @r###"
1459 {
1460 "sum": 0.0,
1461 "average": null,
1462 "min": 0.0,
1463 "max": 0.0
1464 }"###
1465 );
1466 });
1467 }
1468
1469 #[test]
1470 fn rust_no_cognitive() {
1471 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
1472 insta::assert_json_snapshot!(
1473 metric.cognitive,
1474 @r###"
1475 {
1476 "sum": 0.0,
1477 "average": null,
1478 "min": 0.0,
1479 "max": 0.0
1480 }"###
1481 );
1482 });
1483 }
1484
1485 #[test]
1486 fn c_no_cognitive() {
1487 check_metrics::<CppParser>("int a = 42;", "foo.c", |metric| {
1488 insta::assert_json_snapshot!(
1489 metric.cognitive,
1490 @r###"
1491 {
1492 "sum": 0.0,
1493 "average": null,
1494 "min": 0.0,
1495 "max": 0.0
1496 }"###
1497 );
1498 });
1499 }
1500
1501 #[test]
1502 fn mozjs_no_cognitive() {
1503 check_metrics::<MozjsParser>("var a = 42;", "foo.js", |metric| {
1504 insta::assert_json_snapshot!(
1505 metric.cognitive,
1506 @r###"
1507 {
1508 "sum": 0.0,
1509 "average": null,
1510 "min": 0.0,
1511 "max": 0.0
1512 }"###
1513 );
1514 });
1515 }
1516
1517 #[test]
1518 fn javascript_no_cognitive() {
1519 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
1520 insta::assert_json_snapshot!(
1521 metric.cognitive,
1522 @r###"
1523 {
1524 "sum": 0.0,
1525 "average": null,
1526 "min": 0.0,
1527 "max": 0.0
1528 }"###
1529 );
1530 });
1531 }
1532
1533 #[test]
1534 fn python_simple_function() {
1535 check_metrics::<PythonParser>(
1536 "def f(a, b):
1537 if a and b: # +2 (+1 and)
1538 return 1
1539 if c and d: # +2 (+1 and)
1540 return 1",
1541 "foo.py",
1542 |metric| {
1543 insta::assert_json_snapshot!(
1544 metric.cognitive,
1545 @r###"
1546 {
1547 "sum": 4.0,
1548 "average": 4.0,
1549 "min": 0.0,
1550 "max": 4.0
1551 }"###
1552 );
1553 },
1554 );
1555 }
1556
1557 #[test]
1563 fn python_match_two_arm_wildcard() {
1564 check_metrics::<PythonParser>(
1565 "def f(x):
1566 match x:
1567 case 1:
1568 return 'one'
1569 case _:
1570 return 'other'
1571",
1572 "foo.py",
1573 |metric| {
1574 insta::assert_json_snapshot!(
1578 metric.cognitive,
1579 @r###"
1580 {
1581 "sum": 1.0,
1582 "average": 1.0,
1583 "min": 0.0,
1584 "max": 1.0
1585 }"###
1586 );
1587 },
1588 );
1589 }
1590
1591 #[test]
1592 fn python_expression_statement() {
1593 check_metrics::<PythonParser>(
1596 "def f(a, b):
1597 c = True and True",
1598 "foo.py",
1599 |metric| {
1600 insta::assert_json_snapshot!(
1601 metric.cognitive,
1602 @r###"
1603 {
1604 "sum": 1.0,
1605 "average": 1.0,
1606 "min": 0.0,
1607 "max": 1.0
1608 }"###
1609 );
1610 },
1611 );
1612 }
1613
1614 #[test]
1615 fn python_tuple() {
1616 check_metrics::<PythonParser>(
1619 "def f(a, b):
1620 return \"%s%s\" % (a and \"Get\" or \"Set\", b)",
1621 "foo.py",
1622 |metric| {
1623 insta::assert_json_snapshot!(
1624 metric.cognitive,
1625 @r###"
1626 {
1627 "sum": 2.0,
1628 "average": 2.0,
1629 "min": 0.0,
1630 "max": 2.0
1631 }"###
1632 );
1633 },
1634 );
1635 }
1636
1637 #[test]
1638 fn python_elif_function() {
1639 check_metrics::<PythonParser>(
1642 "def f(a, b):
1643 if a and b: # +2 (+1 and)
1644 return 1
1645 elif c and d: # +2 (+1 and)
1646 return 1",
1647 "foo.py",
1648 |metric| {
1649 insta::assert_json_snapshot!(
1650 metric.cognitive,
1651 @r###"
1652 {
1653 "sum": 4.0,
1654 "average": 4.0,
1655 "min": 0.0,
1656 "max": 4.0
1657 }"###
1658 );
1659 },
1660 );
1661 }
1662
1663 #[test]
1664 fn python_more_elifs_function() {
1665 check_metrics::<PythonParser>(
1668 "def f(a, b):
1669 if a and b: # +2 (+1 and)
1670 return 1
1671 elif c and d: # +2 (+1 and)
1672 return 1
1673 elif e and f: # +2 (+1 and)
1674 return 1",
1675 "foo.py",
1676 |metric| {
1677 insta::assert_json_snapshot!(
1678 metric.cognitive,
1679 @r###"
1680 {
1681 "sum": 6.0,
1682 "average": 6.0,
1683 "min": 0.0,
1684 "max": 6.0
1685 }"###
1686 );
1687 },
1688 );
1689 }
1690
1691 #[test]
1692 fn python_if_elif_elif_else_chain() {
1693 check_metrics::<PythonParser>(
1702 "def f(a, b, c, d):
1703 if a:
1704 return 1
1705 elif b:
1706 return 2
1707 elif c:
1708 return 3
1709 else:
1710 return 4",
1711 "foo.py",
1712 |metric| {
1713 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
1714 insta::assert_json_snapshot!(
1715 metric.cognitive,
1716 @r###"
1717 {
1718 "sum": 4.0,
1719 "average": 4.0,
1720 "min": 0.0,
1721 "max": 4.0
1722 }"###
1723 );
1724 },
1725 );
1726 }
1727
1728 #[test]
1729 fn python_else_if_chain_matches_elif() {
1730 check_metrics::<PythonParser>(
1739 "def f(a, b, c, d):
1740 if a and b:
1741 return 1
1742 else:
1743 if c and d:
1744 return 1",
1745 "foo.py",
1746 |metric| {
1747 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
1748 insta::assert_json_snapshot!(
1749 metric.cognitive,
1750 @r###"
1751 {
1752 "sum": 4.0,
1753 "average": 4.0,
1754 "min": 0.0,
1755 "max": 4.0
1756 }"###
1757 );
1758 },
1759 );
1760 }
1761
1762 #[test]
1763 fn rust_simple_function() {
1764 check_metrics::<RustParser>(
1765 "fn f() {
1766 if a && b { // +2 (+1 &&)
1767 println!(\"test\");
1768 }
1769 if c && d { // +2 (+1 &&)
1770 println!(\"test\");
1771 }
1772 }",
1773 "foo.rs",
1774 |metric| {
1775 insta::assert_json_snapshot!(
1776 metric.cognitive,
1777 @r###"
1778 {
1779 "sum": 4.0,
1780 "average": 4.0,
1781 "min": 0.0,
1782 "max": 4.0
1783 }"###
1784 );
1785 },
1786 );
1787 }
1788
1789 #[test]
1790 fn c_simple_function() {
1791 check_metrics::<CppParser>(
1792 "void f() {
1793 if (a && b) { // +2 (+1 &&)
1794 printf(\"test\");
1795 }
1796 if (c && d) { // +2 (+1 &&)
1797 printf(\"test\");
1798 }
1799 }",
1800 "foo.c",
1801 |metric| {
1802 insta::assert_json_snapshot!(
1803 metric.cognitive,
1804 @r###"
1805 {
1806 "sum": 4.0,
1807 "average": 4.0,
1808 "min": 0.0,
1809 "max": 4.0
1810 }"###
1811 );
1812 },
1813 );
1814 }
1815
1816 #[test]
1817 fn mozjs_simple_function() {
1818 check_metrics::<MozjsParser>(
1819 "function f() {
1820 if (a && b) { // +2 (+1 &&)
1821 window.print(\"test\");
1822 }
1823 if (c && d) { // +2 (+1 &&)
1824 window.print(\"test\");
1825 }
1826 }",
1827 "foo.js",
1828 |metric| {
1829 insta::assert_json_snapshot!(
1830 metric.cognitive,
1831 @r###"
1832 {
1833 "sum": 4.0,
1834 "average": 4.0,
1835 "min": 0.0,
1836 "max": 4.0
1837 }"###
1838 );
1839 },
1840 );
1841 }
1842
1843 #[test]
1844 fn javascript_simple_function() {
1845 check_metrics::<JavascriptParser>(
1846 "function f() {
1847 if (a && b) { // +2 (+1 &&)
1848 console.log(\"test\");
1849 }
1850 if (c || d) { // +2 (+1 ||)
1851 console.log(\"test\");
1852 }
1853 }",
1854 "foo.js",
1855 |metric| {
1856 insta::assert_json_snapshot!(
1857 metric.cognitive,
1858 @r###"
1859 {
1860 "sum": 4.0,
1861 "average": 4.0,
1862 "min": 0.0,
1863 "max": 4.0
1864 }"###
1865 );
1866 },
1867 );
1868 }
1869
1870 #[test]
1871 fn python_sequence_same_booleans() {
1872 check_metrics::<PythonParser>(
1873 "def f(a, b):
1874 if a and b and True: # +2 (+1 sequence of and)
1875 return 1",
1876 "foo.py",
1877 |metric| {
1878 insta::assert_json_snapshot!(
1879 metric.cognitive,
1880 @r###"
1881 {
1882 "sum": 2.0,
1883 "average": 2.0,
1884 "min": 0.0,
1885 "max": 2.0
1886 }"###
1887 );
1888 },
1889 );
1890 }
1891
1892 #[test]
1893 fn rust_sequence_same_booleans() {
1894 check_metrics::<RustParser>(
1895 "fn f() {
1896 if a && b && true { // +2 (+1 sequence of &&)
1897 println!(\"test\");
1898 }
1899 }",
1900 "foo.rs",
1901 |metric| {
1902 insta::assert_json_snapshot!(
1903 metric.cognitive,
1904 @r###"
1905 {
1906 "sum": 2.0,
1907 "average": 2.0,
1908 "min": 0.0,
1909 "max": 2.0
1910 }"###
1911 );
1912 },
1913 );
1914
1915 check_metrics::<RustParser>(
1916 "fn f() {
1917 if a || b || c || d { // +2 (+1 sequence of ||)
1918 println!(\"test\");
1919 }
1920 }",
1921 "foo.rs",
1922 |metric| {
1923 insta::assert_json_snapshot!(
1924 metric.cognitive,
1925 @r###"
1926 {
1927 "sum": 2.0,
1928 "average": 2.0,
1929 "min": 0.0,
1930 "max": 2.0
1931 }"###
1932 );
1933 },
1934 );
1935 }
1936
1937 #[test]
1938 fn c_sequence_same_booleans() {
1939 check_metrics::<CppParser>(
1940 "void f() {
1941 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
1942 printf(\"test\");
1943 }
1944 }",
1945 "foo.c",
1946 |metric| {
1947 insta::assert_json_snapshot!(
1948 metric.cognitive,
1949 @r###"
1950 {
1951 "sum": 2.0,
1952 "average": 2.0,
1953 "min": 0.0,
1954 "max": 2.0
1955 }"###
1956 );
1957 },
1958 );
1959
1960 check_metrics::<CppParser>(
1961 "void f() {
1962 if (a || b || c || d) { // +2 (+1 sequence of ||)
1963 printf(\"test\");
1964 }
1965 }",
1966 "foo.c",
1967 |metric| {
1968 insta::assert_json_snapshot!(
1969 metric.cognitive,
1970 @r###"
1971 {
1972 "sum": 2.0,
1973 "average": 2.0,
1974 "min": 0.0,
1975 "max": 2.0
1976 }"###
1977 );
1978 },
1979 );
1980 }
1981
1982 #[test]
1983 fn mozjs_sequence_same_booleans() {
1984 check_metrics::<MozjsParser>(
1985 "function f() {
1986 if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
1987 window.print(\"test\");
1988 }
1989 }",
1990 "foo.js",
1991 |metric| {
1992 insta::assert_json_snapshot!(
1993 metric.cognitive,
1994 @r###"
1995 {
1996 "sum": 2.0,
1997 "average": 2.0,
1998 "min": 0.0,
1999 "max": 2.0
2000 }"###
2001 );
2002 },
2003 );
2004
2005 check_metrics::<MozjsParser>(
2006 "function f() {
2007 if (a || b || c || d) { // +2 (+1 sequence of ||)
2008 window.print(\"test\");
2009 }
2010 }",
2011 "foo.js",
2012 |metric| {
2013 insta::assert_json_snapshot!(
2014 metric.cognitive,
2015 @r###"
2016 {
2017 "sum": 2.0,
2018 "average": 2.0,
2019 "min": 0.0,
2020 "max": 2.0
2021 }"###
2022 );
2023 },
2024 );
2025 }
2026
2027 #[test]
2028 fn rust_not_booleans() {
2029 check_metrics::<RustParser>(
2030 "fn f() {
2031 if !a && !b { // +2 (+1 &&)
2032 println!(\"test\");
2033 }
2034 }",
2035 "foo.rs",
2036 |metric| {
2037 insta::assert_json_snapshot!(
2038 metric.cognitive,
2039 @r###"
2040 {
2041 "sum": 2.0,
2042 "average": 2.0,
2043 "min": 0.0,
2044 "max": 2.0
2045 }"###
2046 );
2047 },
2048 );
2049
2050 check_metrics::<RustParser>(
2051 "fn f() {
2052 if a && !(b && c) { // +3 (+1 &&, +1 &&)
2053 println!(\"test\");
2054 }
2055 }",
2056 "foo.rs",
2057 |metric| {
2058 insta::assert_json_snapshot!(
2059 metric.cognitive,
2060 @r###"
2061 {
2062 "sum": 3.0,
2063 "average": 3.0,
2064 "min": 0.0,
2065 "max": 3.0
2066 }"###
2067 );
2068 },
2069 );
2070
2071 check_metrics::<RustParser>(
2072 "fn f() {
2073 if !(a || b) && !(c || d) { // +4 (+1 ||, +1 &&, +1 ||)
2074 println!(\"test\");
2075 }
2076 }",
2077 "foo.rs",
2078 |metric| {
2079 insta::assert_json_snapshot!(
2080 metric.cognitive,
2081 @r###"
2082 {
2083 "sum": 4.0,
2084 "average": 4.0,
2085 "min": 0.0,
2086 "max": 4.0
2087 }"###
2088 );
2089 },
2090 );
2091 }
2092
2093 #[test]
2094 fn c_not_booleans() {
2095 check_metrics::<CppParser>(
2096 "void f() {
2097 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
2098 printf(\"test\");
2099 }
2100 }",
2101 "foo.c",
2102 |metric| {
2103 insta::assert_json_snapshot!(
2104 metric.cognitive,
2105 @r###"
2106 {
2107 "sum": 3.0,
2108 "average": 3.0,
2109 "min": 0.0,
2110 "max": 3.0
2111 }"###
2112 );
2113 },
2114 );
2115
2116 check_metrics::<CppParser>(
2117 "void f() {
2118 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
2119 printf(\"test\");
2120 }
2121 }",
2122 "foo.c",
2123 |metric| {
2124 insta::assert_json_snapshot!(
2125 metric.cognitive,
2126 @r###"
2127 {
2128 "sum": 4.0,
2129 "average": 4.0,
2130 "min": 0.0,
2131 "max": 4.0
2132 }"###
2133 );
2134 },
2135 );
2136 }
2137
2138 #[test]
2139 fn mozjs_not_booleans() {
2140 check_metrics::<MozjsParser>(
2141 "function f() {
2142 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
2143 window.print(\"test\");
2144 }
2145 }",
2146 "foo.js",
2147 |metric| {
2148 insta::assert_json_snapshot!(
2149 metric.cognitive,
2150 @r###"
2151 {
2152 "sum": 3.0,
2153 "average": 3.0,
2154 "min": 0.0,
2155 "max": 3.0
2156 }"###
2157 );
2158 },
2159 );
2160
2161 check_metrics::<MozjsParser>(
2162 "function f() {
2163 if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
2164 window.print(\"test\");
2165 }
2166 }",
2167 "foo.js",
2168 |metric| {
2169 insta::assert_json_snapshot!(
2170 metric.cognitive,
2171 @r###"
2172 {
2173 "sum": 4.0,
2174 "average": 4.0,
2175 "min": 0.0,
2176 "max": 4.0
2177 }"###
2178 );
2179 },
2180 );
2181 }
2182
2183 #[test]
2184 fn python_sequence_different_booleans() {
2185 check_metrics::<PythonParser>(
2186 "def f(a, b):
2187 if a and b or True: # +3 (+1 and, +1 or)
2188 return 1",
2189 "foo.py",
2190 |metric| {
2191 insta::assert_json_snapshot!(
2192 metric.cognitive,
2193 @r###"
2194 {
2195 "sum": 3.0,
2196 "average": 3.0,
2197 "min": 0.0,
2198 "max": 3.0
2199 }"###
2200 );
2201 },
2202 );
2203 }
2204
2205 #[test]
2206 fn rust_sequence_different_booleans() {
2207 check_metrics::<RustParser>(
2208 "fn f() {
2209 if a && b || true { // +3 (+1 &&, +1 ||)
2210 println!(\"test\");
2211 }
2212 }",
2213 "foo.rs",
2214 |metric| {
2215 insta::assert_json_snapshot!(
2216 metric.cognitive,
2217 @r###"
2218 {
2219 "sum": 3.0,
2220 "average": 3.0,
2221 "min": 0.0,
2222 "max": 3.0
2223 }"###
2224 );
2225 },
2226 );
2227 }
2228
2229 #[test]
2230 fn c_sequence_different_booleans() {
2231 check_metrics::<CppParser>(
2232 "void f() {
2233 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
2234 printf(\"test\");
2235 }
2236 }",
2237 "foo.c",
2238 |metric| {
2239 insta::assert_json_snapshot!(
2240 metric.cognitive,
2241 @r###"
2242 {
2243 "sum": 3.0,
2244 "average": 3.0,
2245 "min": 0.0,
2246 "max": 3.0
2247 }"###
2248 );
2249 },
2250 );
2251 }
2252
2253 #[test]
2254 fn mozjs_sequence_different_booleans() {
2255 check_metrics::<MozjsParser>(
2256 "function f() {
2257 if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
2258 window.print(\"test\");
2259 }
2260 }",
2261 "foo.js",
2262 |metric| {
2263 insta::assert_json_snapshot!(
2264 metric.cognitive,
2265 @r###"
2266 {
2267 "sum": 3.0,
2268 "average": 3.0,
2269 "min": 0.0,
2270 "max": 3.0
2271 }"###
2272 );
2273 },
2274 );
2275 }
2276
2277 #[test]
2278 fn python_formatted_sequence_different_booleans() {
2279 check_metrics::<PythonParser>(
2280 "def f(a, b):
2281 if ( # +1
2282 a and b and # +1
2283 (c or d) # +1
2284 ):
2285 return 1",
2286 "foo.py",
2287 |metric| {
2288 insta::assert_json_snapshot!(
2289 metric.cognitive,
2290 @r###"
2291 {
2292 "sum": 3.0,
2293 "average": 3.0,
2294 "min": 0.0,
2295 "max": 3.0
2296 }"###
2297 );
2298 },
2299 );
2300 }
2301
2302 #[test]
2303 fn python_1_level_nesting() {
2304 check_metrics::<PythonParser>(
2305 "def f(a, b):
2306 if a: # +1
2307 for i in range(b): # +2
2308 return 1",
2309 "foo.py",
2310 |metric| {
2311 insta::assert_json_snapshot!(
2312 metric.cognitive,
2313 @r###"
2314 {
2315 "sum": 3.0,
2316 "average": 3.0,
2317 "min": 0.0,
2318 "max": 3.0
2319 }"###
2320 );
2321 },
2322 );
2323 }
2324
2325 #[test]
2326 fn rust_1_level_nesting() {
2327 check_metrics::<RustParser>(
2328 "fn f() {
2329 if true { // +1
2330 if true { // +2 (nesting = 1)
2331 println!(\"test\");
2332 } else if 1 == 1 { // +1
2333 if true { // +3 (nesting = 2)
2334 println!(\"test\");
2335 }
2336 } else { // +1
2337 if true { // +3 (nesting = 2)
2338 println!(\"test\");
2339 }
2340 }
2341 }
2342 }",
2343 "foo.rs",
2344 |metric| {
2345 insta::assert_json_snapshot!(
2346 metric.cognitive,
2347 @r###"
2348 {
2349 "sum": 11.0,
2350 "average": 11.0,
2351 "min": 0.0,
2352 "max": 11.0
2353 }"###
2354 );
2355 },
2356 );
2357
2358 check_metrics::<RustParser>(
2359 "fn f() {
2360 if true { // +1
2361 match true { // +2 (nesting = 1)
2362 true => println!(\"test\"),
2363 false => println!(\"test\"),
2364 }
2365 }
2366 }",
2367 "foo.rs",
2368 |metric| {
2369 insta::assert_json_snapshot!(
2370 metric.cognitive,
2371 @r###"
2372 {
2373 "sum": 3.0,
2374 "average": 3.0,
2375 "min": 0.0,
2376 "max": 3.0
2377 }"###
2378 );
2379 },
2380 );
2381 }
2382
2383 #[test]
2384 fn c_1_level_nesting() {
2385 check_metrics::<CppParser>(
2386 "void f() {
2387 if (1 == 1) { // +1
2388 if (1 == 1) { // +2 (nesting = 1)
2389 printf(\"test\");
2390 } else if (1 == 1) { // +1
2391 if (1 == 1) { // +3 (nesting = 2)
2392 printf(\"test\");
2393 }
2394 } else { // +1
2395 if (1 == 1) { // +3 (nesting = 2)
2396 printf(\"test\");
2397 }
2398 }
2399 }
2400 }",
2401 "foo.c",
2402 |metric| {
2403 insta::assert_json_snapshot!(
2404 metric.cognitive,
2405 @r###"
2406 {
2407 "sum": 11.0,
2408 "average": 11.0,
2409 "min": 0.0,
2410 "max": 11.0
2411 }"###
2412 );
2413 },
2414 );
2415 }
2416
2417 #[test]
2418 fn mozjs_1_level_nesting() {
2419 check_metrics::<MozjsParser>(
2420 "function f() {
2421 if (1 == 1) { // +1
2422 if (1 == 1) { // +2 (nesting = 1)
2423 window.print(\"test\");
2424 } else if (1 == 1) { // +1
2425 if (1 == 1) { // +3 (nesting = 2)
2426 window.print(\"test\");
2427 }
2428 } else { // +1
2429 if (1 == 1) { // +3 (nesting = 2)
2430 window.print(\"test\");
2431 }
2432 }
2433 }
2434 }",
2435 "foo.js",
2436 |metric| {
2437 insta::assert_json_snapshot!(
2438 metric.cognitive,
2439 @r###"
2440 {
2441 "sum": 11.0,
2442 "average": 11.0,
2443 "min": 0.0,
2444 "max": 11.0
2445 }"###
2446 );
2447 },
2448 );
2449 }
2450
2451 #[test]
2452 fn javascript_nesting() {
2453 check_metrics::<JavascriptParser>(
2454 "function f() {
2455 if (a) { // +1
2456 for (let i = 0; i < 10; i++) { // +2 (nesting = 1)
2457 while (b) { // +3 (nesting = 2)
2458 console.log(\"test\");
2459 }
2460 }
2461 }
2462 }",
2463 "foo.js",
2464 |metric| {
2465 insta::assert_json_snapshot!(
2466 metric.cognitive,
2467 @r###"
2468 {
2469 "sum": 6.0,
2470 "average": 6.0,
2471 "min": 0.0,
2472 "max": 6.0
2473 }"###
2474 );
2475 },
2476 );
2477 }
2478
2479 #[test]
2480 fn python_2_level_nesting() {
2481 check_metrics::<PythonParser>(
2482 "def f(a, b):
2483 if a: # +1
2484 for i in range(b): # +2
2485 if b: # +3
2486 return 1",
2487 "foo.py",
2488 |metric| {
2489 insta::assert_json_snapshot!(
2490 metric.cognitive,
2491 @r###"
2492 {
2493 "sum": 6.0,
2494 "average": 6.0,
2495 "min": 0.0,
2496 "max": 6.0
2497 }"###
2498 );
2499 },
2500 );
2501 }
2502
2503 #[test]
2504 fn rust_2_level_nesting() {
2505 check_metrics::<RustParser>(
2506 "fn f() {
2507 if true { // +1
2508 for i in 0..4 { // +2 (nesting = 1)
2509 match true { // +3 (nesting = 2)
2510 true => println!(\"test\"),
2511 false => println!(\"test\"),
2512 }
2513 }
2514 }
2515 }",
2516 "foo.rs",
2517 |metric| {
2518 insta::assert_json_snapshot!(
2519 metric.cognitive,
2520 @r###"
2521 {
2522 "sum": 6.0,
2523 "average": 6.0,
2524 "min": 0.0,
2525 "max": 6.0
2526 }"###
2527 );
2528 },
2529 );
2530 }
2531
2532 #[test]
2533 fn python_try_construct() {
2534 check_metrics::<PythonParser>(
2535 "def f(a, b):
2536 try:
2537 for foo in bar: # +1
2538 return a
2539 except Exception: # +1
2540 if a < 0: # +2
2541 return a",
2542 "foo.py",
2543 |metric| {
2544 insta::assert_json_snapshot!(
2545 metric.cognitive,
2546 @r###"
2547 {
2548 "sum": 4.0,
2549 "average": 4.0,
2550 "min": 0.0,
2551 "max": 4.0
2552 }"###
2553 );
2554 },
2555 );
2556 }
2557
2558 #[test]
2559 fn python_flat_try_except() {
2560 check_metrics::<PythonParser>(
2567 "def f():
2568 try:
2569 pass
2570 except Exception: # +1
2571 pass",
2572 "foo.py",
2573 |metric| {
2574 assert_eq!(metric.cognitive.cognitive_sum() as u32, 1);
2576 insta::assert_json_snapshot!(
2577 metric.cognitive,
2578 @r###"
2579 {
2580 "sum": 1.0,
2581 "average": 1.0,
2582 "min": 0.0,
2583 "max": 1.0
2584 }"###
2585 );
2586 },
2587 );
2588 }
2589
2590 #[test]
2591 fn python_except_inside_if() {
2592 check_metrics::<PythonParser>(
2599 "def f(x):
2600 if x: # +1
2601 try:
2602 pass
2603 except Exception: # +2 (nesting = 1)
2604 pass",
2605 "foo.py",
2606 |metric| {
2607 assert_eq!(metric.cognitive.cognitive_sum() as u32, 3);
2609 insta::assert_json_snapshot!(
2610 metric.cognitive,
2611 @r###"
2612 {
2613 "sum": 3.0,
2614 "average": 3.0,
2615 "min": 0.0,
2616 "max": 3.0
2617 }"###
2618 );
2619 },
2620 );
2621 }
2622
2623 #[test]
2624 fn python_except_inside_for() {
2625 check_metrics::<PythonParser>(
2628 "def f(xs):
2629 for x in xs: # +1
2630 try:
2631 pass
2632 except Exception: # +2 (nesting = 1)
2633 pass",
2634 "foo.py",
2635 |metric| {
2636 assert_eq!(metric.cognitive.cognitive_sum() as u32, 3);
2638 insta::assert_json_snapshot!(
2639 metric.cognitive,
2640 @r###"
2641 {
2642 "sum": 3.0,
2643 "average": 3.0,
2644 "min": 0.0,
2645 "max": 3.0
2646 }"###
2647 );
2648 },
2649 );
2650 }
2651
2652 #[test]
2653 fn python_multi_except_inside_if() {
2654 check_metrics::<PythonParser>(
2660 "def f(x):
2661 if x: # +1
2662 try:
2663 pass
2664 except ValueError: # +2
2665 pass
2666 except TypeError: # +2
2667 pass
2668 except Exception: # +2
2669 pass",
2670 "foo.py",
2671 |metric| {
2672 assert_eq!(metric.cognitive.cognitive_sum() as u32, 7);
2674 insta::assert_json_snapshot!(
2675 metric.cognitive,
2676 @r###"
2677 {
2678 "sum": 7.0,
2679 "average": 7.0,
2680 "min": 0.0,
2681 "max": 7.0
2682 }"###
2683 );
2684 },
2685 );
2686 }
2687
2688 #[test]
2689 fn mozjs_try_construct() {
2690 check_metrics::<MozjsParser>(
2691 "function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
2692 for (const collector of this.collectors) {
2693 try {
2694 collector._onChannelRedirect(oldChannel, newChannel, flags);
2695 } catch (ex) {
2696 console.error(
2697 \"StackTraceCollector.onChannelRedirect threw an exception\",
2698 ex
2699 );
2700 }
2701 }
2702 callback.onRedirectVerifyCallback(Cr.NS_OK);
2703 }",
2704 "foo.js",
2705 |metric| {
2706 insta::assert_json_snapshot!(
2707 metric.cognitive,
2708 @r###"
2709 {
2710 "sum": 3.0,
2711 "average": 3.0,
2712 "min": 0.0,
2713 "max": 3.0
2714 }"###
2715 );
2716 },
2717 );
2718 }
2719
2720 #[test]
2721 fn javascript_try_construct() {
2722 check_metrics::<JavascriptParser>(
2723 "function f() {
2724 for (let i = 0; i < 10; i++) { // +1
2725 try {
2726 doSomething(i);
2727 } catch (ex) { // +2 (nesting = 1)
2728 if (ex instanceof TypeError) { // +3 (nesting = 2)
2729 console.error(\"type error\");
2730 }
2731 } finally {
2732 cleanup();
2733 }
2734 }
2735 }",
2736 "foo.js",
2737 |metric| {
2738 insta::assert_json_snapshot!(
2739 metric.cognitive,
2740 @r###"
2741 {
2742 "sum": 6.0,
2743 "average": 6.0,
2744 "min": 0.0,
2745 "max": 6.0
2746 }"###
2747 );
2748 },
2749 );
2750 }
2751
2752 #[test]
2760 fn javascript_for_of_loop() {
2761 check_metrics::<JavascriptParser>(
2762 "function f(xs) {
2763 let s = 0;
2764 for (const x of xs) { // +1
2765 s += x;
2766 }
2767 return s;
2768 }",
2769 "foo.js",
2770 |metric| {
2771 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
2772 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
2773 insta::assert_json_snapshot!(
2774 metric.cognitive,
2775 @r###"
2776 {
2777 "sum": 1.0,
2778 "average": 1.0,
2779 "min": 0.0,
2780 "max": 1.0
2781 }"###
2782 );
2783 },
2784 );
2785 }
2786
2787 #[test]
2788 fn mozjs_for_of_loop() {
2789 check_metrics::<MozjsParser>(
2790 "function f(xs) {
2791 let s = 0;
2792 for (const x of xs) { // +1
2793 s += x;
2794 }
2795 return s;
2796 }",
2797 "foo.js",
2798 |metric| {
2799 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
2800 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
2801 insta::assert_json_snapshot!(
2802 metric.cognitive,
2803 @r###"
2804 {
2805 "sum": 1.0,
2806 "average": 1.0,
2807 "min": 0.0,
2808 "max": 1.0
2809 }"###
2810 );
2811 },
2812 );
2813 }
2814
2815 #[test]
2816 fn typescript_for_of_loop() {
2817 check_metrics::<TypescriptParser>(
2818 "function f(xs: number[]): number {
2819 let s = 0;
2820 for (const x of xs) { // +1
2821 s += x;
2822 }
2823 return s;
2824 }",
2825 "foo.ts",
2826 |metric| {
2827 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
2828 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
2829 insta::assert_json_snapshot!(
2830 metric.cognitive,
2831 @r###"
2832 {
2833 "sum": 1.0,
2834 "average": 1.0,
2835 "min": 0.0,
2836 "max": 1.0
2837 }"###
2838 );
2839 },
2840 );
2841 }
2842
2843 #[test]
2844 fn tsx_for_of_loop() {
2845 check_metrics::<TsxParser>(
2846 "function f(xs: number[]): number {
2847 let s = 0;
2848 for (const x of xs) { // +1
2849 s += x;
2850 }
2851 return s;
2852 }",
2853 "foo.tsx",
2854 |metric| {
2855 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
2856 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
2857 insta::assert_json_snapshot!(
2858 metric.cognitive,
2859 @r###"
2860 {
2861 "sum": 1.0,
2862 "average": 1.0,
2863 "min": 0.0,
2864 "max": 1.0
2865 }"###
2866 );
2867 },
2868 );
2869 }
2870
2871 #[test]
2872 fn rust_break_continue() {
2873 check_metrics::<RustParser>(
2875 "fn f() {
2876 'tens: for ten in 0..3 { // +1
2877 '_units: for unit in 0..=9 { // +2 (nesting = 1)
2878 if unit % 2 == 0 { // +3 (nesting = 2)
2879 continue;
2880 } else if unit == 5 { // +1
2881 continue 'tens; // +1
2882 } else if unit == 6 { // +1
2883 break;
2884 } else { // +1
2885 break 'tens; // +1
2886 }
2887 }
2888 }
2889 }",
2890 "foo.rs",
2891 |metric| {
2892 insta::assert_json_snapshot!(
2893 metric.cognitive,
2894 @r###"
2895 {
2896 "sum": 11.0,
2897 "average": 11.0,
2898 "min": 0.0,
2899 "max": 11.0
2900 }"###
2901 );
2902 },
2903 );
2904 }
2905
2906 #[test]
2907 fn c_goto() {
2908 check_metrics::<CppParser>(
2909 "void f() {
2910 OUT: for (int i = 1; i <= max; ++i) { // +1
2911 for (int j = 2; j < i; ++j) { // +2 (nesting = 1)
2912 if (i % j == 0) { // +3 (nesting = 2)
2913 goto OUT; // +1
2914 }
2915 }
2916 }
2917 }",
2918 "foo.c",
2919 |metric| {
2920 insta::assert_json_snapshot!(
2921 metric.cognitive,
2922 @r###"
2923 {
2924 "sum": 7.0,
2925 "average": 7.0,
2926 "min": 0.0,
2927 "max": 7.0
2928 }"###
2929 );
2930 },
2931 );
2932 }
2933
2934 #[test]
2935 fn c_switch() {
2936 check_metrics::<CppParser>(
2937 "void f() {
2938 switch (1) { // +1
2939 case 1:
2940 printf(\"one\");
2941 break;
2942 case 2:
2943 printf(\"two\");
2944 break;
2945 case 3:
2946 printf(\"three\");
2947 break;
2948 default:
2949 printf(\"all\");
2950 break;
2951 }
2952 }",
2953 "foo.c",
2954 |metric| {
2955 insta::assert_json_snapshot!(
2956 metric.cognitive,
2957 @r###"
2958 {
2959 "sum": 1.0,
2960 "average": 1.0,
2961 "min": 0.0,
2962 "max": 1.0
2963 }"###
2964 );
2965 },
2966 );
2967 }
2968
2969 #[test]
2970 fn c_ternary() {
2971 check_metrics::<CppParser>(
2976 "int f(int a) {
2977 if (a) { // +1
2978 return a > 0 ? 1 : -1; // +2 (1 + nesting 1)
2979 }
2980 return a > 0 ? 0 : -1; // +1
2981 }",
2982 "foo.c",
2983 |metric| {
2986 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
2987 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
2988 insta::assert_json_snapshot!(
2989 metric.cognitive,
2990 @r###"
2991 {
2992 "sum": 4.0,
2993 "average": 4.0,
2994 "min": 0.0,
2995 "max": 4.0
2996 }"###
2997 );
2998 },
2999 );
3000 }
3001
3002 #[test]
3003 fn c_try_catch_single() {
3004 check_metrics::<CppParser>(
3005 "void f() {
3006 try {
3007 g();
3008 } catch (const std::exception& e) { // +1
3009 h();
3010 }
3011 }",
3012 "foo.cpp",
3013 |metric| {
3014 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3016 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3017 insta::assert_json_snapshot!(
3018 metric.cognitive,
3019 @r###"
3020 {
3021 "sum": 1.0,
3022 "average": 1.0,
3023 "min": 0.0,
3024 "max": 1.0
3025 }"###
3026 );
3027 },
3028 );
3029 }
3030
3031 #[test]
3032 fn c_try_multiple_catches() {
3033 check_metrics::<CppParser>(
3034 "void f() {
3035 try {
3036 g();
3037 } catch (const std::runtime_error& e) { // +1
3038 h();
3039 } catch (const std::logic_error& e) { // +1
3040 i();
3041 } catch (...) { // +1
3042 j();
3043 }
3044 }",
3045 "foo.cpp",
3046 |metric| {
3047 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3049 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3050 insta::assert_json_snapshot!(
3051 metric.cognitive,
3052 @r###"
3053 {
3054 "sum": 3.0,
3055 "average": 3.0,
3056 "min": 0.0,
3057 "max": 3.0
3058 }"###
3059 );
3060 },
3061 );
3062 }
3063
3064 #[test]
3065 fn c_try_catch_in_loop() {
3066 check_metrics::<CppParser>(
3067 "void f() {
3068 for (int i = 0; i < 10; ++i) { // +1
3069 try {
3070 g();
3071 } catch (const std::exception& e) { // +2 (nesting = 1)
3072 h();
3073 }
3074 }
3075 }",
3076 "foo.cpp",
3077 |metric| {
3078 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3080 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3081 insta::assert_json_snapshot!(
3082 metric.cognitive,
3083 @r###"
3084 {
3085 "sum": 3.0,
3086 "average": 3.0,
3087 "min": 0.0,
3088 "max": 3.0
3089 }"###
3090 );
3091 },
3092 );
3093 }
3094
3095 #[test]
3096 fn c_range_based_for() {
3097 check_metrics::<CppParser>(
3098 "int sum(const std::vector<int>& v) {
3099 int s = 0;
3100 for (int x : v) { // +1
3101 s += x;
3102 }
3103 return s;
3104 }",
3105 "foo.cpp",
3106 |metric| {
3107 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3111 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3112 insta::assert_json_snapshot!(
3113 metric.cognitive,
3114 @r###"
3115 {
3116 "sum": 1.0,
3117 "average": 1.0,
3118 "min": 0.0,
3119 "max": 1.0
3120 }"###
3121 );
3122 },
3123 );
3124 }
3125
3126 #[test]
3127 fn c_nested_range_based_for() {
3128 check_metrics::<CppParser>(
3129 "void f(const std::vector<std::vector<int>>& vv) {
3130 for (const auto& row : vv) { // +1
3131 for (int x : row) { // +2 (nesting = 1)
3132 g(x);
3133 }
3134 }
3135 }",
3136 "foo.cpp",
3137 |metric| {
3138 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3141 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3142 insta::assert_json_snapshot!(
3143 metric.cognitive,
3144 @r###"
3145 {
3146 "sum": 3.0,
3147 "average": 3.0,
3148 "min": 0.0,
3149 "max": 3.0
3150 }"###
3151 );
3152 },
3153 );
3154 }
3155
3156 #[test]
3157 fn c_nested_for() {
3158 check_metrics::<CppParser>(
3159 "void f(int n, int m) {
3160 for (int i = 0; i < n; ++i) { // +1
3161 for (int j = 0; j < m; ++j) { // +2 (nesting = 1)
3162 for (int k = 0; k < 4; ++k) { // +3 (nesting = 2)
3163 g(i, j, k);
3164 }
3165 }
3166 }
3167 }",
3168 "foo.c",
3169 |metric| {
3170 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
3172 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
3173 insta::assert_json_snapshot!(
3174 metric.cognitive,
3175 @r###"
3176 {
3177 "sum": 6.0,
3178 "average": 6.0,
3179 "min": 0.0,
3180 "max": 6.0
3181 }"###
3182 );
3183 },
3184 );
3185 }
3186
3187 #[test]
3188 fn c_nested_while() {
3189 check_metrics::<CppParser>(
3190 "void f(int n) {
3191 while (n > 0) { // +1
3192 while (n % 2 == 0) { // +2 (nesting = 1)
3193 n /= 2;
3194 }
3195 n -= 1;
3196 }
3197 }",
3198 "foo.c",
3199 |metric| {
3200 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3202 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3203 insta::assert_json_snapshot!(
3204 metric.cognitive,
3205 @r###"
3206 {
3207 "sum": 3.0,
3208 "average": 3.0,
3209 "min": 0.0,
3210 "max": 3.0
3211 }"###
3212 );
3213 },
3214 );
3215 }
3216
3217 #[test]
3218 fn c_recursion() {
3219 check_metrics::<CppParser>(
3225 "int fact(int n) {
3226 if (n <= 1) { // +1
3227 return 1;
3228 }
3229 return n * fact(n - 1); // recursion: currently not counted
3230 }",
3231 "foo.c",
3232 |metric| {
3233 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3235 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3236 insta::assert_json_snapshot!(
3237 metric.cognitive,
3238 @r###"
3239 {
3240 "sum": 1.0,
3241 "average": 1.0,
3242 "min": 0.0,
3243 "max": 1.0
3244 }"###
3245 );
3246 },
3247 );
3248 }
3249
3250 #[test]
3251 fn c_goto_sibling_jump() {
3252 check_metrics::<CppParser>(
3253 "void f(int n) {
3254 if (n < 0) { // +1
3255 goto err; // +1
3256 }
3257 if (n > 100) { // +1
3258 goto err; // +1
3259 }
3260 return;
3261 err:
3262 abort();
3263 }",
3264 "foo.c",
3265 |metric| {
3266 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
3269 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
3270 insta::assert_json_snapshot!(
3271 metric.cognitive,
3272 @r###"
3273 {
3274 "sum": 4.0,
3275 "average": 4.0,
3276 "min": 0.0,
3277 "max": 4.0
3278 }"###
3279 );
3280 },
3281 );
3282 }
3283
3284 #[test]
3285 fn c_lambda_inside_function() {
3286 check_metrics::<CppParser>(
3293 "int f(const std::vector<int>& v) {
3294 auto pred = [](int x) {
3295 if (x > 0) { // +2 (lambda nesting = 1)
3296 return true;
3297 }
3298 return false;
3299 };
3300 return std::count_if(v.begin(), v.end(), pred);
3301 }",
3302 "foo.cpp",
3303 |metric| {
3304 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
3306 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
3307 insta::assert_json_snapshot!(
3308 metric.cognitive,
3309 @r###"
3310 {
3311 "sum": 2.0,
3312 "average": 1.0,
3313 "min": 0.0,
3314 "max": 2.0
3315 }"###
3316 );
3317 },
3318 );
3319 }
3320
3321 #[test]
3322 fn c_switch_fall_through() {
3323 check_metrics::<CppParser>(
3327 "void f(int n) {
3328 switch (n) { // +1
3329 case 1:
3330 case 2:
3331 g();
3332 // fall-through
3333 case 3:
3334 h();
3335 break;
3336 default:
3337 i();
3338 break;
3339 }
3340 }",
3341 "foo.c",
3342 |metric| {
3343 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3344 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3345 insta::assert_json_snapshot!(
3346 metric.cognitive,
3347 @r###"
3348 {
3349 "sum": 1.0,
3350 "average": 1.0,
3351 "min": 0.0,
3352 "max": 1.0
3353 }"###
3354 );
3355 },
3356 );
3357 }
3358
3359 #[test]
3360 fn c_switch_in_loop() {
3361 check_metrics::<CppParser>(
3362 "void f(int n) {
3363 for (int i = 0; i < n; ++i) { // +1
3364 switch (i % 3) { // +2 (nesting = 1)
3365 case 0:
3366 a();
3367 break;
3368 case 1:
3369 b();
3370 break;
3371 default:
3372 c();
3373 break;
3374 }
3375 }
3376 }",
3377 "foo.c",
3378 |metric| {
3379 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3381 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3382 insta::assert_json_snapshot!(
3383 metric.cognitive,
3384 @r###"
3385 {
3386 "sum": 3.0,
3387 "average": 3.0,
3388 "min": 0.0,
3389 "max": 3.0
3390 }"###
3391 );
3392 },
3393 );
3394 }
3395
3396 #[test]
3397 fn c_macro_expanded_control_flow() {
3398 check_metrics::<CppParser>(
3403 "#define CHECK(x) do { if (!(x)) return; } while (0)
3404 void f(int a, int b) {
3405 CHECK(a); // expansion is opaque: 0
3406 if (b < 0) { // +1
3407 return;
3408 }
3409 }",
3410 "foo.c",
3411 |metric| {
3412 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3414 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3415 insta::assert_json_snapshot!(
3416 metric.cognitive,
3417 @r###"
3418 {
3419 "sum": 1.0,
3420 "average": 1.0,
3421 "min": 0.0,
3422 "max": 1.0
3423 }"###
3424 );
3425 },
3426 );
3427 }
3428
3429 #[test]
3430 fn mozjs_switch() {
3431 check_metrics::<MozjsParser>(
3432 "function f() {
3433 switch (1) { // +1
3434 case 1:
3435 window.print(\"one\");
3436 break;
3437 case 2:
3438 window.print(\"two\");
3439 break;
3440 case 3:
3441 window.print(\"three\");
3442 break;
3443 default:
3444 window.print(\"all\");
3445 break;
3446 }
3447 }",
3448 "foo.js",
3449 |metric| {
3450 insta::assert_json_snapshot!(
3451 metric.cognitive,
3452 @r###"
3453 {
3454 "sum": 1.0,
3455 "average": 1.0,
3456 "min": 0.0,
3457 "max": 1.0
3458 }"###
3459 );
3460 },
3461 );
3462 }
3463
3464 #[test]
3465 fn javascript_switch() {
3466 check_metrics::<JavascriptParser>(
3467 "function f() {
3468 switch (x) { // +1
3469 case 1:
3470 console.log(\"one\");
3471 break;
3472 case 2:
3473 console.log(\"two\");
3474 break;
3475 default:
3476 console.log(\"other\");
3477 break;
3478 }
3479 }",
3480 "foo.js",
3481 |metric| {
3482 insta::assert_json_snapshot!(
3483 metric.cognitive,
3484 @r###"
3485 {
3486 "sum": 1.0,
3487 "average": 1.0,
3488 "min": 0.0,
3489 "max": 1.0
3490 }"###
3491 );
3492 },
3493 );
3494 }
3495
3496 #[test]
3497 fn python_ternary_operator() {
3498 check_metrics::<PythonParser>(
3499 "def f(a, b):
3500 if a % 2: # +1
3501 return 'c' if a else 'd' # +2
3502 return 'a' if a else 'b' # +1",
3503 "foo.py",
3504 |metric| {
3505 insta::assert_json_snapshot!(
3506 metric.cognitive,
3507 @r###"
3508 {
3509 "sum": 4.0,
3510 "average": 4.0,
3511 "min": 0.0,
3512 "max": 4.0
3513 }"###
3514 );
3515 },
3516 );
3517 }
3518
3519 #[test]
3520 fn python_nested_functions_lambdas() {
3521 check_metrics::<PythonParser>(
3522 "def f(a, b):
3523 def foo(a):
3524 if a: # +2 (+1 nesting)
3525 return 1
3526 # +3 (+1 for boolean sequence +2 for lambda nesting)
3527 bar = lambda a: lambda b: b or True or True
3528 return bar(foo(a))(a)",
3529 "foo.py",
3530 |metric| {
3531 insta::assert_json_snapshot!(
3533 metric.cognitive,
3534 @r###"
3535 {
3536 "sum": 5.0,
3537 "average": 1.25,
3538 "min": 0.0,
3539 "max": 3.0
3540 }"###
3541 );
3542 },
3543 );
3544 }
3545
3546 #[test]
3547 fn python_real_function() {
3548 check_metrics::<PythonParser>(
3549 "def process_raw_constant(constant, min_word_length):
3550 processed_words = []
3551 raw_camelcase_words = []
3552 for raw_word in re.findall(r'[a-z]+', constant): # +1
3553 word = raw_word.strip()
3554 if ( # +2 (+1 if and +1 nesting)
3555 len(word) >= min_word_length
3556 and not (word.startswith('-') or word.endswith('-')) # +2 operators
3557 ):
3558 if is_camel_case_word(word): # +3 (+1 if and +2 nesting)
3559 raw_camelcase_words.append(word)
3560 else: # +1 else
3561 processed_words.append(word.lower())
3562 return processed_words, raw_camelcase_words",
3563 "foo.py",
3564 |metric| {
3565 insta::assert_json_snapshot!(
3566 metric.cognitive,
3567 @r###"
3568 {
3569 "sum": 9.0,
3570 "average": 9.0,
3571 "min": 0.0,
3572 "max": 9.0
3573 }"###
3574 );
3575 },
3576 );
3577 }
3578
3579 #[test]
3580 fn rust_if_let_else_if_else() {
3581 check_metrics::<RustParser>(
3582 "pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
3583 debugln!(\"usage::create_usage_no_title;\");
3584 if let Some(u) = p.meta.usage_str { // +1
3585 String::from(&*u)
3586 } else if used.is_empty() { // +1
3587 create_help_usage(p, true)
3588 } else { // +1
3589 create_smart_usage(p, used)
3590 }
3591 }",
3592 "foo.rs",
3593 |metric| {
3594 insta::assert_json_snapshot!(
3595 metric.cognitive,
3596 @r###"
3597 {
3598 "sum": 3.0,
3599 "average": 3.0,
3600 "min": 0.0,
3601 "max": 3.0
3602 }"###
3603 );
3604 },
3605 );
3606 }
3607
3608 #[test]
3609 fn typescript_if_else_if_else() {
3610 check_metrics::<TypescriptParser>(
3611 "function foo() {
3612 if (this._closed) return Promise.resolve(); // +1
3613 if (this._tempDirectory) { // +1
3614 this.kill();
3615 } else if (this.connection) { // +1
3616 this.kill();
3617 } else { // +1
3618 throw new Error(`Error`);
3619 }
3620 helper.removeEventListeners(this._listeners);
3621 return this._processClosing;
3622 }",
3623 "foo.ts",
3624 |metric| {
3625 insta::assert_json_snapshot!(
3626 metric.cognitive,
3627 @r###"
3628 {
3629 "sum": 4.0,
3630 "average": 4.0,
3631 "min": 0.0,
3632 "max": 4.0
3633 }"###
3634 );
3635 },
3636 );
3637 }
3638
3639 #[test]
3640 fn java_no_cognitive() {
3641 check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
3642 insta::assert_json_snapshot!(
3643 metric.cognitive,
3644 @r###"
3645 {
3646 "sum": 0.0,
3647 "average": null,
3648 "min": 0.0,
3649 "max": 0.0
3650 }
3651 "###
3652 );
3653 });
3654 }
3655
3656 #[test]
3657 fn java_single_branch_function() {
3658 check_metrics::<JavaParser>(
3659 "class X {
3660 public static void print(boolean a){
3661 if(a){ // +1
3662 System.out.println(\"test1\");
3663 }
3664 }
3665 }",
3666 "foo.java",
3667 |metric| {
3668 insta::assert_json_snapshot!(
3669 metric.cognitive,
3670 @r###"
3671 {
3672 "sum": 1.0,
3673 "average": 1.0,
3674 "min": 0.0,
3675 "max": 1.0
3676 }
3677 "###
3678 );
3679 },
3680 );
3681 }
3682
3683 #[test]
3684 fn java_multiple_branch_function() {
3685 check_metrics::<JavaParser>(
3686 "class X {
3687 public static void print(boolean a, boolean b){
3688 if(a){ // +1
3689 System.out.println(\"test1\");
3690 }
3691 if(b){ // +1
3692 System.out.println(\"test2\");
3693 }
3694 else { // +1
3695 System.out.println(\"test3\");
3696 }
3697 }
3698 }",
3699 "foo.java",
3700 |metric| {
3701 insta::assert_json_snapshot!(
3702 metric.cognitive,
3703 @r###"
3704 {
3705 "sum": 3.0,
3706 "average": 3.0,
3707 "min": 0.0,
3708 "max": 3.0
3709 }
3710 "###
3711 );
3712 },
3713 );
3714 }
3715
3716 #[test]
3717 fn java_compound_conditions() {
3718 check_metrics::<JavaParser>(
3719 "class X {
3720 public static void print(boolean a, boolean b, boolean c, boolean d){
3721 if(a && b){ // +2 (+1 &&)
3722 System.out.println(\"test1\");
3723 }
3724 if(c && d){ // +2 (+1 &&)
3725 System.out.println(\"test2\");
3726 }
3727 }
3728 }",
3729 "foo.java",
3730 |metric| {
3731 insta::assert_json_snapshot!(
3732 metric.cognitive,
3733 @r###"
3734 {
3735 "sum": 4.0,
3736 "average": 4.0,
3737 "min": 0.0,
3738 "max": 4.0
3739 }"###
3740 );
3741 },
3742 );
3743 }
3744
3745 #[test]
3746 fn java_switch_statement() {
3747 check_metrics::<JavaParser>(
3748 "class X {
3749 public static void print(boolean a, boolean b, boolean c, boolean d){
3750 switch(expr){ //+1
3751 case 1:
3752 System.out.println(\"test1\");
3753 break;
3754 case 2:
3755 System.out.println(\"test2\");
3756 break;
3757 default:
3758 System.out.println(\"test\");
3759 }
3760 }
3761 }",
3762 "foo.java",
3763 |metric| {
3764 insta::assert_json_snapshot!(
3765 metric.cognitive,
3766 @r###"
3767 {
3768 "sum": 1.0,
3769 "average": 1.0,
3770 "min": 0.0,
3771 "max": 1.0
3772 }"###
3773 );
3774 },
3775 );
3776 }
3777
3778 #[test]
3779 fn java_switch_expression() {
3780 check_metrics::<JavaParser>(
3781 "class X {
3782 public static void print(boolean a, boolean b, boolean c, boolean d){
3783 switch(expr){ // +1
3784 case 1 -> System.out.println(\"test1\");
3785 case 2 -> System.out.println(\"test2\");
3786 default -> System.out.println(\"test\");
3787 }
3788 }
3789 }",
3790 "foo.java",
3791 |metric| {
3792 insta::assert_json_snapshot!(
3793 metric.cognitive,
3794 @r###"
3795 {
3796 "sum": 1.0,
3797 "average": 1.0,
3798 "min": 0.0,
3799 "max": 1.0
3800 }"###
3801 );
3802 },
3803 );
3804 }
3805
3806 #[test]
3807 fn java_not_booleans() {
3808 check_metrics::<JavaParser>(
3809 "class X {
3810 public static void print(boolean a, boolean b, boolean c, boolean d){
3811 if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
3812 printf(\"test\");
3813 }
3814 }
3815 }",
3816 "foo.java",
3817 |metric| {
3818 insta::assert_json_snapshot!(
3819 metric.cognitive,
3820 @r###"
3821 {
3822 "sum": 3.0,
3823 "average": 3.0,
3824 "min": 0.0,
3825 "max": 3.0
3826 }"###
3827 );
3828 },
3829 );
3830 }
3831
3832 #[test]
3833 fn java_enhanced_for_statement() {
3834 check_metrics::<JavaParser>(
3835 "class X {
3836 public static int sum(int[] xs) {
3837 int s = 0;
3838 for (int x : xs) { // +1
3839 s += x;
3840 }
3841 return s;
3842 }
3843 }",
3844 "foo.java",
3845 |metric| {
3846 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3850 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3851 insta::assert_json_snapshot!(
3852 metric.cognitive,
3853 @r###"
3854 {
3855 "sum": 1.0,
3856 "average": 1.0,
3857 "min": 0.0,
3858 "max": 1.0
3859 }"###
3860 );
3861 },
3862 );
3863 }
3864
3865 #[test]
3866 fn java_nested_enhanced_for_statement() {
3867 check_metrics::<JavaParser>(
3868 "class X {
3869 public static void f(int[][] xss) {
3870 for (int[] xs : xss) { // +1
3871 for (int x : xs) { // +2 (nesting = 1)
3872 g(x);
3873 }
3874 }
3875 }
3876 }",
3877 "foo.java",
3878 |metric| {
3879 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
3882 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
3883 insta::assert_json_snapshot!(
3884 metric.cognitive,
3885 @r###"
3886 {
3887 "sum": 3.0,
3888 "average": 3.0,
3889 "min": 0.0,
3890 "max": 3.0
3891 }"###
3892 );
3893 },
3894 );
3895 }
3896
3897 #[test]
3898 fn java_ternary() {
3899 check_metrics::<JavaParser>(
3904 "class X {
3905 public static boolean check(int a) {
3906 return a > 0 ? true : false; // +1
3907 }
3908 }",
3909 "foo.java",
3910 |metric| {
3911 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
3912 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
3913 insta::assert_json_snapshot!(
3914 metric.cognitive,
3915 @r###"
3916 {
3917 "sum": 1.0,
3918 "average": 1.0,
3919 "min": 0.0,
3920 "max": 1.0
3921 }"###
3922 );
3923 },
3924 );
3925 }
3926
3927 #[test]
3928 fn java_nested_ternary() {
3929 check_metrics::<JavaParser>(
3934 "class X {
3935 public static String classify(int a, int b) {
3936 if (a > 0) { // +1
3937 return b > 0 ? (b > 10 ? \"big\" : \"small\") : \"neg\"; // +2, +3
3938 }
3939 return \"zero\";
3940 }
3941 }",
3942 "foo.java",
3943 |metric| {
3944 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
3945 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
3946 insta::assert_json_snapshot!(
3947 metric.cognitive,
3948 @r###"
3949 {
3950 "sum": 6.0,
3951 "average": 6.0,
3952 "min": 0.0,
3953 "max": 6.0
3954 }"###
3955 );
3956 },
3957 );
3958 }
3959
3960 #[test]
3961 fn java_labeled_break_continue() {
3962 check_metrics::<JavaParser>(
3970 "class X {
3971 void scan(int[][] m) {
3972 outer:
3973 for (int i = 0; i < m.length; i++) { // +1
3974 for (int j = 0; j < m[i].length; j++) { // +2
3975 if (m[i][j] < 0) continue outer; // +3, +1
3976 if (m[i][j] > 100) break outer; // +3, +1
3977 }
3978 }
3979 }
3980 }",
3981 "foo.java",
3982 |metric| {
3983 assert_eq!(metric.cognitive.cognitive_sum(), 11.0);
3984 assert_eq!(metric.cognitive.cognitive_max(), 11.0);
3985 insta::assert_json_snapshot!(
3986 metric.cognitive,
3987 @r###"
3988 {
3989 "sum": 11.0,
3990 "average": 11.0,
3991 "min": 0.0,
3992 "max": 11.0
3993 }"###
3994 );
3995 },
3996 );
3997 }
3998
3999 #[test]
4000 fn java_unlabeled_break_continue_not_counted() {
4001 check_metrics::<JavaParser>(
4006 "class X {
4007 void scan(int[] m) {
4008 for (int i = 0; i < m.length; i++) { // +1
4009 if (m[i] < 0) continue; // +2, +0
4010 if (m[i] > 100) break; // +2, +0
4011 }
4012 }
4013 }",
4014 "foo.java",
4015 |metric| {
4016 assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
4017 assert_eq!(metric.cognitive.cognitive_max(), 5.0);
4018 insta::assert_json_snapshot!(
4019 metric.cognitive,
4020 @r###"
4021 {
4022 "sum": 5.0,
4023 "average": 5.0,
4024 "min": 0.0,
4025 "max": 5.0
4026 }"###
4027 );
4028 },
4029 );
4030 }
4031
4032 #[test]
4033 fn csharp_no_cognitive() {
4034 check_metrics::<CsharpParser>("int a = 42;", "foo.cs", |metric| {
4035 insta::assert_json_snapshot!(
4036 metric.cognitive,
4037 @r###"
4038 {
4039 "sum": 0.0,
4040 "average": null,
4041 "min": 0.0,
4042 "max": 0.0
4043 }
4044 "###
4045 );
4046 });
4047 }
4048
4049 #[test]
4050 fn csharp_single_branch_function() {
4051 check_metrics::<CsharpParser>(
4052 "class X {
4053 public static void Print(bool a) {
4054 if (a) {
4055 System.Console.WriteLine(\"test1\");
4056 }
4057 }
4058 }",
4059 "foo.cs",
4060 |metric| {
4061 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
4063 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
4064 insta::assert_json_snapshot!(metric.cognitive);
4065 },
4066 );
4067 }
4068
4069 #[test]
4070 fn csharp_multiple_branch_function() {
4071 check_metrics::<CsharpParser>(
4072 "class X {
4073 public static void Print(bool a, bool b) {
4074 if (a) {
4075 System.Console.WriteLine(\"test1\");
4076 }
4077 if (b) {
4078 System.Console.WriteLine(\"test2\");
4079 } else {
4080 System.Console.WriteLine(\"test3\");
4081 }
4082 }
4083 }",
4084 "foo.cs",
4085 |metric| {
4086 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
4088 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
4089 insta::assert_json_snapshot!(metric.cognitive);
4090 },
4091 );
4092 }
4093
4094 #[test]
4095 fn csharp_compound_conditions() {
4096 check_metrics::<CsharpParser>(
4097 "class X {
4098 public static void Print(bool a, bool b, bool c, bool d) {
4099 if (a && b) {
4100 System.Console.WriteLine(\"test1\");
4101 }
4102 if (c && d) {
4103 System.Console.WriteLine(\"test2\");
4104 }
4105 }
4106 }",
4107 "foo.cs",
4108 |metric| {
4109 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
4111 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
4112 insta::assert_json_snapshot!(metric.cognitive);
4113 },
4114 );
4115 }
4116
4117 #[test]
4118 fn csharp_switch_statement() {
4119 check_metrics::<CsharpParser>(
4120 "class X {
4121 public static void Print(int expr) {
4122 switch (expr) {
4123 case 1:
4124 System.Console.WriteLine(\"test1\");
4125 break;
4126 case 2:
4127 System.Console.WriteLine(\"test2\");
4128 break;
4129 default:
4130 System.Console.WriteLine(\"test\");
4131 break;
4132 }
4133 }
4134 }",
4135 "foo.cs",
4136 |metric| {
4137 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
4139 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
4140 insta::assert_json_snapshot!(metric.cognitive);
4141 },
4142 );
4143 }
4144
4145 #[test]
4146 fn csharp_switch_expression() {
4147 check_metrics::<CsharpParser>(
4148 "class X {
4149 public static string Name(int expr) =>
4150 expr switch {
4151 1 => \"one\",
4152 2 => \"two\",
4153 _ => \"other\"
4154 };
4155 }",
4156 "foo.cs",
4157 |metric| {
4158 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
4160 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
4161 insta::assert_json_snapshot!(metric.cognitive);
4162 },
4163 );
4164 }
4165
4166 #[test]
4167 fn csharp_not_booleans() {
4168 check_metrics::<CsharpParser>(
4169 "class X {
4170 public static void Print(bool a, bool b, bool c) {
4171 if (a && !(b && c)) {
4172 System.Console.WriteLine(\"test\");
4173 }
4174 }
4175 }",
4176 "foo.cs",
4177 |metric| {
4178 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
4180 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
4181 insta::assert_json_snapshot!(metric.cognitive);
4182 },
4183 );
4184 }
4185
4186 #[test]
4187 fn csharp_ternary() {
4188 check_metrics::<CsharpParser>(
4192 "class X {
4193 public static bool Check(int a) {
4194 return a > 0 ? true : false; // +1
4195 }
4196 }",
4197 "foo.cs",
4198 |metric| {
4199 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
4200 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
4201 insta::assert_json_snapshot!(
4202 metric.cognitive,
4203 @r###"
4204 {
4205 "sum": 1.0,
4206 "average": 1.0,
4207 "min": 0.0,
4208 "max": 1.0
4209 }"###
4210 );
4211 },
4212 );
4213 }
4214
4215 #[test]
4216 fn csharp_nested_ternary() {
4217 check_metrics::<CsharpParser>(
4222 "class X {
4223 public static string Classify(int a, int b) {
4224 if (a > 0) { // +1
4225 return b > 0 ? (b > 10 ? \"big\" : \"small\") : \"neg\"; // +2, +3
4226 }
4227 return \"zero\";
4228 }
4229 }",
4230 "foo.cs",
4231 |metric| {
4232 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
4233 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
4234 insta::assert_json_snapshot!(
4235 metric.cognitive,
4236 @r###"
4237 {
4238 "sum": 6.0,
4239 "average": 6.0,
4240 "min": 0.0,
4241 "max": 6.0
4242 }"###
4243 );
4244 },
4245 );
4246 }
4247
4248 #[test]
4249 fn csharp_goto_statement() {
4250 check_metrics::<CsharpParser>(
4255 "class X {
4256 int Classify(int x) {
4257 if (x < 0) goto neg; // +1, +1
4258 return x;
4259 neg:
4260 return -x;
4261 }
4262 }",
4263 "foo.cs",
4264 |metric| {
4265 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
4266 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
4267 insta::assert_json_snapshot!(
4268 metric.cognitive,
4269 @r###"
4270 {
4271 "sum": 2.0,
4272 "average": 2.0,
4273 "min": 0.0,
4274 "max": 2.0
4275 }"###
4276 );
4277 },
4278 );
4279 }
4280
4281 #[test]
4282 fn csharp_goto_case_and_default() {
4283 check_metrics::<CsharpParser>(
4288 "class X {
4289 int Walk(int x) {
4290 switch (x) { // +1
4291 case 1: goto case 2; // +1
4292 case 2: return 2;
4293 case 3: goto default; // +1
4294 default: return 0;
4295 }
4296 }
4297 }",
4298 "foo.cs",
4299 |metric| {
4300 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
4301 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
4302 insta::assert_json_snapshot!(
4303 metric.cognitive,
4304 @r###"
4305 {
4306 "sum": 3.0,
4307 "average": 3.0,
4308 "min": 0.0,
4309 "max": 3.0
4310 }"###
4311 );
4312 },
4313 );
4314 }
4315
4316 #[test]
4317 fn csharp_unlabeled_break_not_counted() {
4318 check_metrics::<CsharpParser>(
4325 "class X {
4326 void Scan(int[] m) {
4327 for (int i = 0; i < m.Length; i++) { // +1
4328 if (m[i] < 0) break; // +2, +0
4329 }
4330 }
4331 }",
4332 "foo.cs",
4333 |metric| {
4334 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
4335 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
4336 insta::assert_json_snapshot!(
4337 metric.cognitive,
4338 @r###"
4339 {
4340 "sum": 3.0,
4341 "average": 3.0,
4342 "min": 0.0,
4343 "max": 3.0
4344 }"###
4345 );
4346 },
4347 );
4348 }
4349
4350 #[test]
4351 fn perl_no_cognitive() {
4352 check_metrics::<PerlParser>("my $a = 42;", "foo.pl", |metric| {
4353 insta::assert_json_snapshot!(metric.cognitive, @r#"
4354 {
4355 "sum": 0.0,
4356 "average": null,
4357 "min": 0.0,
4358 "max": 0.0
4359 }
4360 "#);
4361 });
4362 }
4363
4364 #[test]
4365 fn perl_simple_function() {
4366 check_metrics::<PerlParser>(
4367 "sub f {
4368 return 1;
4369 }",
4370 "foo.pl",
4371 |metric| {
4372 insta::assert_json_snapshot!(metric.cognitive, @r#"
4373 {
4374 "sum": 0.0,
4375 "average": 0.0,
4376 "min": 0.0,
4377 "max": 0.0
4378 }
4379 "#);
4380 },
4381 );
4382 }
4383
4384 #[test]
4385 fn perl_sequence_same_booleans() {
4386 check_metrics::<PerlParser>(
4387 "sub f {
4388 if ($a && $b && $c) { # +1 if, +1 first &&-chain
4389 print 'x';
4390 }
4391 }",
4392 "foo.pl",
4393 |metric| {
4394 insta::assert_json_snapshot!(metric.cognitive, @r#"
4395 {
4396 "sum": 2.0,
4397 "average": 2.0,
4398 "min": 0.0,
4399 "max": 2.0
4400 }
4401 "#);
4402 },
4403 );
4404 }
4405
4406 #[test]
4407 fn perl_sequence_different_booleans() {
4408 check_metrics::<PerlParser>(
4409 "sub f {
4410 if ($a && $b || $c) { # +1 if, +1 &&, +1 ||
4411 print 'x';
4412 }
4413 }",
4414 "foo.pl",
4415 |metric| {
4416 insta::assert_json_snapshot!(metric.cognitive, @r#"
4417 {
4418 "sum": 3.0,
4419 "average": 3.0,
4420 "min": 0.0,
4421 "max": 3.0
4422 }
4423 "#);
4424 },
4425 );
4426 }
4427
4428 #[test]
4429 fn perl_compound_short_circuit_assignment_249() {
4430 check_metrics::<PerlParser>(
4437 "sub f {
4438 my ($x, $y, $z) = @_;
4439 $x ||= 1; # +1 (||=)
4440 $y &&= 2; # +1 (&&=)
4441 $z //= 3; # +1 (//=)
4442 return $x;
4443 }",
4444 "foo.pl",
4445 |metric| {
4446 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
4447 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
4448 insta::assert_json_snapshot!(
4449 metric.cognitive,
4450 @r###"
4451 {
4452 "sum": 3.0,
4453 "average": 3.0,
4454 "min": 0.0,
4455 "max": 3.0
4456 }"###
4457 );
4458 },
4459 );
4460 }
4461
4462 #[test]
4463 fn perl_not_booleans() {
4464 check_metrics::<PerlParser>(
4465 "sub f {
4466 if ($a && !($b && $c)) { # +1 if, +1 &&, +1 inner &&
4467 print 'x';
4468 }
4469 }",
4470 "foo.pl",
4471 |metric| {
4472 insta::assert_json_snapshot!(metric.cognitive, @r#"
4473 {
4474 "sum": 3.0,
4475 "average": 3.0,
4476 "min": 0.0,
4477 "max": 3.0
4478 }
4479 "#);
4480 },
4481 );
4482 }
4483
4484 #[test]
4485 fn perl_1_level_nesting() {
4486 check_metrics::<PerlParser>(
4487 "sub f {
4488 for my $i (1..3) { # +1 for
4489 if ($i % 2) { # +2 if (nested 1)
4490 print $i;
4491 }
4492 }
4493 }",
4494 "foo.pl",
4495 |metric| {
4496 insta::assert_json_snapshot!(metric.cognitive, @r#"
4497 {
4498 "sum": 3.0,
4499 "average": 3.0,
4500 "min": 0.0,
4501 "max": 3.0
4502 }
4503 "#);
4504 },
4505 );
4506 }
4507
4508 #[test]
4509 fn perl_2_level_nesting() {
4510 check_metrics::<PerlParser>(
4511 "sub f {
4512 for my $i (1..3) { # +1 for
4513 while ($n > 0) { # +2 while (nested 1)
4514 if ($n % 2) { # +3 if (nested 2)
4515 $n--;
4516 }
4517 }
4518 }
4519 }",
4520 "foo.pl",
4521 |metric| {
4522 insta::assert_json_snapshot!(metric.cognitive, @r#"
4523 {
4524 "sum": 6.0,
4525 "average": 6.0,
4526 "min": 0.0,
4527 "max": 6.0
4528 }
4529 "#);
4530 },
4531 );
4532 }
4533
4534 #[test]
4535 fn perl_break_continue() {
4536 check_metrics::<PerlParser>(
4540 "sub f {
4541 while (1) { # +1 while (nesting becomes 1)
4542 last if $done; # +2 postfix-if at nesting=1
4543 next; # +0 bare loop control
4544 }
4545 }",
4546 "foo.pl",
4547 |metric| {
4548 insta::assert_json_snapshot!(metric.cognitive, @r#"
4549 {
4550 "sum": 3.0,
4551 "average": 3.0,
4552 "min": 0.0,
4553 "max": 3.0
4554 }
4555 "#);
4556 },
4557 );
4558 }
4559
4560 #[test]
4561 fn perl_if_elsif_else() {
4562 check_metrics::<PerlParser>(
4563 "sub f {
4564 if ($x) { # +1 if
4565 print 'a';
4566 } elsif ($y) { # +1 elsif
4567 print 'b';
4568 } else { # +1 else
4569 print 'c';
4570 }
4571 }",
4572 "foo.pl",
4573 |metric| {
4574 insta::assert_json_snapshot!(metric.cognitive, @r#"
4575 {
4576 "sum": 3.0,
4577 "average": 3.0,
4578 "min": 0.0,
4579 "max": 3.0
4580 }
4581 "#);
4582 },
4583 );
4584 }
4585
4586 #[test]
4587 fn perl_function_definition_without_sub_depth() {
4588 check_metrics::<PerlParser>(
4594 "method outer {
4595 sub inner {
4596 if (1) { } # +2 (depth=1)
4597 }
4598 }",
4599 "foo.pl",
4600 |metric| {
4601 insta::assert_json_snapshot!(metric.cognitive, @r#"
4602 {
4603 "sum": 2.0,
4604 "average": 1.0,
4605 "min": 0.0,
4606 "max": 2.0
4607 }
4608 "#);
4609 },
4610 );
4611 }
4612
4613 #[test]
4614 fn tsx_nested_if_for_with_booleans() {
4615 check_metrics::<TsxParser>(
4616 "function process(items: number[]) {
4617 if (items.length > 0) { // +1
4618 for (let i = 0; i < items.length; i++) { // +2 (nesting=1)
4619 if (items[i] > 0 && items[i] < 100) { // +3 (nesting=2) +1 (&&)
4620 console.log(items[i]);
4621 }
4622 }
4623 }
4624 }",
4625 "foo.tsx",
4626 |metric| {
4627 insta::assert_json_snapshot!(
4628 metric.cognitive,
4629 @r###"
4630 {
4631 "sum": 7.0,
4632 "average": 7.0,
4633 "min": 0.0,
4634 "max": 7.0
4635 }"###
4636 );
4637 },
4638 );
4639 }
4640
4641 #[test]
4642 fn typescript_nested_if_with_boolean_sequence() {
4643 check_metrics::<TypescriptParser>(
4644 "function validate(input: string, strict: boolean): boolean {
4645 if (input.length > 0) { // +1
4646 if (strict && input.trim() === input) { // +2 (nesting=1) +1 (&&)
4647 return true;
4648 }
4649 }
4650 return false;
4651 }",
4652 "foo.ts",
4653 |metric| {
4654 insta::assert_json_snapshot!(
4655 metric.cognitive,
4656 @r###"
4657 {
4658 "sum": 4.0,
4659 "average": 4.0,
4660 "min": 0.0,
4661 "max": 4.0
4662 }"###
4663 );
4664 },
4665 );
4666 }
4667
4668 #[test]
4669 fn typescript_try_catch_with_nesting() {
4670 check_metrics::<TypescriptParser>(
4671 "function fetchData(url: string): string {
4672 try {
4673 if (url.length === 0) { // +1
4674 throw new Error('empty url');
4675 }
4676 return url;
4677 } catch (e) { // +1
4678 if (e instanceof Error) { // +2 (nesting=1)
4679 return e.message;
4680 }
4681 return 'unknown error';
4682 }
4683 }",
4684 "foo.ts",
4685 |metric| {
4686 insta::assert_json_snapshot!(
4687 metric.cognitive,
4688 @r###"
4689 {
4690 "sum": 4.0,
4691 "average": 4.0,
4692 "min": 0.0,
4693 "max": 4.0
4694 }"###
4695 );
4696 },
4697 );
4698 }
4699
4700 #[test]
4701 fn kotlin_cognitive_control_flow() {
4702 check_metrics::<KotlinParser>(
4703 "fun process(x: Int, y: Int): String {
4704 if (x > 0) { // +1
4705 for (i in 1..x) { // +2 (nesting=1)
4706 if (i % 2 == 0) { // +3 (nesting=2)
4707 println(i)
4708 }
4709 }
4710 } else if (x < 0) { // +1 (else-if: flat +1 for else, if not counted as else-if)
4711 when (y) { // +2 (nesting=1)
4712 1 -> println(\"one\")
4713 2 -> println(\"two\")
4714 else -> println(\"other\")
4715 }
4716 } else { // +1
4717 while (y > 0) { // +2
4718 println(y)
4719 }
4720 }
4721 return if (x > y) \"big\" else \"small\"
4722 }",
4723 "foo.kt",
4724 |metric| {
4725 insta::assert_json_snapshot!(
4726 metric.cognitive,
4727 @r###"
4728 {
4729 "sum": 14.0,
4730 "average": 14.0,
4731 "min": 0.0,
4732 "max": 14.0
4733 }
4734 "###
4735 );
4736 },
4737 );
4738 }
4739
4740 #[test]
4741 fn kotlin_no_cognitive() {
4742 check_metrics::<KotlinParser>("fun main() { val x = 42 }", "foo.kt", |metric| {
4743 insta::assert_json_snapshot!(metric.cognitive, @r#"
4744 {
4745 "sum": 0.0,
4746 "average": 0.0,
4747 "min": 0.0,
4748 "max": 0.0
4749 }
4750 "#);
4751 });
4752 }
4753
4754 #[test]
4755 fn kotlin_simple_if_with_boolean() {
4756 check_metrics::<KotlinParser>(
4757 "fun test(a: Boolean, b: Boolean) { if (a && b) { val x = 1 } }",
4758 "foo.kt",
4759 |metric| {
4760 insta::assert_json_snapshot!(metric.cognitive, @r#"
4761 {
4762 "sum": 2.0,
4763 "average": 2.0,
4764 "min": 0.0,
4765 "max": 2.0
4766 }
4767 "#);
4768 },
4769 );
4770 }
4771
4772 #[test]
4773 fn kotlin_nesting() {
4774 check_metrics::<KotlinParser>(
4775 "fun test(items: List<Int>) {
4776 if (items.isNotEmpty()) {
4777 for (i in items) {
4778 if (i > 0) {
4779 println(i)
4780 }
4781 }
4782 }
4783 }",
4784 "foo.kt",
4785 |metric| {
4786 insta::assert_json_snapshot!(metric.cognitive, @r#"
4787 {
4788 "sum": 6.0,
4789 "average": 6.0,
4790 "min": 0.0,
4791 "max": 6.0
4792 }
4793 "#);
4794 },
4795 );
4796 }
4797
4798 #[test]
4799 fn kotlin_when_expression() {
4800 check_metrics::<KotlinParser>(
4801 "fun test(x: Int) { when { x > 10 -> val a = 1; x > 5 -> val b = 2; else -> val c = 3 } }",
4802 "foo.kt",
4803 |metric| {
4804 insta::assert_json_snapshot!(metric.cognitive, @r#"
4805 {
4806 "sum": 1.0,
4807 "average": 1.0,
4808 "min": 0.0,
4809 "max": 1.0
4810 }
4811 "#);
4812 },
4813 );
4814 }
4815
4816 #[test]
4817 fn kotlin_when_else_no_increment() {
4818 check_metrics::<KotlinParser>(
4819 "fun test(x: Int) {
4820 when (x) {
4821 1 -> println(\"one\")
4822 2 -> println(\"two\")
4823 else -> println(\"other\")
4824 }
4825 }",
4826 "foo.kt",
4827 |metric| {
4828 insta::assert_json_snapshot!(metric.cognitive, @r#"
4829 {
4830 "sum": 1.0,
4831 "average": 1.0,
4832 "min": 0.0,
4833 "max": 1.0
4834 }
4835 "#);
4836 },
4837 );
4838 }
4839
4840 #[test]
4841 fn kotlin_else_in_if_still_increments() {
4842 check_metrics::<KotlinParser>(
4843 "fun test(x: Int) {
4844 if (x > 0) {
4845 println(\"positive\")
4846 } else {
4847 println(\"non-positive\")
4848 }
4849 }",
4850 "foo.kt",
4851 |metric| {
4852 insta::assert_json_snapshot!(metric.cognitive, @r#"
4853 {
4854 "sum": 2.0,
4855 "average": 2.0,
4856 "min": 0.0,
4857 "max": 2.0
4858 }
4859 "#);
4860 },
4861 );
4862 }
4863
4864 #[test]
4865 fn kotlin_else_if_chain() {
4866 check_metrics::<KotlinParser>(
4867 "fun test(x: Int) {
4868 if (x > 10) {
4869 } else if (x > 5) {
4870 } else if (x > 0) {
4871 } else {
4872 }
4873 }",
4874 "foo.kt",
4875 |metric| {
4876 insta::assert_json_snapshot!(metric.cognitive, @r#"
4877 {
4878 "sum": 4.0,
4879 "average": 4.0,
4880 "min": 0.0,
4881 "max": 4.0
4882 }
4883 "#);
4884 },
4885 );
4886 }
4887
4888 #[test]
4889 fn kotlin_lambda_nesting() {
4890 check_metrics::<KotlinParser>(
4891 "fun test() { val f = { if (true) { } } }",
4892 "foo.kt",
4893 |metric| {
4894 insta::assert_json_snapshot!(metric.cognitive, @r#"
4895 {
4896 "sum": 2.0,
4897 "average": 1.0,
4898 "min": 0.0,
4899 "max": 2.0
4900 }
4901 "#);
4902 },
4903 );
4904 }
4905
4906 #[test]
4907 fn kotlin_secondary_constructor_depth() {
4908 check_metrics::<KotlinParser>(
4912 "class Foo {
4913 constructor(x: Int) {
4914 fun inner(): Boolean {
4915 if (x > 0) { return true } // +2 (depth=1)
4916 return false
4917 }
4918 }
4919 }",
4920 "foo.kt",
4921 |metric| {
4922 insta::assert_json_snapshot!(metric.cognitive, @r#"
4923 {
4924 "sum": 2.0,
4925 "average": 1.0,
4926 "min": 0.0,
4927 "max": 2.0
4928 }
4929 "#);
4930 },
4931 );
4932 }
4933
4934 #[test]
4935 fn go_no_cognitive() {
4936 check_metrics::<GoParser>("package main\nvar x = 42", "foo.go", |metric| {
4937 insta::assert_json_snapshot!(
4938 metric.cognitive,
4939 @r###"
4940 {
4941 "sum": 0.0,
4942 "average": null,
4943 "min": 0.0,
4944 "max": 0.0
4945 }
4946 "###
4947 );
4948 });
4949 }
4950
4951 #[test]
4952 fn go_simple_function() {
4953 check_metrics::<GoParser>(
4954 "package main
4955 func f(a, b bool) {
4956 if a && b { // +1 (if) +1 (&&)
4957 return
4958 }
4959 if a || b { // +1 (if) +1 (||)
4960 return
4961 }
4962 }",
4963 "foo.go",
4964 |metric| {
4965 insta::assert_json_snapshot!(
4966 metric.cognitive,
4967 @r###"
4968 {
4969 "sum": 4.0,
4970 "average": 4.0,
4971 "min": 0.0,
4972 "max": 4.0
4973 }
4974 "###
4975 );
4976 },
4977 );
4978 }
4979
4980 #[test]
4981 fn go_nesting() {
4982 check_metrics::<GoParser>(
4983 "package main
4984 func f(x int, items []int) {
4985 if x > 0 { // +1 (nesting 0)
4986 for _, v := range items { // +2 (nesting 1)
4987 if v > 0 { // +3 (nesting 2)
4988 println(v)
4989 }
4990 }
4991 }
4992 }",
4993 "foo.go",
4994 |metric| {
4995 insta::assert_json_snapshot!(
4996 metric.cognitive,
4997 @r###"
4998 {
4999 "sum": 6.0,
5000 "average": 6.0,
5001 "min": 0.0,
5002 "max": 6.0
5003 }
5004 "###
5005 );
5006 },
5007 );
5008 }
5009
5010 #[test]
5011 fn go_switch() {
5012 check_metrics::<GoParser>(
5013 "package main
5014 func f(x int) {
5015 switch x { // +1 (nesting 0)
5016 case 1:
5017 if x > 0 { // +2 (nesting 1)
5018 println(x)
5019 }
5020 default:
5021 println(x)
5022 }
5023 }",
5024 "foo.go",
5025 |metric| {
5026 insta::assert_json_snapshot!(
5027 metric.cognitive,
5028 @r###"
5029 {
5030 "sum": 3.0,
5031 "average": 3.0,
5032 "min": 0.0,
5033 "max": 3.0
5034 }
5035 "###
5036 );
5037 },
5038 );
5039 }
5040
5041 #[test]
5042 fn go_goto() {
5043 check_metrics::<GoParser>(
5044 "package main
5045 func f(n int) {
5046 if n > 10 { // +1 (nesting 0)
5047 goto end // +1 (goto)
5048 }
5049 end:
5050 return
5051 }",
5052 "foo.go",
5053 |metric| {
5054 insta::assert_json_snapshot!(
5055 metric.cognitive,
5056 @r###"
5057 {
5058 "sum": 2.0,
5059 "average": 2.0,
5060 "min": 0.0,
5061 "max": 2.0
5062 }
5063 "###
5064 );
5065 },
5066 );
5067 }
5068
5069 #[test]
5070 fn go_else_if_chain() {
5071 check_metrics::<GoParser>(
5072 "package main
5073 func f(x int) {
5074 if x > 0 { // +1 (nesting 0)
5075 println(x)
5076 } else if x < 0 { // +1 (else-if)
5077 println(-x)
5078 } else { // +1 (else)
5079 println(0)
5080 }
5081 }",
5082 "foo.go",
5083 |metric| {
5084 insta::assert_json_snapshot!(
5085 metric.cognitive,
5086 @r###"
5087 {
5088 "sum": 3.0,
5089 "average": 3.0,
5090 "min": 0.0,
5091 "max": 3.0
5092 }
5093 "###
5094 );
5095 },
5096 );
5097 }
5098
5099 #[test]
5100 fn go_labeled_break_continue() {
5101 check_metrics::<GoParser>(
5102 "package main
5103 func f() {
5104 outer:
5105 for i := 0; i < 3; i++ { // +1 (nesting 0)
5106 for j := 0; j < 3; j++ { // +2 (nesting 1)
5107 if i == j { // +3 (nesting 2)
5108 continue outer // +1 (labeled continue)
5109 }
5110 }
5111 }
5112 }",
5113 "foo.go",
5114 |metric| {
5115 insta::assert_json_snapshot!(
5116 metric.cognitive,
5117 @r###"
5118 {
5119 "sum": 7.0,
5120 "average": 7.0,
5121 "min": 0.0,
5122 "max": 7.0
5123 }
5124 "###
5125 );
5126 },
5127 );
5128 }
5129
5130 #[test]
5131 fn go_method_declaration() {
5132 check_metrics::<GoParser>(
5138 "package main
5139 type T struct{ val int }
5140 func (t T) positive() bool {
5141 if t.val > 0 { // +1
5142 return true
5143 }
5144 return false
5145 }",
5146 "foo.go",
5147 |metric| {
5148 insta::assert_json_snapshot!(metric.cognitive, @r#"
5149 {
5150 "sum": 1.0,
5151 "average": 1.0,
5152 "min": 0.0,
5153 "max": 1.0
5154 }
5155 "#);
5156 },
5157 );
5158 }
5159
5160 #[test]
5161 fn bash_no_cognitive() {
5162 check_metrics::<BashParser>("a=42", "foo.sh", |metric| {
5163 insta::assert_json_snapshot!(
5164 metric.cognitive,
5165 @r###"
5166 {
5167 "sum": 0.0,
5168 "average": null,
5169 "min": 0.0,
5170 "max": 0.0
5171 }"###
5172 );
5173 });
5174 }
5175
5176 #[test]
5177 fn bash_simple_if() {
5178 check_metrics::<BashParser>(
5179 "f() {
5180 if [ -z \"$1\" ]; then # +1
5181 echo empty
5182 fi
5183 }",
5184 "foo.sh",
5185 |metric| {
5186 insta::assert_json_snapshot!(
5187 metric.cognitive,
5188 @r###"
5189 {
5190 "sum": 1.0,
5191 "average": 1.0,
5192 "min": 0.0,
5193 "max": 1.0
5194 }"###
5195 );
5196 },
5197 );
5198 }
5199
5200 #[test]
5201 fn bash_if_elif_else() {
5202 check_metrics::<BashParser>(
5203 "f() {
5204 if [ \"$1\" = a ]; then # +1
5205 echo a
5206 elif [ \"$1\" = b ]; then # +1
5207 echo b
5208 else # +1
5209 echo other
5210 fi
5211 }",
5212 "foo.sh",
5213 |metric| {
5214 insta::assert_json_snapshot!(
5215 metric.cognitive,
5216 @r###"
5217 {
5218 "sum": 3.0,
5219 "average": 3.0,
5220 "min": 0.0,
5221 "max": 3.0
5222 }"###
5223 );
5224 },
5225 );
5226 }
5227
5228 #[test]
5229 fn bash_nested_loops() {
5230 check_metrics::<BashParser>(
5231 "f() {
5232 for i in 1 2 3; do # +1
5233 while [ \"$x\" -lt 10 ]; do # +2 (nested)
5234 x=$((x+1))
5235 done
5236 done
5237 }",
5238 "foo.sh",
5239 |metric| {
5240 insta::assert_json_snapshot!(
5241 metric.cognitive,
5242 @r###"
5243 {
5244 "sum": 3.0,
5245 "average": 3.0,
5246 "min": 0.0,
5247 "max": 3.0
5248 }"###
5249 );
5250 },
5251 );
5252 }
5253
5254 #[test]
5255 fn bash_until_loop() {
5256 check_metrics::<BashParser>(
5260 "f() {
5261 until [ -z \"$x\" ]; do # +1
5262 x=$(pop)
5263 done
5264 }",
5265 "foo.sh",
5266 |metric| {
5267 insta::assert_json_snapshot!(
5268 metric.cognitive,
5269 @r###"
5270 {
5271 "sum": 1.0,
5272 "average": 1.0,
5273 "min": 0.0,
5274 "max": 1.0
5275 }"###
5276 );
5277 },
5278 );
5279 }
5280
5281 #[test]
5282 fn bash_case() {
5283 check_metrics::<BashParser>(
5286 "f() {
5287 case \"$1\" in # +1
5288 a) echo a ;;
5289 b) echo b ;;
5290 *) echo other ;;
5291 esac
5292 }",
5293 "foo.sh",
5294 |metric| {
5295 insta::assert_json_snapshot!(
5296 metric.cognitive,
5297 @r###"
5298 {
5299 "sum": 1.0,
5300 "average": 1.0,
5301 "min": 0.0,
5302 "max": 1.0
5303 }"###
5304 );
5305 },
5306 );
5307 }
5308
5309 #[test]
5310 fn bash_boolean_sequence() {
5311 check_metrics::<BashParser>(
5315 "f() {
5316 if [[ -n \"$x\" ]] && [[ -n \"$y\" ]] && [[ -n \"$z\" ]]; then
5317 # +1 if, +1 boolean (one && chain)
5318 echo all
5319 fi
5320 if [[ -n \"$x\" ]] && [[ -n \"$y\" ]] || [[ -n \"$z\" ]]; then
5321 # +1 if, +2 boolean (&& then ||)
5322 echo mixed
5323 fi
5324 }",
5325 "foo.sh",
5326 |metric| {
5327 insta::assert_json_snapshot!(
5328 metric.cognitive,
5329 @r###"
5330 {
5331 "sum": 5.0,
5332 "average": 5.0,
5333 "min": 0.0,
5334 "max": 5.0
5335 }"###
5336 );
5337 },
5338 );
5339 }
5340
5341 #[test]
5342 fn tcl_no_cognitive() {
5343 check_metrics::<TclParser>("set x 1", "foo.tcl", |metric| {
5345 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
5346 assert_eq!(metric.cognitive.cognitive_max(), 0.0);
5347 insta::assert_json_snapshot!(metric.cognitive);
5348 });
5349 }
5350
5351 #[test]
5352 fn tcl_simple_function() {
5353 check_metrics::<TclParser>(
5355 "proc f {a} {
5356 if {$a > 0 && $a < 10} {
5357 puts yes
5358 }
5359}",
5360 "foo.tcl",
5361 |metric| {
5362 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
5363 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
5364 insta::assert_json_snapshot!(metric.cognitive);
5365 },
5366 );
5367 }
5368
5369 #[test]
5370 fn tcl_sequence_same_booleans() {
5371 check_metrics::<TclParser>(
5374 "proc f {a b c d} {
5375 if {$a && $b && $c} {
5376 puts yes
5377 }
5378 if {$a || $b || $c || $d} {
5379 puts no
5380 }
5381}",
5382 "foo.tcl",
5383 |metric| {
5384 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
5386 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
5387 insta::assert_json_snapshot!(metric.cognitive);
5388 },
5389 );
5390 }
5391
5392 #[test]
5393 fn tcl_sequence_different_booleans() {
5394 check_metrics::<TclParser>(
5396 "proc f {a b c} {
5397 if {$a && $b || $c} {
5398 puts yes
5399 }
5400}",
5401 "foo.tcl",
5402 |metric| {
5403 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5405 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5406 insta::assert_json_snapshot!(metric.cognitive);
5407 },
5408 );
5409 }
5410
5411 #[test]
5412 fn tcl_not_booleans() {
5413 check_metrics::<TclParser>(
5416 "proc f {a b} {
5417 if {!$a && !$b} {
5418 puts yes
5419 }
5420}",
5421 "foo.tcl",
5422 |metric| {
5423 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
5425 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
5426 insta::assert_json_snapshot!(metric.cognitive);
5427 },
5428 );
5429 }
5430
5431 #[test]
5432 fn tcl_1_level_nesting() {
5433 check_metrics::<TclParser>(
5435 "proc f {x} {
5436 while {$x > 0} {
5437 if {$x > 10} {
5438 set x [expr {$x - 1}]
5439 }
5440 }
5441}",
5442 "foo.tcl",
5443 |metric| {
5444 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5446 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5447 insta::assert_json_snapshot!(metric.cognitive);
5448 },
5449 );
5450 }
5451
5452 #[test]
5453 fn tcl_2_level_nesting() {
5454 check_metrics::<TclParser>(
5456 "proc f {x} {
5457 while {$x > 0} {
5458 foreach y {1 2 3} {
5459 if {$y > $x} {
5460 puts found
5461 }
5462 }
5463 }
5464}",
5465 "foo.tcl",
5466 |metric| {
5467 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
5469 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
5470 insta::assert_json_snapshot!(metric.cognitive);
5471 },
5472 );
5473 }
5474
5475 #[test]
5476 fn tcl_catch_cognitive() {
5477 check_metrics::<TclParser>(
5480 "proc f {x} {
5481 catch {
5482 if {$x < 0} {
5483 error negative
5484 }
5485 } msg
5486}",
5487 "foo.tcl",
5488 |metric| {
5489 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5491 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5492 insta::assert_json_snapshot!(metric.cognitive);
5493 },
5494 );
5495 }
5496
5497 #[test]
5498 fn tcl_if_elseif_else() {
5499 check_metrics::<TclParser>(
5501 "proc f {x} {
5502 if {$x > 10} {
5503 puts big
5504 } elseif {$x > 5} {
5505 puts medium
5506 } else {
5507 puts small
5508 }
5509}",
5510 "foo.tcl",
5511 |metric| {
5512 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5514 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5515 insta::assert_json_snapshot!(metric.cognitive);
5516 },
5517 );
5518 }
5519
5520 #[test]
5521 fn tcl_not_booleans_nested() {
5522 check_metrics::<TclParser>(
5524 "proc f {a b c} {
5525 if {$a && !($b && $c)} {
5526 puts yes
5527 }
5528}",
5529 "foo.tcl",
5530 |metric| {
5531 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5533 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5534 insta::assert_json_snapshot!(metric.cognitive);
5535 },
5536 );
5537 }
5538
5539 #[test]
5540 fn tcl_not_booleans_double_nested() {
5541 check_metrics::<TclParser>(
5543 "proc f {a b c d} {
5544 if {!($a || $b) && !($c || $d)} {
5545 puts yes
5546 }
5547}",
5548 "foo.tcl",
5549 |metric| {
5550 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
5552 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
5553 insta::assert_json_snapshot!(metric.cognitive);
5554 },
5555 );
5556 }
5557
5558 #[test]
5559 fn tcl_nested_procedure_cognitive() {
5560 check_metrics::<TclParser>(
5562 "proc outer {x} {
5563 proc inner {y} {
5564 if {$y > 0} {
5565 puts positive
5566 }
5567 }
5568 inner $x
5569}",
5570 "foo.tcl",
5571 |metric| {
5572 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
5574 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
5575 insta::assert_json_snapshot!(metric.cognitive);
5576 },
5577 );
5578 }
5579
5580 #[test]
5581 fn tcl_ternary_cognitive() {
5582 check_metrics::<TclParser>(
5585 "proc f {x} {
5586 set y [expr {$x > 0 ? $x : -$x}]
5587 while {$y > 10} {
5588 set y [expr {$y > 5 ? $y - 1 : 0}]
5589 }
5590}",
5591 "foo.tcl",
5592 |metric| {
5593 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
5595 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
5596 insta::assert_json_snapshot!(metric.cognitive);
5597 },
5598 );
5599 }
5600
5601 #[test]
5602 fn lua_cognitive_no_cognitive() {
5603 check_metrics::<LuaParser>("local x = 42", "foo.lua", |metric| {
5605 insta::assert_json_snapshot!(
5606 metric.cognitive,
5607 @r###"
5608 {
5609 "sum": 0.0,
5610 "average": null,
5611 "min": 0.0,
5612 "max": 0.0
5613 }"###
5614 );
5615 });
5616 }
5617
5618 #[test]
5619 fn lua_cognitive_simple_function() {
5620 check_metrics::<LuaParser>(
5623 "local function f(a, b, c, d)
5624 if a and b then
5625 return 1
5626 end
5627 if c and d then
5628 return 1
5629 end
5630end",
5631 "foo.lua",
5632 |metric| {
5633 insta::assert_json_snapshot!(
5634 metric.cognitive,
5635 @r###"
5636 {
5637 "sum": 4.0,
5638 "average": 4.0,
5639 "min": 0.0,
5640 "max": 4.0
5641 }"###
5642 );
5643 },
5644 );
5645 }
5646
5647 #[test]
5648 fn lua_cognitive_sequence_same_booleans() {
5649 check_metrics::<LuaParser>(
5653 "local function f(a, b, c, d)
5654 if a and b and c then
5655 return 1
5656 end
5657 if a or b or c or d then
5658 return 1
5659 end
5660end",
5661 "foo.lua",
5662 |metric| {
5663 insta::assert_json_snapshot!(
5664 metric.cognitive,
5665 @r###"
5666 {
5667 "sum": 4.0,
5668 "average": 4.0,
5669 "min": 0.0,
5670 "max": 4.0
5671 }"###
5672 );
5673 },
5674 );
5675 }
5676
5677 #[test]
5678 fn lua_cognitive_not_booleans() {
5679 check_metrics::<LuaParser>(
5682 "local function f(a, b)
5683 if not a and not b then
5684 return 1
5685 end
5686end",
5687 "foo.lua",
5688 |metric| {
5689 insta::assert_json_snapshot!(
5690 metric.cognitive,
5691 @r###"
5692 {
5693 "sum": 2.0,
5694 "average": 2.0,
5695 "min": 0.0,
5696 "max": 2.0
5697 }"###
5698 );
5699 },
5700 );
5701 }
5702
5703 #[test]
5704 fn lua_cognitive_sequence_different_booleans() {
5705 check_metrics::<LuaParser>(
5708 "local function f(a, b, c)
5709 if a and b or c then
5710 return 1
5711 end
5712end",
5713 "foo.lua",
5714 |metric| {
5715 insta::assert_json_snapshot!(
5716 metric.cognitive,
5717 @r###"
5718 {
5719 "sum": 3.0,
5720 "average": 3.0,
5721 "min": 0.0,
5722 "max": 3.0
5723 }"###
5724 );
5725 },
5726 );
5727 }
5728
5729 #[test]
5730 fn lua_cognitive_1_level_nesting() {
5731 check_metrics::<LuaParser>(
5733 "local function f(t)
5734 for i = 1, #t do
5735 if t[i] > 0 then
5736 return t[i]
5737 end
5738 end
5739end",
5740 "foo.lua",
5741 |metric| {
5742 insta::assert_json_snapshot!(
5743 metric.cognitive,
5744 @r###"
5745 {
5746 "sum": 3.0,
5747 "average": 3.0,
5748 "min": 0.0,
5749 "max": 3.0
5750 }"###
5751 );
5752 },
5753 );
5754 }
5755
5756 #[test]
5757 fn lua_cognitive_2_level_nesting() {
5758 check_metrics::<LuaParser>(
5760 "local function f(t)
5761 for i = 1, #t do
5762 for j = 1, #t do
5763 if t[i] > t[j] then
5764 return t[i]
5765 end
5766 end
5767 end
5768end",
5769 "foo.lua",
5770 |metric| {
5771 insta::assert_json_snapshot!(
5772 metric.cognitive,
5773 @r###"
5774 {
5775 "sum": 6.0,
5776 "average": 6.0,
5777 "min": 0.0,
5778 "max": 6.0
5779 }"###
5780 );
5781 },
5782 );
5783 }
5784
5785 #[test]
5786 fn lua_cognitive_break_continue() {
5787 check_metrics::<LuaParser>(
5790 "local function f(t)
5791 for i = 1, #t do
5792 if t[i] < 0 then
5793 break
5794 end
5795 end
5796end",
5797 "foo.lua",
5798 |metric| {
5799 insta::assert_json_snapshot!(
5800 metric.cognitive,
5801 @r###"
5802 {
5803 "sum": 4.0,
5804 "average": 4.0,
5805 "min": 0.0,
5806 "max": 4.0
5807 }"###
5808 );
5809 },
5810 );
5811 }
5812
5813 #[test]
5814 fn lua_cognitive_elseif_nesting() {
5815 check_metrics::<LuaParser>(
5819 "local function classify(x)
5820 if x > 0 then
5821 return 1
5822 elseif x < 0 then
5823 return -1
5824 elseif x == 0 then
5825 return 0
5826 else
5827 return 0
5828 end
5829end",
5830 "foo.lua",
5831 |metric| {
5832 insta::assert_json_snapshot!(
5833 metric.cognitive,
5834 @r###"
5835 {
5836 "sum": 4.0,
5837 "average": 4.0,
5838 "min": 0.0,
5839 "max": 4.0
5840 }"###
5841 );
5842 },
5843 );
5844 }
5845
5846 #[test]
5847 fn typescript_switch_statement() {
5848 check_metrics::<TypescriptParser>(
5849 "function describe(x: number): string {
5850 switch (x) { // +1
5851 case 1:
5852 return 'one';
5853 case 2:
5854 return 'two';
5855 default:
5856 return 'other';
5857 }
5858 }",
5859 "foo.ts",
5860 |metric| {
5861 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
5862 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
5863 insta::assert_json_snapshot!(metric.cognitive);
5864 },
5865 );
5866 }
5867
5868 #[test]
5869 fn typescript_no_cognitive() {
5870 check_metrics::<TypescriptParser>(
5871 "function f(a: number, b: number): number {
5872 return a + b;
5873 }",
5874 "foo.ts",
5875 |metric| {
5876 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
5877 assert_eq!(metric.cognitive.cognitive_max(), 0.0);
5878 insta::assert_json_snapshot!(metric.cognitive);
5879 },
5880 );
5881 }
5882
5883 #[test]
5884 fn tsx_no_cognitive() {
5885 check_metrics::<TsxParser>(
5886 "function f(a: number, b: number): number {
5887 return a + b;
5888 }",
5889 "foo.tsx",
5890 |metric| {
5891 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
5892 assert_eq!(metric.cognitive.cognitive_max(), 0.0);
5893 insta::assert_json_snapshot!(metric.cognitive);
5894 },
5895 );
5896 }
5897
5898 #[test]
5899 fn tsx_simple_if() {
5900 check_metrics::<TsxParser>(
5901 "function f(x: number): number {
5902 if (x > 0) { // +1
5903 return x;
5904 }
5905 return 0;
5906 }",
5907 "foo.tsx",
5908 |metric| {
5909 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
5910 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
5911 insta::assert_json_snapshot!(metric.cognitive);
5912 },
5913 );
5914 }
5915
5916 #[test]
5917 fn tsx_boolean_sequence() {
5918 check_metrics::<TsxParser>(
5919 "function f(a: boolean, b: boolean, c: boolean): boolean {
5920 return a && b && c; // +1 (&&, sequence)
5921 }",
5922 "foo.tsx",
5923 |metric| {
5924 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
5925 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
5926 insta::assert_json_snapshot!(metric.cognitive);
5927 },
5928 );
5929 }
5930
5931 #[test]
5932 fn tsx_2_level_nesting() {
5933 check_metrics::<TsxParser>(
5934 "function f(a: number[], n: number): number {
5935 for (let i = 0; i < a.length; i++) { // +1
5936 if (a[i] > n) { // +2 (nesting=1)
5937 return a[i];
5938 }
5939 }
5940 return -1;
5941 }",
5942 "foo.tsx",
5943 |metric| {
5944 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5946 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5947 insta::assert_json_snapshot!(metric.cognitive);
5948 },
5949 );
5950 }
5951
5952 #[test]
5953 fn tsx_else_if_chain() {
5954 check_metrics::<TsxParser>(
5955 "function classify(x: number): string {
5956 if (x < 0) { // +1
5957 return 'neg';
5958 } else if (x === 0) { // +1 (else if = structural, not nesting)
5959 return 'zero';
5960 } else { // +1
5961 return 'pos';
5962 }
5963 }",
5964 "foo.tsx",
5965 |metric| {
5966 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5968 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5969 insta::assert_json_snapshot!(metric.cognitive);
5970 },
5971 );
5972 }
5973
5974 #[test]
5975 fn js_sibling_bool_sequences() {
5976 check_metrics::<JavascriptParser>(
5981 "function f(a, b, c, d) {
5982 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
5983 }",
5984 "foo.js",
5985 |metric| {
5986 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
5987 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
5988 insta::assert_json_snapshot!(metric.cognitive);
5989 },
5990 );
5991 }
5992
5993 #[test]
5994 fn js_nested_bool_same_op() {
5995 check_metrics::<JavascriptParser>(
5998 "function f(a, b, c, d) {
5999 return a || (b && c && d); // +1(||) +1(&&) = 2
6000 }",
6001 "foo.js",
6002 |metric| {
6003 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6004 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6005 insta::assert_json_snapshot!(metric.cognitive);
6006 },
6007 );
6008 }
6009
6010 #[test]
6011 fn python_sibling_bool_sequences() {
6012 check_metrics::<PythonParser>(
6018 "def f(a, b, c, d):
6019 return (a and b) or (c and d) # +1(and) +1(or) +1(and) = 3
6020 ",
6021 "foo.py",
6022 |metric| {
6023 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6024 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6025 insta::assert_json_snapshot!(metric.cognitive);
6026 },
6027 );
6028 }
6029
6030 #[test]
6031 fn python_nested_bool_same_op() {
6032 check_metrics::<PythonParser>(
6035 "def f(a, b, c, d):
6036 return a or (b and c and d) # +1(or) +1(and) = 2
6037 ",
6038 "foo.py",
6039 |metric| {
6040 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6041 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6042 insta::assert_json_snapshot!(metric.cognitive);
6043 },
6044 );
6045 }
6046
6047 #[test]
6048 fn perl_sibling_bool_sequences() {
6049 check_metrics::<PerlParser>(
6055 "sub f {
6056 my ($a, $b, $c, $d) = @_;
6057 return ($a && $b) || ($c && $d); # +1(&&) +1(||) +1(&&) = 3
6058 }",
6059 "foo.pl",
6060 |metric| {
6061 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6062 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6063 insta::assert_json_snapshot!(metric.cognitive);
6064 },
6065 );
6066 }
6067
6068 #[test]
6069 fn perl_nested_bool_same_op() {
6070 check_metrics::<PerlParser>(
6075 "sub f {
6076 my ($a, $b, $c, $d) = @_;
6077 return $a || ($b && $c && $d); # +1(||) +1(&&) = 2
6078 }",
6079 "foo.pl",
6080 |metric| {
6081 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6082 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6083 insta::assert_json_snapshot!(metric.cognitive);
6084 },
6085 );
6086 }
6087
6088 #[test]
6089 fn rust_sibling_bool_sequences() {
6090 check_metrics::<RustParser>(
6093 "fn f(a: bool, b: bool, c: bool, d: bool) -> bool {
6094 (a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3
6095 }",
6096 "foo.rs",
6097 |metric| {
6098 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6099 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6100 insta::assert_json_snapshot!(metric.cognitive);
6101 },
6102 );
6103 }
6104
6105 #[test]
6106 fn rust_nested_bool_same_op() {
6107 check_metrics::<RustParser>(
6110 "fn f(a: bool, b: bool, c: bool, d: bool) -> bool {
6111 a || (b && c && d) // +1(||) +1(&&) = 2
6112 }",
6113 "foo.rs",
6114 |metric| {
6115 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6116 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6117 insta::assert_json_snapshot!(metric.cognitive);
6118 },
6119 );
6120 }
6121
6122 #[test]
6123 fn c_sibling_bool_sequences() {
6124 check_metrics::<CppParser>(
6127 "int f(int a, int b, int c, int d) {
6128 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
6129 }",
6130 "foo.c",
6131 |metric| {
6132 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6133 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6134 insta::assert_json_snapshot!(metric.cognitive);
6135 },
6136 );
6137 }
6138
6139 #[test]
6140 fn c_nested_bool_same_op() {
6141 check_metrics::<CppParser>(
6144 "int f(int a, int b, int c, int d) {
6145 return a || (b && c && d); // +1(||) +1(&&) = 2
6146 }",
6147 "foo.c",
6148 |metric| {
6149 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6150 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6151 insta::assert_json_snapshot!(metric.cognitive);
6152 },
6153 );
6154 }
6155
6156 #[test]
6157 fn mozjs_sibling_bool_sequences() {
6158 check_metrics::<MozjsParser>(
6161 "function f(a, b, c, d) {
6162 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
6163 }",
6164 "foo.js",
6165 |metric| {
6166 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6167 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6168 insta::assert_json_snapshot!(metric.cognitive);
6169 },
6170 );
6171 }
6172
6173 #[test]
6174 fn mozjs_nested_bool_same_op() {
6175 check_metrics::<MozjsParser>(
6178 "function f(a, b, c, d) {
6179 return a || (b && c && d); // +1(||) +1(&&) = 2
6180 }",
6181 "foo.js",
6182 |metric| {
6183 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6184 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6185 insta::assert_json_snapshot!(metric.cognitive);
6186 },
6187 );
6188 }
6189
6190 #[test]
6191 fn typescript_sibling_bool_sequences() {
6192 check_metrics::<TypescriptParser>(
6195 "function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
6196 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
6197 }",
6198 "foo.ts",
6199 |metric| {
6200 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6201 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6202 insta::assert_json_snapshot!(metric.cognitive);
6203 },
6204 );
6205 }
6206
6207 #[test]
6208 fn typescript_nested_bool_same_op() {
6209 check_metrics::<TypescriptParser>(
6212 "function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
6213 return a || (b && c && d); // +1(||) +1(&&) = 2
6214 }",
6215 "foo.ts",
6216 |metric| {
6217 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6218 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6219 insta::assert_json_snapshot!(metric.cognitive);
6220 },
6221 );
6222 }
6223
6224 #[test]
6225 fn tsx_sibling_bool_sequences() {
6226 check_metrics::<TsxParser>(
6229 "function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
6230 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
6231 }",
6232 "foo.tsx",
6233 |metric| {
6234 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6235 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6236 insta::assert_json_snapshot!(metric.cognitive);
6237 },
6238 );
6239 }
6240
6241 #[test]
6242 fn tsx_nested_bool_same_op() {
6243 check_metrics::<TsxParser>(
6246 "function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
6247 return a || (b && c && d); // +1(||) +1(&&) = 2
6248 }",
6249 "foo.tsx",
6250 |metric| {
6251 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6252 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6253 insta::assert_json_snapshot!(metric.cognitive);
6254 },
6255 );
6256 }
6257
6258 #[test]
6259 fn javascript_nullish_coalescing_chain_230() {
6260 check_metrics::<JavascriptParser>(
6265 "function pick(a, b, c) {
6266 return a ?? b ?? c; // +1 (chain of ??)
6267 }",
6268 "foo.js",
6269 |metric| {
6270 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6271 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
6272 insta::assert_json_snapshot!(
6273 metric.cognitive,
6274 @r###"
6275 {
6276 "sum": 1.0,
6277 "average": 1.0,
6278 "min": 0.0,
6279 "max": 1.0
6280 }"###
6281 );
6282 },
6283 );
6284 }
6285
6286 #[test]
6287 fn typescript_nullish_coalescing_with_if_230() {
6288 check_metrics::<TypescriptParser>(
6294 "function risky(x: string | null, fallback: string | null): string {
6295 if (x === \"y\") { // +1
6296 return x ?? fallback ?? \"unknown\"; // +1 (chain of ??)
6297 }
6298 return \"no\";
6299 }",
6300 "foo.ts",
6301 |metric| {
6302 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6303 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6304 insta::assert_json_snapshot!(
6305 metric.cognitive,
6306 @r###"
6307 {
6308 "sum": 2.0,
6309 "average": 2.0,
6310 "min": 0.0,
6311 "max": 2.0
6312 }"###
6313 );
6314 },
6315 );
6316 }
6317
6318 #[test]
6319 fn tsx_nullish_coalescing_chain_230() {
6320 check_metrics::<TsxParser>(
6322 "function pick(a: number | null, b: number | null, c: number): number {
6323 return a ?? b ?? c; // +1 (chain of ??)
6324 }",
6325 "foo.tsx",
6326 |metric| {
6327 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6328 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
6329 insta::assert_json_snapshot!(
6330 metric.cognitive,
6331 @r###"
6332 {
6333 "sum": 1.0,
6334 "average": 1.0,
6335 "min": 0.0,
6336 "max": 1.0
6337 }"###
6338 );
6339 },
6340 );
6341 }
6342
6343 #[test]
6344 fn mozjs_nullish_coalescing_chain_230() {
6345 check_metrics::<MozjsParser>(
6347 "function pick(a, b, c) {
6348 return a ?? b ?? c; // +1 (chain of ??)
6349 }",
6350 "foo.js",
6351 |metric| {
6352 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6353 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
6354 insta::assert_json_snapshot!(
6355 metric.cognitive,
6356 @r###"
6357 {
6358 "sum": 1.0,
6359 "average": 1.0,
6360 "min": 0.0,
6361 "max": 1.0
6362 }"###
6363 );
6364 },
6365 );
6366 }
6367
6368 #[test]
6369 fn csharp_null_coalescing_cognitive_230() {
6370 check_metrics::<CsharpParser>(
6376 "class C {
6377 string Risky(string x, string fallback) {
6378 if (x == \"y\") { // +1
6379 return x ?? fallback ?? \"unknown\"; // +1 (chain of ??)
6380 }
6381 return \"no\";
6382 }
6383 }",
6384 "foo.cs",
6385 |metric| {
6386 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6387 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6388 insta::assert_json_snapshot!(
6389 metric.cognitive,
6390 @r###"
6391 {
6392 "sum": 2.0,
6393 "average": 2.0,
6394 "min": 0.0,
6395 "max": 2.0
6396 }"###
6397 );
6398 },
6399 );
6400 }
6401
6402 #[test]
6403 fn php_null_coalescing_cognitive_230() {
6404 check_metrics::<PhpParser>(
6410 "<?php
6411 function risky($x, $fallback) {
6412 if ($x === \"y\") { // +1
6413 return $x ?? $fallback ?? \"unknown\"; // +1 (chain of ??)
6414 }
6415 return \"no\";
6416 }",
6417 "foo.php",
6418 |metric| {
6419 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6420 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6421 insta::assert_json_snapshot!(
6422 metric.cognitive,
6423 @r###"
6424 {
6425 "sum": 2.0,
6426 "average": 2.0,
6427 "min": 0.0,
6428 "max": 2.0
6429 }"###
6430 );
6431 },
6432 );
6433 }
6434
6435 #[test]
6444 fn php_word_form_and_forms_boolean_sequence_230() {
6445 check_metrics::<PhpParser>(
6446 "<?php
6447 function check_and($a, $b, $c, $d) {
6448 if ($a and $b and $c and $d) { // +1 (if) + 1 (and chain)
6449 return true;
6450 }
6451 return false;
6452 }",
6453 "foo.php",
6454 |metric| {
6455 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6456 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6457 },
6458 );
6459 }
6460
6461 #[test]
6462 fn php_word_form_or_forms_boolean_sequence_230() {
6463 check_metrics::<PhpParser>(
6464 "<?php
6465 function check_or($a, $b, $c, $d) {
6466 if ($a or $b or $c or $d) { // +1 (if) + 1 (or chain)
6467 return true;
6468 }
6469 return false;
6470 }",
6471 "foo.php",
6472 |metric| {
6473 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6474 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6475 },
6476 );
6477 }
6478
6479 #[test]
6480 fn php_word_form_xor_forms_boolean_sequence_230() {
6481 check_metrics::<PhpParser>(
6482 "<?php
6483 function check_xor($a, $b, $c, $d) {
6484 if ($a xor $b xor $c xor $d) { // +1 (if) + 1 (xor chain)
6485 return true;
6486 }
6487 return false;
6488 }",
6489 "foo.php",
6490 |metric| {
6491 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6492 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6493 },
6494 );
6495 }
6496
6497 #[test]
6498 fn java_cognitive_else_if_chain() {
6499 check_metrics::<JavaParser>(
6503 "class X {
6504 public static void f(int x) {
6505 if (x > 10) {
6506 } else if (x > 5) {
6507 } else if (x > 0) {
6508 } else {
6509 }
6510 }
6511 }",
6512 "foo.java",
6513 |metric| {
6514 insta::assert_json_snapshot!(
6515 metric.cognitive,
6516 @r###"
6517 {
6518 "sum": 4.0,
6519 "average": 4.0,
6520 "min": 0.0,
6521 "max": 4.0
6522 }"###
6523 );
6524 },
6525 );
6526 }
6527
6528 #[test]
6529 fn java_cognitive_nested_else_if() {
6530 check_metrics::<JavaParser>(
6535 "class X {
6536 public static void f(int x) {
6537 for (int i = 0; i < x; i++) {
6538 if (i > 10) {
6539 } else if (i > 5) {
6540 } else {
6541 }
6542 }
6543 }
6544 }",
6545 "foo.java",
6546 |metric| {
6547 insta::assert_json_snapshot!(
6548 metric.cognitive,
6549 @r###"
6550 {
6551 "sum": 5.0,
6552 "average": 5.0,
6553 "min": 0.0,
6554 "max": 5.0
6555 }"###
6556 );
6557 },
6558 );
6559 }
6560
6561 #[test]
6562 fn java_cognitive_if_inside_else_block_is_not_else_if() {
6563 check_metrics::<JavaParser>(
6569 "class X {
6570 public static void f(int a, int c) {
6571 if (a > 0) {
6572 } else {
6573 if (c > 0) {
6574 }
6575 }
6576 }
6577 }",
6578 "foo.java",
6579 |metric| {
6580 insta::assert_json_snapshot!(
6581 metric.cognitive,
6582 @r###"
6583 {
6584 "sum": 4.0,
6585 "average": 4.0,
6586 "min": 0.0,
6587 "max": 4.0
6588 }"###
6589 );
6590 },
6591 );
6592 }
6593
6594 #[test]
6595 fn java_sibling_bool_sequences() {
6596 check_metrics::<JavaParser>(
6599 "class X {
6600 boolean f(boolean a, boolean b, boolean c, boolean d) {
6601 return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
6602 }
6603 }",
6604 "foo.java",
6605 |metric| {
6606 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6607 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
6608 insta::assert_json_snapshot!(metric.cognitive);
6609 },
6610 );
6611 }
6612
6613 #[test]
6614 fn java_nested_bool_same_op() {
6615 check_metrics::<JavaParser>(
6618 "class X {
6619 boolean f(boolean a, boolean b, boolean c, boolean d) {
6620 return a || (b && c && d); // +1(||) +1(&&) = 2
6621 }
6622 }",
6623 "foo.java",
6624 |metric| {
6625 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6626 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6627 insta::assert_json_snapshot!(metric.cognitive);
6628 },
6629 );
6630 }
6631
6632 #[test]
6633 fn groovy_no_cognitive() {
6634 check_metrics::<GroovyParser>("class A { int x = 42 }", "foo.groovy", |metric| {
6635 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
6636 });
6637 }
6638
6639 #[test]
6640 fn groovy_single_branch_function() {
6641 check_metrics::<GroovyParser>(
6642 "void f(int x) {
6643 if (x > 0) {
6644 println(x)
6645 }
6646 }",
6647 "foo.groovy",
6648 |metric| {
6649 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6651 },
6652 );
6653 }
6654
6655 #[test]
6656 fn groovy_nested_if() {
6657 check_metrics::<GroovyParser>(
6658 "void f(int x, int y) {
6659 if (x > 0) {
6660 if (y > 0) {
6661 println(x)
6662 }
6663 }
6664 }",
6665 "foo.groovy",
6666 |metric| {
6667 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6669 },
6670 );
6671 }
6672
6673 #[test]
6674 fn groovy_else_if_chain() {
6675 check_metrics::<GroovyParser>(
6680 "class X {
6681 static void f(int x) {
6682 if (x > 10) {
6683 } else if (x > 5) {
6684 } else if (x > 0) {
6685 } else {
6686 }
6687 }
6688 }",
6689 "foo.groovy",
6690 |metric| {
6691 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
6693 },
6694 );
6695 }
6696
6697 #[test]
6698 fn groovy_else_if_chain_lower_than_nested_ifs() {
6699 check_metrics::<GroovyParser>(
6703 "class X {
6704 static void f(int x) {
6705 if (x > 10) {
6706 if (x > 5) {
6707 if (x > 0) {
6708 }
6709 }
6710 }
6711 }
6712 }",
6713 "foo.groovy",
6714 |metric| {
6715 assert!(metric.cognitive.cognitive_sum() > 4.0);
6720 },
6721 );
6722 }
6723
6724 #[test]
6725 fn groovy_sequence_booleans_same_op() {
6726 check_metrics::<GroovyParser>(
6728 "void f(boolean a, boolean b, boolean c) {
6729 if (a && b && c) { println(a) }
6730 }",
6731 "foo.groovy",
6732 |metric| {
6733 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6735 },
6736 );
6737 }
6738
6739 #[test]
6740 fn groovy_sequence_booleans_mixed_ops() {
6741 check_metrics::<GroovyParser>(
6743 "void f(boolean a, boolean b, boolean c) {
6744 if (a && b || c) { println(a) }
6745 }",
6746 "foo.groovy",
6747 |metric| {
6748 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6750 },
6751 );
6752 }
6753
6754 #[test]
6755 fn groovy_not_operator_negation() {
6756 check_metrics::<GroovyParser>(
6759 "void f(boolean a, boolean b) {
6760 if (a && !b) { println(a) }
6761 }",
6762 "foo.groovy",
6763 |metric| {
6764 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6766 },
6767 );
6768 }
6769
6770 #[test]
6771 fn groovy_for_while_do_loops() {
6772 check_metrics::<GroovyParser>(
6773 "void f(int n) {
6774 for (int i = 0; i < n; i++) {
6775 while (i > 0) {
6776 i--
6777 }
6778 }
6779 }",
6780 "foo.groovy",
6781 |metric| {
6782 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6784 },
6785 );
6786 }
6787
6788 #[test]
6789 fn groovy_enhanced_for() {
6790 check_metrics::<GroovyParser>(
6791 "void f(List items) {
6792 for (item in items) {
6793 println(item)
6794 }
6795 }",
6796 "foo.groovy",
6797 |metric| {
6798 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6799 },
6800 );
6801 }
6802
6803 #[test]
6804 fn groovy_try_catch_nesting() {
6805 check_metrics::<GroovyParser>(
6806 "void f() {
6807 try {
6808 risky()
6809 } catch (Exception e) {
6810 handle(e)
6811 }
6812 }",
6813 "foo.groovy",
6814 |metric| {
6815 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6817 },
6818 );
6819 }
6820
6821 #[test]
6822 fn groovy_ternary_expression() {
6823 check_metrics::<GroovyParser>(
6824 "void f(int x) {
6825 def y = (x > 0) ? 1 : 2
6826 }",
6827 "foo.groovy",
6828 |metric| {
6829 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6831 },
6832 );
6833 }
6834
6835 #[test]
6836 fn groovy_elvis_chain_246() {
6837 check_metrics::<GroovyParser>(
6847 "def pick(a, b, c) {
6848 return a ?: b ?: c // +1 (Elvis chain)
6849 }",
6850 "foo.groovy",
6851 |metric| {
6852 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
6853 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
6854 },
6855 );
6856 }
6857
6858 #[test]
6859 fn groovy_elvis_inside_if_246() {
6860 check_metrics::<GroovyParser>(
6864 "def f(a, b) {
6865 if (a != null) { // +1
6866 return a ?: b ?: 'x' // +1 (Elvis chain)
6867 }
6868 return 'no'
6869 }",
6870 "foo.groovy",
6871 |metric| {
6872 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
6873 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
6874 },
6875 );
6876 }
6877
6878 #[test]
6879 fn groovy_labeled_break_continue() {
6880 check_metrics::<GroovyParser>(
6882 "void f() {
6883 outer:
6884 for (int i = 0; i < 10; i++) {
6885 inner:
6886 for (int j = 0; j < 10; j++) {
6887 if (i == j) break outer
6888 if (i < j) continue inner
6889 }
6890 }
6891 }",
6892 "foo.groovy",
6893 |metric| {
6894 assert_eq!(metric.cognitive.cognitive_sum(), 11.0);
6897 },
6898 );
6899 }
6900
6901 #[test]
6902 fn groovy_multiple_branch_function() {
6903 check_metrics::<GroovyParser>(
6907 "class X {
6908 static void print(boolean a, boolean b) {
6909 if (a) {
6910 println 'test1'
6911 }
6912 if (b) {
6913 println 'test2'
6914 } else {
6915 println 'test3'
6916 }
6917 }
6918 }",
6919 "foo.groovy",
6920 |metric| {
6921 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
6923 },
6924 );
6925 }
6926
6927 #[test]
6928 fn groovy_unlabeled_break_continue_not_counted() {
6929 check_metrics::<GroovyParser>(
6933 "class X {
6934 void scan(int[] m) {
6935 for (int i = 0; i < m.length; i++) {
6936 if (m[i] < 0) continue
6937 if (m[i] > 100) break
6938 }
6939 }
6940 }",
6941 "foo.groovy",
6942 |metric| {
6943 assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
6945 },
6946 );
6947 }
6948
6949 #[test]
6950 fn groovy_cognitive_nested_else_if() {
6951 check_metrics::<GroovyParser>(
6957 "class X {
6958 static void f(int x) {
6959 for (int i = 0; i < x; i++) {
6960 if (i > 10) {
6961 } else if (i > 5) {
6962 } else {
6963 }
6964 }
6965 }
6966 }",
6967 "foo.groovy",
6968 |metric| {
6969 assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
6971 },
6972 );
6973 }
6974
6975 #[test]
6976 fn groovy_cognitive_if_inside_else_block_is_not_else_if() {
6977 check_metrics::<GroovyParser>(
6983 "class X {
6984 static void f(int a, int c) {
6985 if (a > 0) {
6986 } else {
6987 if (c > 0) {
6988 }
6989 }
6990 }
6991 }",
6992 "foo.groovy",
6993 |metric| {
6994 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
6996 },
6997 );
6998 }
6999
7000 #[test]
7001 fn groovy_nested_ternary() {
7002 check_metrics::<GroovyParser>(
7006 "class X {
7007 static String classify(int a, int b) {
7008 if (a > 0) {
7009 return b > 0 ? (b > 10 ? 'big' : 'small') : 'neg'
7010 }
7011 return 'zero'
7012 }
7013 }",
7014 "foo.groovy",
7015 |metric| {
7016 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
7019 },
7020 );
7021 }
7022
7023 #[test]
7024 fn csharp_cognitive_else_if_chain() {
7025 check_metrics::<CsharpParser>(
7029 "class X {
7030 public static void F(int x) {
7031 if (x > 10) {
7032 } else if (x > 5) {
7033 } else if (x > 0) {
7034 } else {
7035 }
7036 }
7037 }",
7038 "foo.cs",
7039 |metric| {
7040 insta::assert_json_snapshot!(
7041 metric.cognitive,
7042 @r###"
7043 {
7044 "sum": 4.0,
7045 "average": 4.0,
7046 "min": 0.0,
7047 "max": 4.0
7048 }"###
7049 );
7050 },
7051 );
7052 }
7053
7054 #[test]
7055 fn csharp_cognitive_nested_else_if() {
7056 check_metrics::<CsharpParser>(
7061 "class X {
7062 public static void F(int x) {
7063 for (int i = 0; i < x; i++) {
7064 if (i > 10) {
7065 } else if (i > 5) {
7066 } else {
7067 }
7068 }
7069 }
7070 }",
7071 "foo.cs",
7072 |metric| {
7073 insta::assert_json_snapshot!(
7074 metric.cognitive,
7075 @r###"
7076 {
7077 "sum": 5.0,
7078 "average": 5.0,
7079 "min": 0.0,
7080 "max": 5.0
7081 }"###
7082 );
7083 },
7084 );
7085 }
7086
7087 #[test]
7088 fn csharp_cognitive_if_inside_else_block_is_not_else_if() {
7089 check_metrics::<CsharpParser>(
7095 "class X {
7096 public static void F(int a, int c) {
7097 if (a > 0) {
7098 } else {
7099 if (c > 0) {
7100 }
7101 }
7102 }
7103 }",
7104 "foo.cs",
7105 |metric| {
7106 insta::assert_json_snapshot!(
7107 metric.cognitive,
7108 @r###"
7109 {
7110 "sum": 4.0,
7111 "average": 4.0,
7112 "min": 0.0,
7113 "max": 4.0
7114 }"###
7115 );
7116 },
7117 );
7118 }
7119
7120 #[test]
7121 fn csharp_sibling_bool_sequences() {
7122 check_metrics::<CsharpParser>(
7125 "class X {
7126 bool F(bool a, bool b, bool c, bool d) {
7127 return (a && b) || (c && d);
7128 }
7129 }",
7130 "foo.cs",
7131 |metric| {
7132 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7133 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7134 insta::assert_json_snapshot!(metric.cognitive);
7135 },
7136 );
7137 }
7138
7139 #[test]
7140 fn csharp_nested_bool_same_op() {
7141 check_metrics::<CsharpParser>(
7144 "class X {
7145 bool F(bool a, bool b, bool c, bool d) {
7146 return a || (b && c && d);
7147 }
7148 }",
7149 "foo.cs",
7150 |metric| {
7151 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7152 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7153 insta::assert_json_snapshot!(metric.cognitive);
7154 },
7155 );
7156 }
7157
7158 #[test]
7159 fn kotlin_sibling_bool_sequences() {
7160 check_metrics::<KotlinParser>(
7163 "fun f(a: Boolean, b: Boolean, c: Boolean, d: Boolean) =
7164 (a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3",
7165 "foo.kt",
7166 |metric| {
7167 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7168 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7169 insta::assert_json_snapshot!(metric.cognitive);
7170 },
7171 );
7172 }
7173
7174 #[test]
7175 fn kotlin_nested_bool_same_op() {
7176 check_metrics::<KotlinParser>(
7179 "fun f(a: Boolean, b: Boolean, c: Boolean, d: Boolean) =
7180 a || (b && c && d) // +1(||) +1(&&) = 2",
7181 "foo.kt",
7182 |metric| {
7183 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7184 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7185 insta::assert_json_snapshot!(metric.cognitive);
7186 },
7187 );
7188 }
7189
7190 #[test]
7191 fn kotlin_elvis_chain_239() {
7192 check_metrics::<KotlinParser>(
7199 "fun pick(a: String?, b: String?, c: String): String = a ?: b ?: c // +1 (Elvis chain)",
7200 "foo.kt",
7201 |metric| {
7202 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7203 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
7204 insta::assert_json_snapshot!(
7205 metric.cognitive,
7206 @r###"
7207 {
7208 "sum": 1.0,
7209 "average": 1.0,
7210 "min": 0.0,
7211 "max": 1.0
7212 }"###
7213 );
7214 },
7215 );
7216 }
7217
7218 #[test]
7219 fn kotlin_elvis_inside_if_239() {
7220 check_metrics::<KotlinParser>(
7225 "fun f(a: String?, b: String?): String {
7226 if (a != null) { // +1
7227 return a ?: b ?: \"x\" // +1 (Elvis chain)
7228 }
7229 return \"no\"
7230 }",
7231 "foo.kt",
7232 |metric| {
7233 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7234 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7235 insta::assert_json_snapshot!(
7236 metric.cognitive,
7237 @r###"
7238 {
7239 "sum": 2.0,
7240 "average": 2.0,
7241 "min": 0.0,
7242 "max": 2.0
7243 }"###
7244 );
7245 },
7246 );
7247 }
7248
7249 #[test]
7250 fn go_sibling_bool_sequences() {
7251 check_metrics::<GoParser>(
7254 "package main
7255 func f(a, b, c, d bool) bool {
7256 return (a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3
7257 }",
7258 "foo.go",
7259 |metric| {
7260 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7261 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7262 insta::assert_json_snapshot!(metric.cognitive);
7263 },
7264 );
7265 }
7266
7267 #[test]
7268 fn go_nested_bool_same_op() {
7269 check_metrics::<GoParser>(
7272 "package main
7273 func f(a, b, c, d bool) bool {
7274 return a || (b && c && d) // +1(||) +1(&&) = 2
7275 }",
7276 "foo.go",
7277 |metric| {
7278 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7279 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7280 insta::assert_json_snapshot!(metric.cognitive);
7281 },
7282 );
7283 }
7284
7285 #[test]
7286 fn tcl_sibling_bool_sequences() {
7287 check_metrics::<TclParser>(
7290 "proc f {a b c d} {
7291 if {($a && $b) || ($c && $d)} {
7292 puts yes
7293 }
7294}",
7295 "foo.tcl",
7296 |metric| {
7297 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
7298 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
7299 insta::assert_json_snapshot!(metric.cognitive);
7300 },
7301 );
7302 }
7303
7304 #[test]
7305 fn tcl_nested_bool_same_op() {
7306 check_metrics::<TclParser>(
7309 "proc f {a b c d} {
7310 if {$a || ($b && $c && $d)} {
7311 puts yes
7312 }
7313}",
7314 "foo.tcl",
7315 |metric| {
7316 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7317 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7318 insta::assert_json_snapshot!(metric.cognitive);
7319 },
7320 );
7321 }
7322
7323 #[test]
7324 fn lua_sibling_bool_sequences() {
7325 check_metrics::<LuaParser>(
7328 "local function f(a, b, c, d)
7329 if (a and b) or (c and d) then
7330 return 1
7331 end
7332end",
7333 "foo.lua",
7334 |metric| {
7335 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
7336 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
7337 insta::assert_json_snapshot!(metric.cognitive);
7338 },
7339 );
7340 }
7341
7342 #[test]
7343 fn lua_nested_bool_same_op() {
7344 check_metrics::<LuaParser>(
7347 "local function f(a, b, c, d)
7348 if a or (b and c and d) then
7349 return 1
7350 end
7351end",
7352 "foo.lua",
7353 |metric| {
7354 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7355 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7356 insta::assert_json_snapshot!(metric.cognitive);
7357 },
7358 );
7359 }
7360
7361 #[test]
7362 fn bash_sibling_bool_sequences() {
7363 check_metrics::<BashParser>(
7367 "f() {
7368 if [[ -n \"$a\" ]] && [[ -n \"$b\" ]] || [[ -n \"$c\" ]] && [[ -n \"$d\" ]]; then
7369 echo test
7370 fi
7371 }",
7372 "foo.sh",
7373 |metric| {
7374 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
7375 assert_eq!(metric.cognitive.cognitive_max(), 4.0);
7376 insta::assert_json_snapshot!(metric.cognitive);
7377 },
7378 );
7379 }
7380
7381 #[test]
7382 fn bash_nested_bool_same_op() {
7383 check_metrics::<BashParser>(
7388 "f() {
7389 if [[ -n \"$a\" ]] || [[ -n \"$b\" ]] && [[ -n \"$c\" ]] && [[ -n \"$d\" ]]; then
7390 echo test
7391 fi
7392 }",
7393 "foo.sh",
7394 |metric| {
7395 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7396 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7397 insta::assert_json_snapshot!(metric.cognitive);
7398 },
7399 );
7400 }
7401
7402 #[test]
7403 fn php_no_cognitive() {
7404 check_metrics::<PhpParser>("<?php $a = 42;", "foo.php", |metric| {
7405 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7406 assert_eq!(metric.cognitive.cognitive_max(), 0.0);
7407 insta::assert_json_snapshot!(metric.cognitive);
7408 });
7409 }
7410
7411 #[test]
7412 fn php_simple_function() {
7413 check_metrics::<PhpParser>(
7415 "<?php
7416 function f(bool $a): void {
7417 if ($a) {
7418 echo 'hi';
7419 }
7420 }",
7421 "foo.php",
7422 |metric| {
7423 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7424 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
7425 insta::assert_json_snapshot!(metric.cognitive);
7426 },
7427 );
7428 }
7429
7430 #[test]
7431 fn php_ternary() {
7432 check_metrics::<PhpParser>(
7438 "<?php
7439 function check(int $a): bool {
7440 return $a > 0 ? true : false; // +1
7441 }",
7442 "foo.php",
7443 |metric| {
7444 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7445 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
7446 insta::assert_json_snapshot!(
7447 metric.cognitive,
7448 @r###"
7449 {
7450 "sum": 1.0,
7451 "average": 1.0,
7452 "min": 0.0,
7453 "max": 1.0
7454 }"###
7455 );
7456 },
7457 );
7458 }
7459
7460 #[test]
7461 fn php_nested_ternary() {
7462 check_metrics::<PhpParser>(
7467 "<?php
7468 function classify(int $a, int $b): string {
7469 if ($a > 0) { // +1
7470 return $b > 0 ? ($b > 10 ? 'big' : 'small') : 'neg'; // +2, +3
7471 }
7472 return 'zero';
7473 }",
7474 "foo.php",
7475 |metric| {
7476 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
7477 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
7478 insta::assert_json_snapshot!(
7479 metric.cognitive,
7480 @r###"
7481 {
7482 "sum": 6.0,
7483 "average": 6.0,
7484 "min": 0.0,
7485 "max": 6.0
7486 }"###
7487 );
7488 },
7489 );
7490 }
7491
7492 #[test]
7493 fn php_sequence_same_booleans() {
7494 check_metrics::<PhpParser>(
7497 "<?php
7498 function f(bool $a, bool $b, bool $c): bool {
7499 return $a && $b && $c;
7500 }",
7501 "foo.php",
7502 |metric| {
7503 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7505 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
7506 insta::assert_json_snapshot!(metric.cognitive);
7507 },
7508 );
7509 }
7510
7511 #[test]
7512 fn php_sequence_different_booleans() {
7513 check_metrics::<PhpParser>(
7515 "<?php
7516 function f(bool $a, bool $b, bool $c): bool {
7517 return $a && $b || $c;
7518 }",
7519 "foo.php",
7520 |metric| {
7521 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7523 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7524 insta::assert_json_snapshot!(metric.cognitive);
7525 },
7526 );
7527 }
7528
7529 #[test]
7530 fn php_not_booleans() {
7531 check_metrics::<PhpParser>(
7533 "<?php
7534 function f(bool $a, bool $b, bool $c): bool {
7535 return $a && !($b && $c);
7536 }",
7537 "foo.php",
7538 |metric| {
7539 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7541 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
7542 insta::assert_json_snapshot!(metric.cognitive);
7543 },
7544 );
7545 }
7546
7547 #[test]
7548 fn php_1_level_nesting() {
7549 check_metrics::<PhpParser>(
7551 "<?php
7552 function f(int $n): int {
7553 for ($i = 0; $i < $n; $i++) {
7554 if ($i % 2 === 0) {
7555 return $i;
7556 }
7557 }
7558 return -1;
7559 }",
7560 "foo.php",
7561 |metric| {
7562 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7564 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7565 insta::assert_json_snapshot!(metric.cognitive);
7566 },
7567 );
7568 }
7569
7570 #[test]
7571 fn php_2_level_nesting() {
7572 check_metrics::<PhpParser>(
7574 "<?php
7575 function f(int $n): int {
7576 for ($i = 0; $i < $n; $i++) {
7577 while ($i > 0) {
7578 if ($i % 2 === 0) {
7579 return $i;
7580 }
7581 }
7582 }
7583 return -1;
7584 }",
7585 "foo.php",
7586 |metric| {
7587 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
7589 assert_eq!(metric.cognitive.cognitive_max(), 6.0);
7590 insta::assert_json_snapshot!(metric.cognitive);
7591 },
7592 );
7593 }
7594
7595 #[test]
7596 fn php_break_continue() {
7597 check_metrics::<PhpParser>(
7600 "<?php
7601 function f(int $n): int {
7602 for ($i = 0; $i < $n; $i++) {
7603 if ($i % 2 === 0) {
7604 continue;
7605 }
7606 if ($i > 100) {
7607 break;
7608 }
7609 }
7610 return 0;
7611 }",
7612 "foo.php",
7613 |metric| {
7614 assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
7616 assert_eq!(metric.cognitive.cognitive_max(), 5.0);
7617 insta::assert_json_snapshot!(metric.cognitive);
7618 },
7619 );
7620 }
7621
7622 #[test]
7626 fn elixir_empty_function() {
7627 check_metrics::<ElixirParser>(
7628 "defmodule Foo do\n def f(x) do\n x\n end\nend\n",
7629 "foo.ex",
7630 |metric| {
7631 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7632 insta::assert_json_snapshot!(
7633 metric.cognitive,
7634 @r###"
7635 {
7636 "sum": 0.0,
7637 "average": null,
7638 "min": 0.0,
7639 "max": 0.0
7640 }"###
7641 );
7642 },
7643 );
7644 }
7645
7646 #[test]
7649 fn elixir_simple_if() {
7650 check_metrics::<ElixirParser>(
7651 "defmodule Foo do\n def f(x) do\n if x > 0 do\n :pos\n end\n end\nend\n",
7652 "foo.ex",
7653 |metric| {
7654 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7655 insta::assert_json_snapshot!(metric.cognitive);
7656 },
7657 );
7658 }
7659
7660 #[test]
7663 fn elixir_if_else() {
7664 check_metrics::<ElixirParser>(
7665 "defmodule Foo do\n def f(x) do\n if x > 0 do\n :pos\n else\n :neg\n end\n end\nend\n",
7666 "foo.ex",
7667 |metric| {
7668 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7670 insta::assert_json_snapshot!(metric.cognitive);
7671 },
7672 );
7673 }
7674
7675 #[test]
7679 fn elixir_case_arms_count_once() {
7680 check_metrics::<ElixirParser>(
7681 "defmodule Foo do\n def f(x) do\n case x do\n 1 -> :one\n 2 -> :two\n _ -> :other\n end\n end\nend\n",
7682 "foo.ex",
7683 |metric| {
7684 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7686 insta::assert_json_snapshot!(metric.cognitive);
7687 },
7688 );
7689 }
7690
7691 #[test]
7694 fn elixir_cond_counts_once() {
7695 check_metrics::<ElixirParser>(
7696 "defmodule Foo do\n def f(x) do\n cond do\n x > 0 -> :pos\n x < 0 -> :neg\n true -> :zero\n end\n end\nend\n",
7697 "foo.ex",
7698 |metric| {
7699 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7701 insta::assert_json_snapshot!(metric.cognitive);
7702 },
7703 );
7704 }
7705
7706 #[test]
7709 fn elixir_nested_if_amplifies() {
7710 check_metrics::<ElixirParser>(
7711 "defmodule Foo do\n def f(x, y) do\n if x > 0 do\n if y > 0 do\n :both\n end\n end\n end\nend\n",
7712 "foo.ex",
7713 |metric| {
7714 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7716 insta::assert_json_snapshot!(metric.cognitive);
7717 },
7718 );
7719 }
7720
7721 #[test]
7726 fn elixir_try_rescue_catch() {
7727 check_metrics::<ElixirParser>(
7728 "defmodule Foo do\n def f do\n try do\n :ok\n rescue\n _ -> :err\n catch\n _ -> :thrown\n end\n end\nend\n",
7729 "foo.ex",
7730 |metric| {
7731 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7733 insta::assert_json_snapshot!(metric.cognitive);
7734 },
7735 );
7736 }
7737
7738 #[test]
7742 fn elixir_boolean_sequence() {
7743 check_metrics::<ElixirParser>(
7744 "defmodule Foo do\n def f(x, y, z) do\n if x && y || z do\n :hit\n end\n end\nend\n",
7745 "foo.ex",
7746 |metric| {
7747 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7749 insta::assert_json_snapshot!(metric.cognitive);
7750 },
7751 );
7752 }
7753
7754 #[test]
7760 fn elixir_enum_reduce_is_zero() {
7761 check_metrics::<ElixirParser>(
7762 "defmodule Foo do\n def sum(xs) do\n Enum.reduce(xs, 0, fn x, acc -> acc + x end)\n end\nend\n",
7763 "foo.ex",
7764 |metric| {
7765 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7768 insta::assert_json_snapshot!(metric.cognitive);
7769 },
7770 );
7771 }
7772
7773 #[test]
7779 fn elixir_recursion_is_zero_documented_limitation() {
7780 check_metrics::<ElixirParser>(
7781 "defmodule Foo do\n def fact(0), do: 1\n def fact(n), do: n * fact(n - 1)\nend\n",
7782 "foo.ex",
7783 |metric| {
7784 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7785 insta::assert_json_snapshot!(metric.cognitive);
7786 },
7787 );
7788 }
7789
7790 #[test]
7791 fn php_match_cognitive() {
7792 check_metrics::<PhpParser>(
7795 "<?php
7796 function color(string $c): int {
7797 return match ($c) {
7798 'red' => 1,
7799 'green' => 2,
7800 default => 0,
7801 };
7802 }",
7803 "foo.php",
7804 |metric| {
7805 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7807 assert_eq!(metric.cognitive.cognitive_max(), 1.0);
7808 insta::assert_json_snapshot!(metric.cognitive);
7809 },
7810 );
7811 }
7812
7813 #[test]
7814 fn ruby_no_cognitive() {
7815 check_metrics::<RubyParser>("a = 42\n", "foo.rb", |metric| {
7816 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7817 insta::assert_json_snapshot!(metric.cognitive);
7818 });
7819 }
7820
7821 #[test]
7822 fn ruby_simple_function() {
7823 check_metrics::<RubyParser>("def foo\n a = 1\nend\n", "foo.rb", |metric| {
7825 assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
7826 insta::assert_json_snapshot!(metric.cognitive);
7827 });
7828 }
7829
7830 #[test]
7831 fn ruby_1_level_nesting() {
7832 check_metrics::<RubyParser>("def foo\n if a\n b\n end\nend\n", "foo.rb", |metric| {
7834 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7835 insta::assert_json_snapshot!(metric.cognitive);
7836 });
7837 }
7838
7839 #[test]
7840 fn ruby_2_level_nesting() {
7841 check_metrics::<RubyParser>(
7843 "def foo\n if a\n if b\n c\n end\n end\nend\n",
7844 "foo.rb",
7845 |metric| {
7846 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7847 insta::assert_json_snapshot!(metric.cognitive);
7848 },
7849 );
7850 }
7851
7852 #[test]
7853 fn ruby_sequence_same_booleans() {
7854 check_metrics::<RubyParser>(
7857 "def foo\n if a && b && c\n d\n end\nend\n",
7858 "foo.rb",
7859 |metric| {
7860 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
7861 insta::assert_json_snapshot!(metric.cognitive);
7862 },
7863 );
7864 }
7865
7866 #[test]
7867 fn ruby_sequence_different_booleans() {
7868 check_metrics::<RubyParser>(
7870 "def foo\n if a && b || c\n d\n end\nend\n",
7871 "foo.rb",
7872 |metric| {
7873 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7874 insta::assert_json_snapshot!(metric.cognitive);
7875 },
7876 );
7877 }
7878
7879 #[test]
7880 fn ruby_not_booleans() {
7881 check_metrics::<RubyParser>(
7884 "def foo\n if !a\n b\n end\nend\n",
7885 "foo.rb",
7886 |metric| {
7887 assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
7888 insta::assert_json_snapshot!(metric.cognitive);
7889 },
7890 );
7891 }
7892
7893 #[test]
7894 fn ruby_break_next() {
7895 check_metrics::<RubyParser>(
7898 "def foo\n while a\n break\n next\n end\nend\n",
7899 "foo.rb",
7900 |metric| {
7901 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7902 insta::assert_json_snapshot!(metric.cognitive);
7903 },
7904 );
7905 }
7906
7907 #[test]
7908 fn ruby_else_if_chain() {
7909 check_metrics::<RubyParser>(
7921 "def foo\n if a\n 1\n elsif b\n 2\n elsif c\n 3\n else\n 4\n end\nend\n",
7922 "foo.rb",
7923 |metric| {
7924 assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
7925 insta::assert_json_snapshot!(metric.cognitive);
7926 },
7927 );
7928 check_metrics::<RubyParser>(
7929 "def foo\n if a\n if b\n if c\n 1\n end\n end\n end\nend\n",
7930 "foo.rb",
7931 |metric| {
7932 assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
7933 },
7934 );
7935 }
7936
7937 #[test]
7938 fn javascript_compound_short_circuit_assignment_236() {
7939 check_metrics::<JavascriptParser>(
7945 "function f(x) {
7946 x ??= 1; // +1 (??=)
7947 x &&= 2; // +1 (&&=)
7948 x ||= 3; // +1 (||=)
7949 }",
7950 "foo.js",
7951 |metric| {
7952 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7953 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7954 insta::assert_json_snapshot!(
7955 metric.cognitive,
7956 @r###"
7957 {
7958 "sum": 3.0,
7959 "average": 3.0,
7960 "min": 0.0,
7961 "max": 3.0
7962 }"###
7963 );
7964 },
7965 );
7966 }
7967
7968 #[test]
7969 fn typescript_compound_short_circuit_assignment_236() {
7970 check_metrics::<TypescriptParser>(
7973 "function f(x: number | null) {
7974 x ??= 1; // +1 (??=)
7975 x &&= 2; // +1 (&&=)
7976 x ||= 3; // +1 (||=)
7977 }",
7978 "foo.ts",
7979 |metric| {
7980 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
7981 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
7982 insta::assert_json_snapshot!(
7983 metric.cognitive,
7984 @r###"
7985 {
7986 "sum": 3.0,
7987 "average": 3.0,
7988 "min": 0.0,
7989 "max": 3.0
7990 }"###
7991 );
7992 },
7993 );
7994 }
7995
7996 #[test]
7997 fn tsx_compound_short_circuit_assignment_236() {
7998 check_metrics::<TsxParser>(
8001 "function f(x: number | null) {
8002 x ??= 1; // +1 (??=)
8003 x &&= 2; // +1 (&&=)
8004 x ||= 3; // +1 (||=)
8005 }",
8006 "foo.tsx",
8007 |metric| {
8008 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
8009 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
8010 insta::assert_json_snapshot!(
8011 metric.cognitive,
8012 @r###"
8013 {
8014 "sum": 3.0,
8015 "average": 3.0,
8016 "min": 0.0,
8017 "max": 3.0
8018 }"###
8019 );
8020 },
8021 );
8022 }
8023
8024 #[test]
8025 fn mozjs_compound_short_circuit_assignment_236() {
8026 check_metrics::<MozjsParser>(
8030 "function f(x) {
8031 x ??= 1; // +1 (??=)
8032 x &&= 2; // +1 (&&=)
8033 x ||= 3; // +1 (||=)
8034 }",
8035 "foo.js",
8036 |metric| {
8037 assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
8038 assert_eq!(metric.cognitive.cognitive_max(), 3.0);
8039 insta::assert_json_snapshot!(
8040 metric.cognitive,
8041 @r###"
8042 {
8043 "sum": 3.0,
8044 "average": 3.0,
8045 "min": 0.0,
8046 "max": 3.0
8047 }"###
8048 );
8049 },
8050 );
8051 }
8052
8053 #[test]
8054 fn csharp_compound_short_circuit_assignment_236() {
8055 check_metrics::<CsharpParser>(
8061 "class C {
8062 int? F(int? x) {
8063 x ??= 1; // +1 (??=)
8064 return x ?? 0;
8065 }
8066 }",
8067 "foo.cs",
8068 |metric| {
8069 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
8071 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
8072 insta::assert_json_snapshot!(
8073 metric.cognitive,
8074 @r###"
8075 {
8076 "sum": 2.0,
8077 "average": 2.0,
8078 "min": 0.0,
8079 "max": 2.0
8080 }"###
8081 );
8082 },
8083 );
8084 }
8085
8086 #[test]
8087 fn php_compound_short_circuit_assignment_236() {
8088 check_metrics::<PhpParser>(
8094 "<?php
8095 function f($x) {
8096 $x ??= 1; // +1 (??=)
8097 return $x ?? 0; // +1 (??)
8098 }",
8099 "foo.php",
8100 |metric| {
8101 assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
8102 assert_eq!(metric.cognitive.cognitive_max(), 2.0);
8103 insta::assert_json_snapshot!(
8104 metric.cognitive,
8105 @r###"
8106 {
8107 "sum": 2.0,
8108 "average": 2.0,
8109 "min": 0.0,
8110 "max": 2.0
8111 }"###
8112 );
8113 },
8114 );
8115 }
8116}