#![allow(
clippy::enum_glob_use,
clippy::match_same_arms,
clippy::needless_pass_by_value,
clippy::wildcard_imports
)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use std::collections::HashMap;
use serde::Serialize;
use serde::ser::{SerializeStruct, Serializer};
use std::fmt;
use crate::checker::Checker;
use crate::macros::{csharp_prefix_unary_expr_kinds, implement_metric_trait};
use crate::*;
#[derive(Debug, Clone)]
pub struct Stats {
structural: usize,
structural_sum: usize,
structural_min: usize,
structural_max: usize,
nesting: usize,
total_space_functions: usize,
boolean_seq: BoolSequence,
}
impl Default for Stats {
fn default() -> Self {
Self {
structural: 0,
structural_sum: 0,
structural_min: usize::MAX,
structural_max: 0,
nesting: 0,
total_space_functions: 1,
boolean_seq: BoolSequence::default(),
}
}
}
impl Serialize for Stats {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut st = serializer.serialize_struct("cognitive", 4)?;
st.serialize_field("sum", &self.cognitive_sum())?;
st.serialize_field("average", &self.cognitive_average())?;
st.serialize_field("min", &self.cognitive_min())?;
st.serialize_field("max", &self.cognitive_max())?;
st.end()
}
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"sum: {}, average: {}, min:{}, max: {}",
self.cognitive(),
self.cognitive_average(),
self.cognitive_min(),
self.cognitive_max()
)
}
}
impl Stats {
pub fn merge(&mut self, other: &Stats) {
self.structural_min = self.structural_min.min(other.structural_min);
self.structural_max = self.structural_max.max(other.structural_max);
self.structural_sum += other.structural_sum;
}
#[must_use]
pub fn cognitive(&self) -> f64 {
self.structural as f64
}
#[must_use]
pub fn cognitive_sum(&self) -> f64 {
self.structural_sum as f64
}
#[must_use]
pub fn cognitive_min(&self) -> f64 {
if self.structural_min == usize::MAX {
0.0
} else {
self.structural_min as f64
}
}
#[must_use]
pub fn cognitive_max(&self) -> f64 {
self.structural_max as f64
}
#[must_use]
pub fn cognitive_average(&self) -> f64 {
self.cognitive_sum() / self.total_space_functions as f64
}
#[inline]
pub(crate) fn compute_sum(&mut self) {
self.structural_sum += self.structural;
}
#[inline]
pub(crate) fn compute_minmax(&mut self) {
self.structural_min = self.structural_min.min(self.structural);
self.structural_max = self.structural_max.max(self.structural);
self.compute_sum();
}
pub(crate) fn finalize(&mut self, total_space_functions: usize) {
self.total_space_functions = total_space_functions;
}
}
#[doc(hidden)]
pub trait Cognitive
where
Self: Checker,
{
fn compute<'a>(
node: &Node<'a>,
code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
);
}
fn compute_booleans_with<F: Fn(u16) -> bool>(node: &Node, stats: &mut Stats, is_op: F) {
let enclosing_end = node.end_byte();
for child in node.children() {
let id = child.kind_id();
if is_op(id) {
stats.structural =
stats
.boolean_seq
.eval_based_on_prev(id, enclosing_end, stats.structural);
}
}
}
fn compute_booleans<T: PartialEq + From<u16>>(node: &Node, stats: &mut Stats, typs1: T, typs2: T) {
compute_booleans_with(node, stats, |id| {
let converted: T = id.into();
typs1 == converted || typs2 == converted
});
}
fn compute_ruby_booleans(node: &Node, stats: &mut Stats) {
compute_booleans_with(node, stats, |id| {
matches!(
id.into(),
Ruby::AMPAMP | Ruby::PIPEPIPE | Ruby::And | Ruby::Or
)
});
}
fn compute_perl_booleans(node: &Node, stats: &mut Stats) {
compute_booleans_with(node, stats, |id| {
matches!(
id.into(),
Perl::AMPAMP
| Perl::PIPEPIPE
| Perl::SLASHSLASH
| Perl::And
| Perl::Or
| Perl::AMPAMPEQ
| Perl::PIPEPIPEEQ
| Perl::SLASHSLASHEQ
)
});
}
fn compute_elixir_booleans(node: &Node, stats: &mut Stats) {
compute_booleans_with(node, stats, |id| {
matches!(
id.into(),
Elixir::AMPAMP | Elixir::PIPEPIPE | Elixir::And | Elixir::Or
)
});
}
#[derive(Debug, Default, Clone)]
struct BoolSequence {
boolean_op: Option<(u16, usize)>,
}
impl BoolSequence {
fn reset(&mut self) {
self.boolean_op = None;
}
fn not_operator(&mut self) {
self.reset();
}
fn eval_based_on_prev(
&mut self,
bool_id: u16,
enclosing_end: usize,
structural: usize,
) -> usize {
match self.boolean_op {
Some((prev_id, prev_end)) if prev_id == bool_id && enclosing_end <= prev_end => {
structural
}
_ => {
self.boolean_op = Some((bool_id, enclosing_end));
structural + 1
}
}
}
}
#[inline]
fn increment(stats: &mut Stats) {
stats.structural += stats.nesting + 1;
}
#[inline]
fn increment_by_one(stats: &mut Stats) {
stats.structural += 1;
}
#[inline]
fn increment_branch_extension(stats: &mut Stats) {
stats.structural += 1;
stats.boolean_seq.reset();
}
fn get_nesting_from_map(
node: &Node,
nesting_map: &HashMap<usize, (usize, usize, usize)>,
) -> (usize, usize, usize) {
node.parent()
.and_then(|parent| nesting_map.get(&parent.id()))
.copied()
.unwrap_or((0, 0, 0))
}
fn increment_function_depth<T: PartialEq + From<u16>>(depth: &mut usize, node: &Node, stops: &[T]) {
let mut child = *node;
while let Some(parent) = child.parent() {
if stops.contains(&T::from(parent.kind_id())) {
*depth += 1;
break;
}
child = parent;
}
}
#[inline]
fn increase_nesting(stats: &mut Stats, nesting: &mut usize, depth: usize, lambda: usize) {
stats.nesting = *nesting + depth + lambda;
increment(stats);
*nesting += 1;
stats.boolean_seq.reset();
}
impl Cognitive for PythonCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Python::*;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement | WhileStatement | ConditionalExpression | MatchStatement => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ElifClause => {
increment_branch_extension(stats);
}
ElseClause | FinallyClause => {
increment_by_one(stats);
}
ExceptClause => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ExpressionList | ExpressionStatement | Tuple => {
stats.boolean_seq.reset();
}
NotOperator => {
stats.boolean_seq.not_operator();
}
BooleanOperator => {
if node.count_specific_ancestors::<PythonParser>(
|node| node.kind_id() == BooleanOperator,
|node| node.kind_id() == Lambda,
) == 0
{
stats.structural += node.count_specific_ancestors::<PythonParser>(
|node| node.kind_id() == Lambda,
|node| {
matches!(
node.kind_id().into(),
ExpressionList | IfStatement | ForStatement | WhileStatement
)
},
);
}
compute_booleans(node, stats, And, Or);
}
Lambda => {
lambda += 1;
}
FunctionDefinition => {
increment_function_depth(&mut depth, node, &[FunctionDefinition]);
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for RustCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Rust::*;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfExpression if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForExpression | WhileExpression | MatchExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
increment_by_one(stats);
}
BreakExpression | ContinueExpression => {
if let Some(label_child) = node.child(1)
&& let Label = label_child.kind_id().into()
{
increment_by_one(stats);
}
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
FunctionItem => {
nesting = 0;
increment_function_depth(&mut depth, node, &[FunctionItem]);
}
ClosureExpression => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for CppCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Cpp::*;
let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement
| ForRangeLoop
| WhileStatement
| DoStatement
| SwitchStatement
| CatchClause
| ConditionalExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
GotoStatement | Else => {
increment_by_one(stats);
}
UnaryExpression2 => {
stats.boolean_seq.not_operator();
}
BinaryExpression2 => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
LambdaExpression => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
macro_rules! js_cognitive {
($lang:ident) => {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use $lang::*;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement | ForInStatement | WhileStatement | DoStatement | SwitchStatement | CatchClause | TernaryExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
increment_by_one(stats);
}
ExpressionStatement => {
stats.boolean_seq.reset();
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans_with(node, stats, |id| {
matches!(id.into(), AMPAMP | PIPEPIPE | QMARKQMARK)
});
}
AugmentedAssignmentExpression => {
compute_booleans_with(node, stats, |id| {
matches!(id.into(), AMPAMPEQ | PIPEPIPEEQ | QMARKQMARKEQ)
});
}
FunctionDeclaration => {
nesting = 0;
lambda = 0;
increment_function_depth(&mut depth, node, &[FunctionDeclaration]);
}
ArrowFunction => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
};
}
impl Cognitive for MozjsCode {
js_cognitive!(Mozjs);
}
impl Cognitive for JavascriptCode {
js_cognitive!(Javascript);
}
impl Cognitive for TypescriptCode {
js_cognitive!(Typescript);
}
impl Cognitive for TsxCode {
js_cognitive!(Tsx);
}
impl Cognitive for JavaCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Java::*;
let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement
| EnhancedForStatement
| WhileStatement
| DoStatement
| SwitchBlock
| CatchClause
| TernaryExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
increment_by_one(stats);
}
BreakStatement | ContinueStatement
if node.is_child(Identifier as u16) =>
{
increment_by_one(stats);
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
LambdaExpression => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for GroovyCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Groovy::*;
let (mut nesting, depth, lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement | ForInStatement | WhileStatement | DoWhileStatement | SwitchBlock
| CatchClause | TernaryExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
increment_by_one(stats);
}
BreakStatement | ContinueStatement if node.is_child(Identifier as u16) => {
increment_by_one(stats);
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
ElvisExpression => {
compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKCOLON));
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for CsharpCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Csharp::*;
let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement
| ForeachStatement
| WhileStatement
| DoStatement
| SwitchStatement
| SwitchExpression
| CatchClause
| ConditionalExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
increment_by_one(stats);
}
GotoStatement => {
increment_by_one(stats);
}
csharp_prefix_unary_expr_kinds!() => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans_with(node, stats, |id| {
matches!(id.into(), AMPAMP | PIPEPIPE | QMARKQMARK)
});
}
AssignmentExpression => {
compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKQMARKEQ));
}
LambdaExpression | AnonymousMethodExpression => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for PerlCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Perl as P;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
P::IfStatement
| P::UnlessStatement
| P::WhileStatement
| P::UntilStatement
| P::ForStatement1
| P::ForStatement2
| P::TernaryExpression
| P::IfSimpleStatement
| P::UnlessSimpleStatement
| P::WhileSimpleStatement
| P::UntilSimpleStatement
| P::ForSimpleStatement => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
P::Else | P::ElsifClause => {
increment_by_one(stats);
}
P::Goto | P::GotoExpression => {
increment_by_one(stats);
}
P::LoopControlStatement if node.is_child(P::Label as u16) => {
increment_by_one(stats);
}
P::UnaryExpression => {
stats.boolean_seq.not_operator();
}
P::BinaryExpression => {
compute_perl_booleans(node, stats);
}
P::FunctionDefinition | P::FunctionDefinitionWithoutSub => {
nesting = 0;
increment_function_depth(
&mut depth,
node,
&[P::FunctionDefinition, P::FunctionDefinitionWithoutSub],
);
}
P::AnonymousFunction => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for KotlinCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Kotlin::*;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfExpression if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ForStatement | WhileStatement | DoWhileStatement | WhenExpression | CatchBlock => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Else => {
let in_when = node.parent().is_some_and(|p| p.kind_id() == WhenEntry);
if !in_when {
increment_by_one(stats);
}
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans_with(node, stats, |id| {
matches!(id.into(), AMPAMP | PIPEPIPE | QMARKCOLON)
});
}
FunctionDeclaration | SecondaryConstructor => {
nesting = 0;
increment_function_depth(
&mut depth,
node,
&[FunctionDeclaration, SecondaryConstructor],
);
}
LambdaLiteral | AnonymousFunction => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for GoCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Go as G;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
G::IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
G::ForStatement
| G::ExpressionSwitchStatement
| G::TypeSwitchStatement
| G::SelectStatement => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
G::Else | G::GotoStatement => {
increment_by_one(stats);
}
G::BreakStatement | G::ContinueStatement if node.is_child(G::LabelName as u16) => {
increment_by_one(stats);
}
G::UnaryExpression => {
stats.boolean_seq.not_operator();
}
G::BinaryExpression => {
compute_booleans(node, stats, G::AMPAMP, G::PIPEPIPE);
}
G::FunctionDeclaration | G::MethodDeclaration => {
nesting = 0;
increment_function_depth(
&mut depth,
node,
&[G::FunctionDeclaration, G::MethodDeclaration],
);
}
G::FuncLiteral => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for BashCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Bash::*;
let (mut nesting, mut depth, lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement | WhileStatement | ForStatement | CStyleForStatement | CaseStatement => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ElifClause | ElseClause => {
increment_branch_extension(stats);
}
List | BinaryExpression3 => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
FunctionDefinition => {
nesting = 0;
increment_function_depth(&mut depth, node, &[FunctionDefinition]);
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for TclCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Tcl::*;
let (mut nesting, mut depth, lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
If if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Elseif => {
increment_branch_extension(stats);
}
Else => {
increment_by_one(stats);
}
While | Foreach | TernaryExpr => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Catch => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
UnaryExpr if node.child(0).is_some_and(|c| c.kind_id() == Tcl::BANG) => {
stats.boolean_seq.not_operator();
}
BinopExpr => {
compute_booleans(node, stats, AMPAMP, PIPEPIPE);
}
Procedure => {
nesting = 0;
increment_function_depth(&mut depth, node, &[Procedure]);
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for LuaCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Lua::*;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ElseifStatement => {
increment_branch_extension(stats);
}
ForStatement | WhileStatement | RepeatStatement => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ElseStatement | BreakStatement | GotoStatement => {
increment_by_one(stats);
}
UnaryExpression => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans(node, stats, And, Or);
}
FunctionDeclaration | FunctionDeclaration2 | FunctionDeclaration3 => {
nesting = 0;
increment_function_depth(
&mut depth,
node,
&[
FunctionDeclaration,
FunctionDeclaration2,
FunctionDeclaration3,
],
);
}
FunctionDefinition => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
impl Cognitive for PhpCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Php::*;
let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
IfStatement
| ForStatement
| ForeachStatement
| WhileStatement
| DoStatement
| SwitchStatement
| MatchExpression
| CatchClause
| ConditionalExpression => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
ElseClause | ElseClause2 | ElseIfClause | ElseIfClause2 => {
increment_branch_extension(stats);
}
UnaryOpExpression | UnaryOpExpression2 => {
stats.boolean_seq.not_operator();
}
BinaryExpression => {
compute_booleans_with(node, stats, |id| {
matches!(id.into(), AMPAMP | PIPEPIPE | And | Or | Xor | QMARKQMARK)
});
}
AugmentedAssignmentExpression => {
compute_booleans_with(node, stats, |id| matches!(id.into(), QMARKQMARKEQ));
}
AnonymousFunction | ArrowFunction => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
pub(crate) fn elixir_call_keyword<'a>(node: &'a Node<'a>, code: &'a [u8]) -> Option<&'a str> {
if node.kind_id() != Elixir::Call as u16 {
return None;
}
let target = node.child_by_field_name("target")?;
if target.kind_id() != Elixir::Identifier as u16 {
return None;
}
target.utf8_text(code)
}
#[inline]
pub(crate) fn elixir_is_method_macro(kw: &str) -> bool {
matches!(kw, "def" | "defp" | "defmacro" | "defmacrop")
}
#[inline]
pub(crate) fn elixir_is_class_macro(kw: &str) -> bool {
kw == "defmodule"
}
pub(crate) fn elixir_is_inside_quote_block(node: &Node<'_>, code: &[u8]) -> bool {
let mut current = node.parent();
while let Some(n) = current {
if elixir_call_keyword(&n, code) == Some("quote") {
return true;
}
current = n.parent();
}
false
}
pub(crate) fn elixir_do_block_call_children<'a>(
node: &'a Node<'a>,
) -> impl Iterator<Item = Node<'a>> + 'a {
node.children()
.filter(|child| child.kind_id() == Elixir::DoBlock as u16)
.flat_map(|do_block| do_block.children())
.filter(|stmt| stmt.kind_id() == Elixir::Call as u16)
}
impl Cognitive for ElixirCode {
fn compute<'a>(
node: &Node<'a>,
code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Elixir as E;
let (mut nesting, depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
E::Call => match elixir_call_keyword(node, code) {
Some("if" | "unless" | "for" | "while" | "case" | "cond" | "with") => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
Some(kw) if elixir_is_method_macro(kw) => {
nesting = 0;
}
_ => {}
},
E::Else => {
increment_by_one(stats);
}
E::RescueBlock | E::CatchBlock => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
E::AnonymousFunction => {
lambda += 1;
}
E::BinaryOperator | E::BinaryOperator2 | E::BinaryOperator3 => {
compute_elixir_booleans(node, stats);
}
E::UnaryOperator => {
stats.boolean_seq.not_operator();
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
implement_metric_trait!(Cognitive, PreprocCode, CcommentCode);
impl Cognitive for RubyCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
stats: &mut Stats,
nesting_map: &mut HashMap<usize, (usize, usize, usize)>,
) {
use Ruby as R;
let (mut nesting, mut depth, mut lambda) = get_nesting_from_map(node, nesting_map);
match node.kind_id().into() {
R::If if !Self::is_else_if(node) => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
R::Unless
| R::While
| R::Until
| R::For
| R::Case
| R::CaseMatch
| R::Conditional
| R::IfModifier
| R::UnlessModifier
| R::WhileModifier
| R::UntilModifier
| R::Rescue
| R::RescueModifier
| R::RescueModifier2
| R::RescueModifier3 => {
increase_nesting(stats, &mut nesting, depth, lambda);
}
R::Elsif | R::Else => {
increment_branch_extension(stats);
}
R::Break | R::Break2 | R::Next | R::Next2 | R::Redo | R::Retry => {
increment_by_one(stats);
}
R::Unary | R::Unary2 | R::Unary3 | R::Unary4 | R::Unary5
if node
.child(0)
.is_some_and(|c| matches!(c.kind_id().into(), R::BANG | R::Not)) =>
{
stats.boolean_seq.not_operator();
}
R::Binary | R::Binary2 | R::Binary3 => {
compute_ruby_booleans(node, stats);
}
R::Method | R::SingletonMethod => {
nesting = 0;
increment_function_depth(&mut depth, node, &[R::Method, R::SingletonMethod]);
}
R::Block | R::DoBlock | R::Lambda => {
lambda += 1;
}
_ => {}
}
nesting_map.insert(node.id(), (nesting, depth, lambda));
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::similar_names,
clippy::doc_markdown,
clippy::needless_raw_string_hashes,
clippy::too_many_lines
)]
mod tests {
use crate::tools::check_metrics;
use super::*;
#[test]
fn cognitive_empty_file_min_is_zero() {
let stats = Stats::default();
assert_eq!(stats.cognitive_min(), 0.0);
}
#[test]
fn python_no_cognitive() {
check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn rust_no_cognitive() {
check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn c_no_cognitive() {
check_metrics::<CppParser>("int a = 42;", "foo.c", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn mozjs_no_cognitive() {
check_metrics::<MozjsParser>("var a = 42;", "foo.js", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn javascript_no_cognitive() {
check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn python_simple_function() {
check_metrics::<PythonParser>(
"def f(a, b):
if a and b: # +2 (+1 and)
return 1
if c and d: # +2 (+1 and)
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_match_two_arm_wildcard() {
check_metrics::<PythonParser>(
"def f(x):
match x:
case 1:
return 'one'
case _:
return 'other'
",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn python_expression_statement() {
check_metrics::<PythonParser>(
"def f(a, b):
c = True and True",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn python_tuple() {
check_metrics::<PythonParser>(
"def f(a, b):
return \"%s%s\" % (a and \"Get\" or \"Set\", b)",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn python_elif_function() {
check_metrics::<PythonParser>(
"def f(a, b):
if a and b: # +2 (+1 and)
return 1
elif c and d: # +2 (+1 and)
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_more_elifs_function() {
check_metrics::<PythonParser>(
"def f(a, b):
if a and b: # +2 (+1 and)
return 1
elif c and d: # +2 (+1 and)
return 1
elif e and f: # +2 (+1 and)
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn python_if_elif_elif_else_chain() {
check_metrics::<PythonParser>(
"def f(a, b, c, d):
if a:
return 1
elif b:
return 2
elif c:
return 3
else:
return 4",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_else_if_chain_matches_elif() {
check_metrics::<PythonParser>(
"def f(a, b, c, d):
if a and b:
return 1
else:
if c and d:
return 1",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn rust_simple_function() {
check_metrics::<RustParser>(
"fn f() {
if a && b { // +2 (+1 &&)
println!(\"test\");
}
if c && d { // +2 (+1 &&)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn c_simple_function() {
check_metrics::<CppParser>(
"void f() {
if (a && b) { // +2 (+1 &&)
printf(\"test\");
}
if (c && d) { // +2 (+1 &&)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn mozjs_simple_function() {
check_metrics::<MozjsParser>(
"function f() {
if (a && b) { // +2 (+1 &&)
window.print(\"test\");
}
if (c && d) { // +2 (+1 &&)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn javascript_simple_function() {
check_metrics::<JavascriptParser>(
"function f() {
if (a && b) { // +2 (+1 &&)
console.log(\"test\");
}
if (c || d) { // +2 (+1 ||)
console.log(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_sequence_same_booleans() {
check_metrics::<PythonParser>(
"def f(a, b):
if a and b and True: # +2 (+1 sequence of and)
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn rust_sequence_same_booleans() {
check_metrics::<RustParser>(
"fn f() {
if a && b && true { // +2 (+1 sequence of &&)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
check_metrics::<RustParser>(
"fn f() {
if a || b || c || d { // +2 (+1 sequence of ||)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn c_sequence_same_booleans() {
check_metrics::<CppParser>(
"void f() {
if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
check_metrics::<CppParser>(
"void f() {
if (a || b || c || d) { // +2 (+1 sequence of ||)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn mozjs_sequence_same_booleans() {
check_metrics::<MozjsParser>(
"function f() {
if (a && b && 1 == 1) { // +2 (+1 sequence of &&)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
check_metrics::<MozjsParser>(
"function f() {
if (a || b || c || d) { // +2 (+1 sequence of ||)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn rust_not_booleans() {
check_metrics::<RustParser>(
"fn f() {
if !a && !b { // +2 (+1 &&)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
check_metrics::<RustParser>(
"fn f() {
if a && !(b && c) { // +3 (+1 &&, +1 &&)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
check_metrics::<RustParser>(
"fn f() {
if !(a || b) && !(c || d) { // +4 (+1 ||, +1 &&, +1 ||)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn c_not_booleans() {
check_metrics::<CppParser>(
"void f() {
if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
check_metrics::<CppParser>(
"void f() {
if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn mozjs_not_booleans() {
check_metrics::<MozjsParser>(
"function f() {
if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
check_metrics::<MozjsParser>(
"function f() {
if (!(a || b) && !(c || d)) { // +4 (+1 ||, +1 &&, +1 ||)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_sequence_different_booleans() {
check_metrics::<PythonParser>(
"def f(a, b):
if a and b or True: # +3 (+1 and, +1 or)
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn rust_sequence_different_booleans() {
check_metrics::<RustParser>(
"fn f() {
if a && b || true { // +3 (+1 &&, +1 ||)
println!(\"test\");
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_sequence_different_booleans() {
check_metrics::<CppParser>(
"void f() {
if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
printf(\"test\");
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn mozjs_sequence_different_booleans() {
check_metrics::<MozjsParser>(
"function f() {
if (a && b || 1 == 1) { // +3 (+1 &&, +1 ||)
window.print(\"test\");
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn python_formatted_sequence_different_booleans() {
check_metrics::<PythonParser>(
"def f(a, b):
if ( # +1
a and b and # +1
(c or d) # +1
):
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn python_1_level_nesting() {
check_metrics::<PythonParser>(
"def f(a, b):
if a: # +1
for i in range(b): # +2
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn rust_1_level_nesting() {
check_metrics::<RustParser>(
"fn f() {
if true { // +1
if true { // +2 (nesting = 1)
println!(\"test\");
} else if 1 == 1 { // +1
if true { // +3 (nesting = 2)
println!(\"test\");
}
} else { // +1
if true { // +3 (nesting = 2)
println!(\"test\");
}
}
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 11.0,
"average": 11.0,
"min": 0.0,
"max": 11.0
}"###
);
},
);
check_metrics::<RustParser>(
"fn f() {
if true { // +1
match true { // +2 (nesting = 1)
true => println!(\"test\"),
false => println!(\"test\"),
}
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_1_level_nesting() {
check_metrics::<CppParser>(
"void f() {
if (1 == 1) { // +1
if (1 == 1) { // +2 (nesting = 1)
printf(\"test\");
} else if (1 == 1) { // +1
if (1 == 1) { // +3 (nesting = 2)
printf(\"test\");
}
} else { // +1
if (1 == 1) { // +3 (nesting = 2)
printf(\"test\");
}
}
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 11.0,
"average": 11.0,
"min": 0.0,
"max": 11.0
}"###
);
},
);
}
#[test]
fn mozjs_1_level_nesting() {
check_metrics::<MozjsParser>(
"function f() {
if (1 == 1) { // +1
if (1 == 1) { // +2 (nesting = 1)
window.print(\"test\");
} else if (1 == 1) { // +1
if (1 == 1) { // +3 (nesting = 2)
window.print(\"test\");
}
} else { // +1
if (1 == 1) { // +3 (nesting = 2)
window.print(\"test\");
}
}
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 11.0,
"average": 11.0,
"min": 0.0,
"max": 11.0
}"###
);
},
);
}
#[test]
fn javascript_nesting() {
check_metrics::<JavascriptParser>(
"function f() {
if (a) { // +1
for (let i = 0; i < 10; i++) { // +2 (nesting = 1)
while (b) { // +3 (nesting = 2)
console.log(\"test\");
}
}
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn python_2_level_nesting() {
check_metrics::<PythonParser>(
"def f(a, b):
if a: # +1
for i in range(b): # +2
if b: # +3
return 1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn rust_2_level_nesting() {
check_metrics::<RustParser>(
"fn f() {
if true { // +1
for i in 0..4 { // +2 (nesting = 1)
match true { // +3 (nesting = 2)
true => println!(\"test\"),
false => println!(\"test\"),
}
}
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn python_try_construct() {
check_metrics::<PythonParser>(
"def f(a, b):
try:
for foo in bar: # +1
return a
except Exception: # +1
if a < 0: # +2
return a",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_flat_try_except() {
check_metrics::<PythonParser>(
"def f():
try:
pass
except Exception: # +1
pass",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum() as u32, 1);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn python_except_inside_if() {
check_metrics::<PythonParser>(
"def f(x):
if x: # +1
try:
pass
except Exception: # +2 (nesting = 1)
pass",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum() as u32, 3);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn python_except_inside_for() {
check_metrics::<PythonParser>(
"def f(xs):
for x in xs: # +1
try:
pass
except Exception: # +2 (nesting = 1)
pass",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum() as u32, 3);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn python_multi_except_inside_if() {
check_metrics::<PythonParser>(
"def f(x):
if x: # +1
try:
pass
except ValueError: # +2
pass
except TypeError: # +2
pass
except Exception: # +2
pass",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum() as u32, 7);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 7.0,
"average": 7.0,
"min": 0.0,
"max": 7.0
}"###
);
},
);
}
#[test]
fn mozjs_try_construct() {
check_metrics::<MozjsParser>(
"function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
for (const collector of this.collectors) {
try {
collector._onChannelRedirect(oldChannel, newChannel, flags);
} catch (ex) {
console.error(
\"StackTraceCollector.onChannelRedirect threw an exception\",
ex
);
}
}
callback.onRedirectVerifyCallback(Cr.NS_OK);
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn javascript_try_construct() {
check_metrics::<JavascriptParser>(
"function f() {
for (let i = 0; i < 10; i++) { // +1
try {
doSomething(i);
} catch (ex) { // +2 (nesting = 1)
if (ex instanceof TypeError) { // +3 (nesting = 2)
console.error(\"type error\");
}
} finally {
cleanup();
}
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn javascript_for_of_loop() {
check_metrics::<JavascriptParser>(
"function f(xs) {
let s = 0;
for (const x of xs) { // +1
s += x;
}
return s;
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn mozjs_for_of_loop() {
check_metrics::<MozjsParser>(
"function f(xs) {
let s = 0;
for (const x of xs) { // +1
s += x;
}
return s;
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn typescript_for_of_loop() {
check_metrics::<TypescriptParser>(
"function f(xs: number[]): number {
let s = 0;
for (const x of xs) { // +1
s += x;
}
return s;
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn tsx_for_of_loop() {
check_metrics::<TsxParser>(
"function f(xs: number[]): number {
let s = 0;
for (const x of xs) { // +1
s += x;
}
return s;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn rust_break_continue() {
check_metrics::<RustParser>(
"fn f() {
'tens: for ten in 0..3 { // +1
'_units: for unit in 0..=9 { // +2 (nesting = 1)
if unit % 2 == 0 { // +3 (nesting = 2)
continue;
} else if unit == 5 { // +1
continue 'tens; // +1
} else if unit == 6 { // +1
break;
} else { // +1
break 'tens; // +1
}
}
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 11.0,
"average": 11.0,
"min": 0.0,
"max": 11.0
}"###
);
},
);
}
#[test]
fn c_goto() {
check_metrics::<CppParser>(
"void f() {
OUT: for (int i = 1; i <= max; ++i) { // +1
for (int j = 2; j < i; ++j) { // +2 (nesting = 1)
if (i % j == 0) { // +3 (nesting = 2)
goto OUT; // +1
}
}
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 7.0,
"average": 7.0,
"min": 0.0,
"max": 7.0
}"###
);
},
);
}
#[test]
fn c_switch() {
check_metrics::<CppParser>(
"void f() {
switch (1) { // +1
case 1:
printf(\"one\");
break;
case 2:
printf(\"two\");
break;
case 3:
printf(\"three\");
break;
default:
printf(\"all\");
break;
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn c_ternary() {
check_metrics::<CppParser>(
"int f(int a) {
if (a) { // +1
return a > 0 ? 1 : -1; // +2 (1 + nesting 1)
}
return a > 0 ? 0 : -1; // +1
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn c_try_catch_single() {
check_metrics::<CppParser>(
"void f() {
try {
g();
} catch (const std::exception& e) { // +1
h();
}
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn c_try_multiple_catches() {
check_metrics::<CppParser>(
"void f() {
try {
g();
} catch (const std::runtime_error& e) { // +1
h();
} catch (const std::logic_error& e) { // +1
i();
} catch (...) { // +1
j();
}
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_try_catch_in_loop() {
check_metrics::<CppParser>(
"void f() {
for (int i = 0; i < 10; ++i) { // +1
try {
g();
} catch (const std::exception& e) { // +2 (nesting = 1)
h();
}
}
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_range_based_for() {
check_metrics::<CppParser>(
"int sum(const std::vector<int>& v) {
int s = 0;
for (int x : v) { // +1
s += x;
}
return s;
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn c_nested_range_based_for() {
check_metrics::<CppParser>(
"void f(const std::vector<std::vector<int>>& vv) {
for (const auto& row : vv) { // +1
for (int x : row) { // +2 (nesting = 1)
g(x);
}
}
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_nested_for() {
check_metrics::<CppParser>(
"void f(int n, int m) {
for (int i = 0; i < n; ++i) { // +1
for (int j = 0; j < m; ++j) { // +2 (nesting = 1)
for (int k = 0; k < 4; ++k) { // +3 (nesting = 2)
g(i, j, k);
}
}
}
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn c_nested_while() {
check_metrics::<CppParser>(
"void f(int n) {
while (n > 0) { // +1
while (n % 2 == 0) { // +2 (nesting = 1)
n /= 2;
}
n -= 1;
}
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_recursion() {
check_metrics::<CppParser>(
"int fact(int n) {
if (n <= 1) { // +1
return 1;
}
return n * fact(n - 1); // recursion: currently not counted
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn c_goto_sibling_jump() {
check_metrics::<CppParser>(
"void f(int n) {
if (n < 0) { // +1
goto err; // +1
}
if (n > 100) { // +1
goto err; // +1
}
return;
err:
abort();
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn c_lambda_inside_function() {
check_metrics::<CppParser>(
"int f(const std::vector<int>& v) {
auto pred = [](int x) {
if (x > 0) { // +2 (lambda nesting = 1)
return true;
}
return false;
};
return std::count_if(v.begin(), v.end(), pred);
}",
"foo.cpp",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 1.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn c_switch_fall_through() {
check_metrics::<CppParser>(
"void f(int n) {
switch (n) { // +1
case 1:
case 2:
g();
// fall-through
case 3:
h();
break;
default:
i();
break;
}
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn c_switch_in_loop() {
check_metrics::<CppParser>(
"void f(int n) {
for (int i = 0; i < n; ++i) { // +1
switch (i % 3) { // +2 (nesting = 1)
case 0:
a();
break;
case 1:
b();
break;
default:
c();
break;
}
}
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn c_macro_expanded_control_flow() {
check_metrics::<CppParser>(
"#define CHECK(x) do { if (!(x)) return; } while (0)
void f(int a, int b) {
CHECK(a); // expansion is opaque: 0
if (b < 0) { // +1
return;
}
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn mozjs_switch() {
check_metrics::<MozjsParser>(
"function f() {
switch (1) { // +1
case 1:
window.print(\"one\");
break;
case 2:
window.print(\"two\");
break;
case 3:
window.print(\"three\");
break;
default:
window.print(\"all\");
break;
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn javascript_switch() {
check_metrics::<JavascriptParser>(
"function f() {
switch (x) { // +1
case 1:
console.log(\"one\");
break;
case 2:
console.log(\"two\");
break;
default:
console.log(\"other\");
break;
}
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn python_ternary_operator() {
check_metrics::<PythonParser>(
"def f(a, b):
if a % 2: # +1
return 'c' if a else 'd' # +2
return 'a' if a else 'b' # +1",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn python_nested_functions_lambdas() {
check_metrics::<PythonParser>(
"def f(a, b):
def foo(a):
if a: # +2 (+1 nesting)
return 1
# +3 (+1 for boolean sequence +2 for lambda nesting)
bar = lambda a: lambda b: b or True or True
return bar(foo(a))(a)",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 5.0,
"average": 1.25,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn python_real_function() {
check_metrics::<PythonParser>(
"def process_raw_constant(constant, min_word_length):
processed_words = []
raw_camelcase_words = []
for raw_word in re.findall(r'[a-z]+', constant): # +1
word = raw_word.strip()
if ( # +2 (+1 if and +1 nesting)
len(word) >= min_word_length
and not (word.startswith('-') or word.endswith('-')) # +2 operators
):
if is_camel_case_word(word): # +3 (+1 if and +2 nesting)
raw_camelcase_words.append(word)
else: # +1 else
processed_words.append(word.lower())
return processed_words, raw_camelcase_words",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 9.0,
"average": 9.0,
"min": 0.0,
"max": 9.0
}"###
);
},
);
}
#[test]
fn rust_if_let_else_if_else() {
check_metrics::<RustParser>(
"pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
debugln!(\"usage::create_usage_no_title;\");
if let Some(u) = p.meta.usage_str { // +1
String::from(&*u)
} else if used.is_empty() { // +1
create_help_usage(p, true)
} else { // +1
create_smart_usage(p, used)
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn typescript_if_else_if_else() {
check_metrics::<TypescriptParser>(
"function foo() {
if (this._closed) return Promise.resolve(); // +1
if (this._tempDirectory) { // +1
this.kill();
} else if (this.connection) { // +1
this.kill();
} else { // +1
throw new Error(`Error`);
}
helper.removeEventListeners(this._listeners);
return this._processClosing;
}",
"foo.ts",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn java_no_cognitive() {
check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}
"###
);
});
}
#[test]
fn java_single_branch_function() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a){
if(a){ // +1
System.out.println(\"test1\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}
"###
);
},
);
}
#[test]
fn java_multiple_branch_function() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a, boolean b){
if(a){ // +1
System.out.println(\"test1\");
}
if(b){ // +1
System.out.println(\"test2\");
}
else { // +1
System.out.println(\"test3\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"###
);
},
);
}
#[test]
fn java_compound_conditions() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a, boolean b, boolean c, boolean d){
if(a && b){ // +2 (+1 &&)
System.out.println(\"test1\");
}
if(c && d){ // +2 (+1 &&)
System.out.println(\"test2\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn java_switch_statement() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a, boolean b, boolean c, boolean d){
switch(expr){ //+1
case 1:
System.out.println(\"test1\");
break;
case 2:
System.out.println(\"test2\");
break;
default:
System.out.println(\"test\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn java_switch_expression() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a, boolean b, boolean c, boolean d){
switch(expr){ // +1
case 1 -> System.out.println(\"test1\");
case 2 -> System.out.println(\"test2\");
default -> System.out.println(\"test\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn java_not_booleans() {
check_metrics::<JavaParser>(
"class X {
public static void print(boolean a, boolean b, boolean c, boolean d){
if (a && !(b && c)) { // +3 (+1 &&, +1 &&)
printf(\"test\");
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn java_enhanced_for_statement() {
check_metrics::<JavaParser>(
"class X {
public static int sum(int[] xs) {
int s = 0;
for (int x : xs) { // +1
s += x;
}
return s;
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn java_nested_enhanced_for_statement() {
check_metrics::<JavaParser>(
"class X {
public static void f(int[][] xss) {
for (int[] xs : xss) { // +1
for (int x : xs) { // +2 (nesting = 1)
g(x);
}
}
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn java_ternary() {
check_metrics::<JavaParser>(
"class X {
public static boolean check(int a) {
return a > 0 ? true : false; // +1
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn java_nested_ternary() {
check_metrics::<JavaParser>(
"class X {
public static String classify(int a, int b) {
if (a > 0) { // +1
return b > 0 ? (b > 10 ? \"big\" : \"small\") : \"neg\"; // +2, +3
}
return \"zero\";
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn java_labeled_break_continue() {
check_metrics::<JavaParser>(
"class X {
void scan(int[][] m) {
outer:
for (int i = 0; i < m.length; i++) { // +1
for (int j = 0; j < m[i].length; j++) { // +2
if (m[i][j] < 0) continue outer; // +3, +1
if (m[i][j] > 100) break outer; // +3, +1
}
}
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 11.0);
assert_eq!(metric.cognitive.cognitive_max(), 11.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 11.0,
"average": 11.0,
"min": 0.0,
"max": 11.0
}"###
);
},
);
}
#[test]
fn java_unlabeled_break_continue_not_counted() {
check_metrics::<JavaParser>(
"class X {
void scan(int[] m) {
for (int i = 0; i < m.length; i++) { // +1
if (m[i] < 0) continue; // +2, +0
if (m[i] > 100) break; // +2, +0
}
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
assert_eq!(metric.cognitive.cognitive_max(), 5.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 5.0,
"average": 5.0,
"min": 0.0,
"max": 5.0
}"###
);
},
);
}
#[test]
fn csharp_no_cognitive() {
check_metrics::<CsharpParser>("int a = 42;", "foo.cs", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}
"###
);
});
}
#[test]
fn csharp_single_branch_function() {
check_metrics::<CsharpParser>(
"class X {
public static void Print(bool a) {
if (a) {
System.Console.WriteLine(\"test1\");
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_multiple_branch_function() {
check_metrics::<CsharpParser>(
"class X {
public static void Print(bool a, bool b) {
if (a) {
System.Console.WriteLine(\"test1\");
}
if (b) {
System.Console.WriteLine(\"test2\");
} else {
System.Console.WriteLine(\"test3\");
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_compound_conditions() {
check_metrics::<CsharpParser>(
"class X {
public static void Print(bool a, bool b, bool c, bool d) {
if (a && b) {
System.Console.WriteLine(\"test1\");
}
if (c && d) {
System.Console.WriteLine(\"test2\");
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_switch_statement() {
check_metrics::<CsharpParser>(
"class X {
public static void Print(int expr) {
switch (expr) {
case 1:
System.Console.WriteLine(\"test1\");
break;
case 2:
System.Console.WriteLine(\"test2\");
break;
default:
System.Console.WriteLine(\"test\");
break;
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_switch_expression() {
check_metrics::<CsharpParser>(
"class X {
public static string Name(int expr) =>
expr switch {
1 => \"one\",
2 => \"two\",
_ => \"other\"
};
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_not_booleans() {
check_metrics::<CsharpParser>(
"class X {
public static void Print(bool a, bool b, bool c) {
if (a && !(b && c)) {
System.Console.WriteLine(\"test\");
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_ternary() {
check_metrics::<CsharpParser>(
"class X {
public static bool Check(int a) {
return a > 0 ? true : false; // +1
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn csharp_nested_ternary() {
check_metrics::<CsharpParser>(
"class X {
public static string Classify(int a, int b) {
if (a > 0) { // +1
return b > 0 ? (b > 10 ? \"big\" : \"small\") : \"neg\"; // +2, +3
}
return \"zero\";
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn csharp_goto_statement() {
check_metrics::<CsharpParser>(
"class X {
int Classify(int x) {
if (x < 0) goto neg; // +1, +1
return x;
neg:
return -x;
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn csharp_goto_case_and_default() {
check_metrics::<CsharpParser>(
"class X {
int Walk(int x) {
switch (x) { // +1
case 1: goto case 2; // +1
case 2: return 2;
case 3: goto default; // +1
default: return 0;
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn csharp_unlabeled_break_not_counted() {
check_metrics::<CsharpParser>(
"class X {
void Scan(int[] m) {
for (int i = 0; i < m.Length; i++) { // +1
if (m[i] < 0) break; // +2, +0
}
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn perl_no_cognitive() {
check_metrics::<PerlParser>("my $a = 42;", "foo.pl", |metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}
"#);
});
}
#[test]
fn perl_simple_function() {
check_metrics::<PerlParser>(
"sub f {
return 1;
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 0.0,
"average": 0.0,
"min": 0.0,
"max": 0.0
}
"#);
},
);
}
#[test]
fn perl_sequence_same_booleans() {
check_metrics::<PerlParser>(
"sub f {
if ($a && $b && $c) { # +1 if, +1 first &&-chain
print 'x';
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn perl_sequence_different_booleans() {
check_metrics::<PerlParser>(
"sub f {
if ($a && $b || $c) { # +1 if, +1 &&, +1 ||
print 'x';
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"#);
},
);
}
#[test]
fn perl_compound_short_circuit_assignment_249() {
check_metrics::<PerlParser>(
"sub f {
my ($x, $y, $z) = @_;
$x ||= 1; # +1 (||=)
$y &&= 2; # +1 (&&=)
$z //= 3; # +1 (//=)
return $x;
}",
"foo.pl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn perl_not_booleans() {
check_metrics::<PerlParser>(
"sub f {
if ($a && !($b && $c)) { # +1 if, +1 &&, +1 inner &&
print 'x';
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"#);
},
);
}
#[test]
fn perl_1_level_nesting() {
check_metrics::<PerlParser>(
"sub f {
for my $i (1..3) { # +1 for
if ($i % 2) { # +2 if (nested 1)
print $i;
}
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"#);
},
);
}
#[test]
fn perl_2_level_nesting() {
check_metrics::<PerlParser>(
"sub f {
for my $i (1..3) { # +1 for
while ($n > 0) { # +2 while (nested 1)
if ($n % 2) { # +3 if (nested 2)
$n--;
}
}
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}
"#);
},
);
}
#[test]
fn perl_break_continue() {
check_metrics::<PerlParser>(
"sub f {
while (1) { # +1 while (nesting becomes 1)
last if $done; # +2 postfix-if at nesting=1
next; # +0 bare loop control
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"#);
},
);
}
#[test]
fn perl_if_elsif_else() {
check_metrics::<PerlParser>(
"sub f {
if ($x) { # +1 if
print 'a';
} elsif ($y) { # +1 elsif
print 'b';
} else { # +1 else
print 'c';
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"#);
},
);
}
#[test]
fn perl_function_definition_without_sub_depth() {
check_metrics::<PerlParser>(
"method outer {
sub inner {
if (1) { } # +2 (depth=1)
}
}",
"foo.pl",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 1.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn tsx_nested_if_for_with_booleans() {
check_metrics::<TsxParser>(
"function process(items: number[]) {
if (items.length > 0) { // +1
for (let i = 0; i < items.length; i++) { // +2 (nesting=1)
if (items[i] > 0 && items[i] < 100) { // +3 (nesting=2) +1 (&&)
console.log(items[i]);
}
}
}
}",
"foo.tsx",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 7.0,
"average": 7.0,
"min": 0.0,
"max": 7.0
}"###
);
},
);
}
#[test]
fn typescript_nested_if_with_boolean_sequence() {
check_metrics::<TypescriptParser>(
"function validate(input: string, strict: boolean): boolean {
if (input.length > 0) { // +1
if (strict && input.trim() === input) { // +2 (nesting=1) +1 (&&)
return true;
}
}
return false;
}",
"foo.ts",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn typescript_try_catch_with_nesting() {
check_metrics::<TypescriptParser>(
"function fetchData(url: string): string {
try {
if (url.length === 0) { // +1
throw new Error('empty url');
}
return url;
} catch (e) { // +1
if (e instanceof Error) { // +2 (nesting=1)
return e.message;
}
return 'unknown error';
}
}",
"foo.ts",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn kotlin_cognitive_control_flow() {
check_metrics::<KotlinParser>(
"fun process(x: Int, y: Int): String {
if (x > 0) { // +1
for (i in 1..x) { // +2 (nesting=1)
if (i % 2 == 0) { // +3 (nesting=2)
println(i)
}
}
} else if (x < 0) { // +1 (else-if: flat +1 for else, if not counted as else-if)
when (y) { // +2 (nesting=1)
1 -> println(\"one\")
2 -> println(\"two\")
else -> println(\"other\")
}
} else { // +1
while (y > 0) { // +2
println(y)
}
}
return if (x > y) \"big\" else \"small\"
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 14.0,
"average": 14.0,
"min": 0.0,
"max": 14.0
}
"###
);
},
);
}
#[test]
fn kotlin_no_cognitive() {
check_metrics::<KotlinParser>("fun main() { val x = 42 }", "foo.kt", |metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 0.0,
"average": 0.0,
"min": 0.0,
"max": 0.0
}
"#);
});
}
#[test]
fn kotlin_simple_if_with_boolean() {
check_metrics::<KotlinParser>(
"fun test(a: Boolean, b: Boolean) { if (a && b) { val x = 1 } }",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn kotlin_nesting() {
check_metrics::<KotlinParser>(
"fun test(items: List<Int>) {
if (items.isNotEmpty()) {
for (i in items) {
if (i > 0) {
println(i)
}
}
}
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}
"#);
},
);
}
#[test]
fn kotlin_when_expression() {
check_metrics::<KotlinParser>(
"fun test(x: Int) { when { x > 10 -> val a = 1; x > 5 -> val b = 2; else -> val c = 3 } }",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}
"#);
},
);
}
#[test]
fn kotlin_when_else_no_increment() {
check_metrics::<KotlinParser>(
"fun test(x: Int) {
when (x) {
1 -> println(\"one\")
2 -> println(\"two\")
else -> println(\"other\")
}
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}
"#);
},
);
}
#[test]
fn kotlin_else_in_if_still_increments() {
check_metrics::<KotlinParser>(
"fun test(x: Int) {
if (x > 0) {
println(\"positive\")
} else {
println(\"non-positive\")
}
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn kotlin_else_if_chain() {
check_metrics::<KotlinParser>(
"fun test(x: Int) {
if (x > 10) {
} else if (x > 5) {
} else if (x > 0) {
} else {
}
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}
"#);
},
);
}
#[test]
fn kotlin_lambda_nesting() {
check_metrics::<KotlinParser>(
"fun test() { val f = { if (true) { } } }",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 1.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn kotlin_secondary_constructor_depth() {
check_metrics::<KotlinParser>(
"class Foo {
constructor(x: Int) {
fun inner(): Boolean {
if (x > 0) { return true } // +2 (depth=1)
return false
}
}
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 2.0,
"average": 1.0,
"min": 0.0,
"max": 2.0
}
"#);
},
);
}
#[test]
fn go_no_cognitive() {
check_metrics::<GoParser>("package main\nvar x = 42", "foo.go", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}
"###
);
});
}
#[test]
fn go_simple_function() {
check_metrics::<GoParser>(
"package main
func f(a, b bool) {
if a && b { // +1 (if) +1 (&&)
return
}
if a || b { // +1 (if) +1 (||)
return
}
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}
"###
);
},
);
}
#[test]
fn go_nesting() {
check_metrics::<GoParser>(
"package main
func f(x int, items []int) {
if x > 0 { // +1 (nesting 0)
for _, v := range items { // +2 (nesting 1)
if v > 0 { // +3 (nesting 2)
println(v)
}
}
}
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}
"###
);
},
);
}
#[test]
fn go_switch() {
check_metrics::<GoParser>(
"package main
func f(x int) {
switch x { // +1 (nesting 0)
case 1:
if x > 0 { // +2 (nesting 1)
println(x)
}
default:
println(x)
}
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"###
);
},
);
}
#[test]
fn go_goto() {
check_metrics::<GoParser>(
"package main
func f(n int) {
if n > 10 { // +1 (nesting 0)
goto end // +1 (goto)
}
end:
return
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}
"###
);
},
);
}
#[test]
fn go_else_if_chain() {
check_metrics::<GoParser>(
"package main
func f(x int) {
if x > 0 { // +1 (nesting 0)
println(x)
} else if x < 0 { // +1 (else-if)
println(-x)
} else { // +1 (else)
println(0)
}
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}
"###
);
},
);
}
#[test]
fn go_labeled_break_continue() {
check_metrics::<GoParser>(
"package main
func f() {
outer:
for i := 0; i < 3; i++ { // +1 (nesting 0)
for j := 0; j < 3; j++ { // +2 (nesting 1)
if i == j { // +3 (nesting 2)
continue outer // +1 (labeled continue)
}
}
}
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 7.0,
"average": 7.0,
"min": 0.0,
"max": 7.0
}
"###
);
},
);
}
#[test]
fn go_method_declaration() {
check_metrics::<GoParser>(
"package main
type T struct{ val int }
func (t T) positive() bool {
if t.val > 0 { // +1
return true
}
return false
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(metric.cognitive, @r#"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}
"#);
},
);
}
#[test]
fn bash_no_cognitive() {
check_metrics::<BashParser>("a=42", "foo.sh", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn bash_simple_if() {
check_metrics::<BashParser>(
"f() {
if [ -z \"$1\" ]; then # +1
echo empty
fi
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn bash_if_elif_else() {
check_metrics::<BashParser>(
"f() {
if [ \"$1\" = a ]; then # +1
echo a
elif [ \"$1\" = b ]; then # +1
echo b
else # +1
echo other
fi
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn bash_nested_loops() {
check_metrics::<BashParser>(
"f() {
for i in 1 2 3; do # +1
while [ \"$x\" -lt 10 ]; do # +2 (nested)
x=$((x+1))
done
done
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn bash_until_loop() {
check_metrics::<BashParser>(
"f() {
until [ -z \"$x\" ]; do # +1
x=$(pop)
done
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn bash_case() {
check_metrics::<BashParser>(
"f() {
case \"$1\" in # +1
a) echo a ;;
b) echo b ;;
*) echo other ;;
esac
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn bash_boolean_sequence() {
check_metrics::<BashParser>(
"f() {
if [[ -n \"$x\" ]] && [[ -n \"$y\" ]] && [[ -n \"$z\" ]]; then
# +1 if, +1 boolean (one && chain)
echo all
fi
if [[ -n \"$x\" ]] && [[ -n \"$y\" ]] || [[ -n \"$z\" ]]; then
# +1 if, +2 boolean (&& then ||)
echo mixed
fi
}",
"foo.sh",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 5.0,
"average": 5.0,
"min": 0.0,
"max": 5.0
}"###
);
},
);
}
#[test]
fn tcl_no_cognitive() {
check_metrics::<TclParser>("set x 1", "foo.tcl", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
assert_eq!(metric.cognitive.cognitive_max(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
});
}
#[test]
fn tcl_simple_function() {
check_metrics::<TclParser>(
"proc f {a} {
if {$a > 0 && $a < 10} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_sequence_same_booleans() {
check_metrics::<TclParser>(
"proc f {a b c d} {
if {$a && $b && $c} {
puts yes
}
if {$a || $b || $c || $d} {
puts no
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_sequence_different_booleans() {
check_metrics::<TclParser>(
"proc f {a b c} {
if {$a && $b || $c} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_not_booleans() {
check_metrics::<TclParser>(
"proc f {a b} {
if {!$a && !$b} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_1_level_nesting() {
check_metrics::<TclParser>(
"proc f {x} {
while {$x > 0} {
if {$x > 10} {
set x [expr {$x - 1}]
}
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_2_level_nesting() {
check_metrics::<TclParser>(
"proc f {x} {
while {$x > 0} {
foreach y {1 2 3} {
if {$y > $x} {
puts found
}
}
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_catch_cognitive() {
check_metrics::<TclParser>(
"proc f {x} {
catch {
if {$x < 0} {
error negative
}
} msg
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_if_elseif_else() {
check_metrics::<TclParser>(
"proc f {x} {
if {$x > 10} {
puts big
} elseif {$x > 5} {
puts medium
} else {
puts small
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_not_booleans_nested() {
check_metrics::<TclParser>(
"proc f {a b c} {
if {$a && !($b && $c)} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_not_booleans_double_nested() {
check_metrics::<TclParser>(
"proc f {a b c d} {
if {!($a || $b) && !($c || $d)} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_nested_procedure_cognitive() {
check_metrics::<TclParser>(
"proc outer {x} {
proc inner {y} {
if {$y > 0} {
puts positive
}
}
inner $x
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_ternary_cognitive() {
check_metrics::<TclParser>(
"proc f {x} {
set y [expr {$x > 0 ? $x : -$x}]
while {$y > 10} {
set y [expr {$y > 5 ? $y - 1 : 0}]
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn lua_cognitive_no_cognitive() {
check_metrics::<LuaParser>("local x = 42", "foo.lua", |metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
});
}
#[test]
fn lua_cognitive_simple_function() {
check_metrics::<LuaParser>(
"local function f(a, b, c, d)
if a and b then
return 1
end
if c and d then
return 1
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_sequence_same_booleans() {
check_metrics::<LuaParser>(
"local function f(a, b, c, d)
if a and b and c then
return 1
end
if a or b or c or d then
return 1
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_not_booleans() {
check_metrics::<LuaParser>(
"local function f(a, b)
if not a and not b then
return 1
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_sequence_different_booleans() {
check_metrics::<LuaParser>(
"local function f(a, b, c)
if a and b or c then
return 1
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_1_level_nesting() {
check_metrics::<LuaParser>(
"local function f(t)
for i = 1, #t do
if t[i] > 0 then
return t[i]
end
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_2_level_nesting() {
check_metrics::<LuaParser>(
"local function f(t)
for i = 1, #t do
for j = 1, #t do
if t[i] > t[j] then
return t[i]
end
end
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_break_continue() {
check_metrics::<LuaParser>(
"local function f(t)
for i = 1, #t do
if t[i] < 0 then
break
end
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn lua_cognitive_elseif_nesting() {
check_metrics::<LuaParser>(
"local function classify(x)
if x > 0 then
return 1
elseif x < 0 then
return -1
elseif x == 0 then
return 0
else
return 0
end
end",
"foo.lua",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn typescript_switch_statement() {
check_metrics::<TypescriptParser>(
"function describe(x: number): string {
switch (x) { // +1
case 1:
return 'one';
case 2:
return 'two';
default:
return 'other';
}
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn typescript_no_cognitive() {
check_metrics::<TypescriptParser>(
"function f(a: number, b: number): number {
return a + b;
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
assert_eq!(metric.cognitive.cognitive_max(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_no_cognitive() {
check_metrics::<TsxParser>(
"function f(a: number, b: number): number {
return a + b;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
assert_eq!(metric.cognitive.cognitive_max(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_simple_if() {
check_metrics::<TsxParser>(
"function f(x: number): number {
if (x > 0) { // +1
return x;
}
return 0;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_boolean_sequence() {
check_metrics::<TsxParser>(
"function f(a: boolean, b: boolean, c: boolean): boolean {
return a && b && c; // +1 (&&, sequence)
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_2_level_nesting() {
check_metrics::<TsxParser>(
"function f(a: number[], n: number): number {
for (let i = 0; i < a.length; i++) { // +1
if (a[i] > n) { // +2 (nesting=1)
return a[i];
}
}
return -1;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_else_if_chain() {
check_metrics::<TsxParser>(
"function classify(x: number): string {
if (x < 0) { // +1
return 'neg';
} else if (x === 0) { // +1 (else if = structural, not nesting)
return 'zero';
} else { // +1
return 'pos';
}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn js_sibling_bool_sequences() {
check_metrics::<JavascriptParser>(
"function f(a, b, c, d) {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn js_nested_bool_same_op() {
check_metrics::<JavascriptParser>(
"function f(a, b, c, d) {
return a || (b && c && d); // +1(||) +1(&&) = 2
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn python_sibling_bool_sequences() {
check_metrics::<PythonParser>(
"def f(a, b, c, d):
return (a and b) or (c and d) # +1(and) +1(or) +1(and) = 3
",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn python_nested_bool_same_op() {
check_metrics::<PythonParser>(
"def f(a, b, c, d):
return a or (b and c and d) # +1(or) +1(and) = 2
",
"foo.py",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn perl_sibling_bool_sequences() {
check_metrics::<PerlParser>(
"sub f {
my ($a, $b, $c, $d) = @_;
return ($a && $b) || ($c && $d); # +1(&&) +1(||) +1(&&) = 3
}",
"foo.pl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn perl_nested_bool_same_op() {
check_metrics::<PerlParser>(
"sub f {
my ($a, $b, $c, $d) = @_;
return $a || ($b && $c && $d); # +1(||) +1(&&) = 2
}",
"foo.pl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn rust_sibling_bool_sequences() {
check_metrics::<RustParser>(
"fn f(a: bool, b: bool, c: bool, d: bool) -> bool {
(a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3
}",
"foo.rs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn rust_nested_bool_same_op() {
check_metrics::<RustParser>(
"fn f(a: bool, b: bool, c: bool, d: bool) -> bool {
a || (b && c && d) // +1(||) +1(&&) = 2
}",
"foo.rs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn c_sibling_bool_sequences() {
check_metrics::<CppParser>(
"int f(int a, int b, int c, int d) {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn c_nested_bool_same_op() {
check_metrics::<CppParser>(
"int f(int a, int b, int c, int d) {
return a || (b && c && d); // +1(||) +1(&&) = 2
}",
"foo.c",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn mozjs_sibling_bool_sequences() {
check_metrics::<MozjsParser>(
"function f(a, b, c, d) {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn mozjs_nested_bool_same_op() {
check_metrics::<MozjsParser>(
"function f(a, b, c, d) {
return a || (b && c && d); // +1(||) +1(&&) = 2
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn typescript_sibling_bool_sequences() {
check_metrics::<TypescriptParser>(
"function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn typescript_nested_bool_same_op() {
check_metrics::<TypescriptParser>(
"function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
return a || (b && c && d); // +1(||) +1(&&) = 2
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_sibling_bool_sequences() {
check_metrics::<TsxParser>(
"function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tsx_nested_bool_same_op() {
check_metrics::<TsxParser>(
"function f(a: boolean, b: boolean, c: boolean, d: boolean): boolean {
return a || (b && c && d); // +1(||) +1(&&) = 2
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn javascript_nullish_coalescing_chain_230() {
check_metrics::<JavascriptParser>(
"function pick(a, b, c) {
return a ?? b ?? c; // +1 (chain of ??)
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn typescript_nullish_coalescing_with_if_230() {
check_metrics::<TypescriptParser>(
"function risky(x: string | null, fallback: string | null): string {
if (x === \"y\") { // +1
return x ?? fallback ?? \"unknown\"; // +1 (chain of ??)
}
return \"no\";
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn tsx_nullish_coalescing_chain_230() {
check_metrics::<TsxParser>(
"function pick(a: number | null, b: number | null, c: number): number {
return a ?? b ?? c; // +1 (chain of ??)
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn mozjs_nullish_coalescing_chain_230() {
check_metrics::<MozjsParser>(
"function pick(a, b, c) {
return a ?? b ?? c; // +1 (chain of ??)
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn csharp_null_coalescing_cognitive_230() {
check_metrics::<CsharpParser>(
"class C {
string Risky(string x, string fallback) {
if (x == \"y\") { // +1
return x ?? fallback ?? \"unknown\"; // +1 (chain of ??)
}
return \"no\";
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn php_null_coalescing_cognitive_230() {
check_metrics::<PhpParser>(
"<?php
function risky($x, $fallback) {
if ($x === \"y\") { // +1
return $x ?? $fallback ?? \"unknown\"; // +1 (chain of ??)
}
return \"no\";
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn php_word_form_and_forms_boolean_sequence_230() {
check_metrics::<PhpParser>(
"<?php
function check_and($a, $b, $c, $d) {
if ($a and $b and $c and $d) { // +1 (if) + 1 (and chain)
return true;
}
return false;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
},
);
}
#[test]
fn php_word_form_or_forms_boolean_sequence_230() {
check_metrics::<PhpParser>(
"<?php
function check_or($a, $b, $c, $d) {
if ($a or $b or $c or $d) { // +1 (if) + 1 (or chain)
return true;
}
return false;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
},
);
}
#[test]
fn php_word_form_xor_forms_boolean_sequence_230() {
check_metrics::<PhpParser>(
"<?php
function check_xor($a, $b, $c, $d) {
if ($a xor $b xor $c xor $d) { // +1 (if) + 1 (xor chain)
return true;
}
return false;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
},
);
}
#[test]
fn java_cognitive_else_if_chain() {
check_metrics::<JavaParser>(
"class X {
public static void f(int x) {
if (x > 10) {
} else if (x > 5) {
} else if (x > 0) {
} else {
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn java_cognitive_nested_else_if() {
check_metrics::<JavaParser>(
"class X {
public static void f(int x) {
for (int i = 0; i < x; i++) {
if (i > 10) {
} else if (i > 5) {
} else {
}
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 5.0,
"average": 5.0,
"min": 0.0,
"max": 5.0
}"###
);
},
);
}
#[test]
fn java_cognitive_if_inside_else_block_is_not_else_if() {
check_metrics::<JavaParser>(
"class X {
public static void f(int a, int c) {
if (a > 0) {
} else {
if (c > 0) {
}
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn java_sibling_bool_sequences() {
check_metrics::<JavaParser>(
"class X {
boolean f(boolean a, boolean b, boolean c, boolean d) {
return (a && b) || (c && d); // +1(&&) +1(||) +1(&&) = 3
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn java_nested_bool_same_op() {
check_metrics::<JavaParser>(
"class X {
boolean f(boolean a, boolean b, boolean c, boolean d) {
return a || (b && c && d); // +1(||) +1(&&) = 2
}
}",
"foo.java",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn groovy_no_cognitive() {
check_metrics::<GroovyParser>("class A { int x = 42 }", "foo.groovy", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
});
}
#[test]
fn groovy_single_branch_function() {
check_metrics::<GroovyParser>(
"void f(int x) {
if (x > 0) {
println(x)
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
},
);
}
#[test]
fn groovy_nested_if() {
check_metrics::<GroovyParser>(
"void f(int x, int y) {
if (x > 0) {
if (y > 0) {
println(x)
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
},
);
}
#[test]
fn groovy_else_if_chain() {
check_metrics::<GroovyParser>(
"class X {
static void f(int x) {
if (x > 10) {
} else if (x > 5) {
} else if (x > 0) {
} else {
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
},
);
}
#[test]
fn groovy_else_if_chain_lower_than_nested_ifs() {
check_metrics::<GroovyParser>(
"class X {
static void f(int x) {
if (x > 10) {
if (x > 5) {
if (x > 0) {
}
}
}
}
}",
"foo.groovy",
|metric| {
assert!(metric.cognitive.cognitive_sum() > 4.0);
},
);
}
#[test]
fn groovy_sequence_booleans_same_op() {
check_metrics::<GroovyParser>(
"void f(boolean a, boolean b, boolean c) {
if (a && b && c) { println(a) }
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
},
);
}
#[test]
fn groovy_sequence_booleans_mixed_ops() {
check_metrics::<GroovyParser>(
"void f(boolean a, boolean b, boolean c) {
if (a && b || c) { println(a) }
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
},
);
}
#[test]
fn groovy_not_operator_negation() {
check_metrics::<GroovyParser>(
"void f(boolean a, boolean b) {
if (a && !b) { println(a) }
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
},
);
}
#[test]
fn groovy_for_while_do_loops() {
check_metrics::<GroovyParser>(
"void f(int n) {
for (int i = 0; i < n; i++) {
while (i > 0) {
i--
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
},
);
}
#[test]
fn groovy_enhanced_for() {
check_metrics::<GroovyParser>(
"void f(List items) {
for (item in items) {
println(item)
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
},
);
}
#[test]
fn groovy_try_catch_nesting() {
check_metrics::<GroovyParser>(
"void f() {
try {
risky()
} catch (Exception e) {
handle(e)
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
},
);
}
#[test]
fn groovy_ternary_expression() {
check_metrics::<GroovyParser>(
"void f(int x) {
def y = (x > 0) ? 1 : 2
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
},
);
}
#[test]
fn groovy_elvis_chain_246() {
check_metrics::<GroovyParser>(
"def pick(a, b, c) {
return a ?: b ?: c // +1 (Elvis chain)
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
},
);
}
#[test]
fn groovy_elvis_inside_if_246() {
check_metrics::<GroovyParser>(
"def f(a, b) {
if (a != null) { // +1
return a ?: b ?: 'x' // +1 (Elvis chain)
}
return 'no'
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
},
);
}
#[test]
fn groovy_labeled_break_continue() {
check_metrics::<GroovyParser>(
"void f() {
outer:
for (int i = 0; i < 10; i++) {
inner:
for (int j = 0; j < 10; j++) {
if (i == j) break outer
if (i < j) continue inner
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 11.0);
},
);
}
#[test]
fn groovy_multiple_branch_function() {
check_metrics::<GroovyParser>(
"class X {
static void print(boolean a, boolean b) {
if (a) {
println 'test1'
}
if (b) {
println 'test2'
} else {
println 'test3'
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
},
);
}
#[test]
fn groovy_unlabeled_break_continue_not_counted() {
check_metrics::<GroovyParser>(
"class X {
void scan(int[] m) {
for (int i = 0; i < m.length; i++) {
if (m[i] < 0) continue
if (m[i] > 100) break
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
},
);
}
#[test]
fn groovy_cognitive_nested_else_if() {
check_metrics::<GroovyParser>(
"class X {
static void f(int x) {
for (int i = 0; i < x; i++) {
if (i > 10) {
} else if (i > 5) {
} else {
}
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
},
);
}
#[test]
fn groovy_cognitive_if_inside_else_block_is_not_else_if() {
check_metrics::<GroovyParser>(
"class X {
static void f(int a, int c) {
if (a > 0) {
} else {
if (c > 0) {
}
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
},
);
}
#[test]
fn groovy_nested_ternary() {
check_metrics::<GroovyParser>(
"class X {
static String classify(int a, int b) {
if (a > 0) {
return b > 0 ? (b > 10 ? 'big' : 'small') : 'neg'
}
return 'zero'
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
},
);
}
#[test]
fn csharp_cognitive_else_if_chain() {
check_metrics::<CsharpParser>(
"class X {
public static void F(int x) {
if (x > 10) {
} else if (x > 5) {
} else if (x > 0) {
} else {
}
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn csharp_cognitive_nested_else_if() {
check_metrics::<CsharpParser>(
"class X {
public static void F(int x) {
for (int i = 0; i < x; i++) {
if (i > 10) {
} else if (i > 5) {
} else {
}
}
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 5.0,
"average": 5.0,
"min": 0.0,
"max": 5.0
}"###
);
},
);
}
#[test]
fn csharp_cognitive_if_inside_else_block_is_not_else_if() {
check_metrics::<CsharpParser>(
"class X {
public static void F(int a, int c) {
if (a > 0) {
} else {
if (c > 0) {
}
}
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 4.0,
"average": 4.0,
"min": 0.0,
"max": 4.0
}"###
);
},
);
}
#[test]
fn csharp_sibling_bool_sequences() {
check_metrics::<CsharpParser>(
"class X {
bool F(bool a, bool b, bool c, bool d) {
return (a && b) || (c && d);
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn csharp_nested_bool_same_op() {
check_metrics::<CsharpParser>(
"class X {
bool F(bool a, bool b, bool c, bool d) {
return a || (b && c && d);
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn kotlin_sibling_bool_sequences() {
check_metrics::<KotlinParser>(
"fun f(a: Boolean, b: Boolean, c: Boolean, d: Boolean) =
(a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3",
"foo.kt",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn kotlin_nested_bool_same_op() {
check_metrics::<KotlinParser>(
"fun f(a: Boolean, b: Boolean, c: Boolean, d: Boolean) =
a || (b && c && d) // +1(||) +1(&&) = 2",
"foo.kt",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn kotlin_elvis_chain_239() {
check_metrics::<KotlinParser>(
"fun pick(a: String?, b: String?, c: String): String = a ?: b ?: c // +1 (Elvis chain)",
"foo.kt",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn kotlin_elvis_inside_if_239() {
check_metrics::<KotlinParser>(
"fun f(a: String?, b: String?): String {
if (a != null) { // +1
return a ?: b ?: \"x\" // +1 (Elvis chain)
}
return \"no\"
}",
"foo.kt",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn go_sibling_bool_sequences() {
check_metrics::<GoParser>(
"package main
func f(a, b, c, d bool) bool {
return (a && b) || (c && d) // +1(&&) +1(||) +1(&&) = 3
}",
"foo.go",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn go_nested_bool_same_op() {
check_metrics::<GoParser>(
"package main
func f(a, b, c, d bool) bool {
return a || (b && c && d) // +1(||) +1(&&) = 2
}",
"foo.go",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_sibling_bool_sequences() {
check_metrics::<TclParser>(
"proc f {a b c d} {
if {($a && $b) || ($c && $d)} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn tcl_nested_bool_same_op() {
check_metrics::<TclParser>(
"proc f {a b c d} {
if {$a || ($b && $c && $d)} {
puts yes
}
}",
"foo.tcl",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn lua_sibling_bool_sequences() {
check_metrics::<LuaParser>(
"local function f(a, b, c, d)
if (a and b) or (c and d) then
return 1
end
end",
"foo.lua",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn lua_nested_bool_same_op() {
check_metrics::<LuaParser>(
"local function f(a, b, c, d)
if a or (b and c and d) then
return 1
end
end",
"foo.lua",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn bash_sibling_bool_sequences() {
check_metrics::<BashParser>(
"f() {
if [[ -n \"$a\" ]] && [[ -n \"$b\" ]] || [[ -n \"$c\" ]] && [[ -n \"$d\" ]]; then
echo test
fi
}",
"foo.sh",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
assert_eq!(metric.cognitive.cognitive_max(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn bash_nested_bool_same_op() {
check_metrics::<BashParser>(
"f() {
if [[ -n \"$a\" ]] || [[ -n \"$b\" ]] && [[ -n \"$c\" ]] && [[ -n \"$d\" ]]; then
echo test
fi
}",
"foo.sh",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_no_cognitive() {
check_metrics::<PhpParser>("<?php $a = 42;", "foo.php", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
assert_eq!(metric.cognitive.cognitive_max(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
});
}
#[test]
fn php_simple_function() {
check_metrics::<PhpParser>(
"<?php
function f(bool $a): void {
if ($a) {
echo 'hi';
}
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_ternary() {
check_metrics::<PhpParser>(
"<?php
function check(int $a): bool {
return $a > 0 ? true : false; // +1
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 1.0,
"average": 1.0,
"min": 0.0,
"max": 1.0
}"###
);
},
);
}
#[test]
fn php_nested_ternary() {
check_metrics::<PhpParser>(
"<?php
function classify(int $a, int $b): string {
if ($a > 0) { // +1
return $b > 0 ? ($b > 10 ? 'big' : 'small') : 'neg'; // +2, +3
}
return 'zero';
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 6.0,
"average": 6.0,
"min": 0.0,
"max": 6.0
}"###
);
},
);
}
#[test]
fn php_sequence_same_booleans() {
check_metrics::<PhpParser>(
"<?php
function f(bool $a, bool $b, bool $c): bool {
return $a && $b && $c;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_sequence_different_booleans() {
check_metrics::<PhpParser>(
"<?php
function f(bool $a, bool $b, bool $c): bool {
return $a && $b || $c;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_not_booleans() {
check_metrics::<PhpParser>(
"<?php
function f(bool $a, bool $b, bool $c): bool {
return $a && !($b && $c);
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_1_level_nesting() {
check_metrics::<PhpParser>(
"<?php
function f(int $n): int {
for ($i = 0; $i < $n; $i++) {
if ($i % 2 === 0) {
return $i;
}
}
return -1;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_2_level_nesting() {
check_metrics::<PhpParser>(
"<?php
function f(int $n): int {
for ($i = 0; $i < $n; $i++) {
while ($i > 0) {
if ($i % 2 === 0) {
return $i;
}
}
}
return -1;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
assert_eq!(metric.cognitive.cognitive_max(), 6.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_break_continue() {
check_metrics::<PhpParser>(
"<?php
function f(int $n): int {
for ($i = 0; $i < $n; $i++) {
if ($i % 2 === 0) {
continue;
}
if ($i > 100) {
break;
}
}
return 0;
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 5.0);
assert_eq!(metric.cognitive.cognitive_max(), 5.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_empty_function() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def f(x) do\n x\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 0.0,
"average": null,
"min": 0.0,
"max": 0.0
}"###
);
},
);
}
#[test]
fn elixir_simple_if() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def f(x) do\n if x > 0 do\n :pos\n end\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_if_else() {
check_metrics::<ElixirParser>(
"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",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_case_arms_count_once() {
check_metrics::<ElixirParser>(
"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",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_cond_counts_once() {
check_metrics::<ElixirParser>(
"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",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_nested_if_amplifies() {
check_metrics::<ElixirParser>(
"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",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_try_rescue_catch() {
check_metrics::<ElixirParser>(
"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",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_boolean_sequence() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def f(x, y, z) do\n if x && y || z do\n :hit\n end\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_enum_reduce_is_zero() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def sum(xs) do\n Enum.reduce(xs, 0, fn x, acc -> acc + x end)\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn elixir_recursion_is_zero_documented_limitation() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def fact(0), do: 1\n def fact(n), do: n * fact(n - 1)\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn php_match_cognitive() {
check_metrics::<PhpParser>(
"<?php
function color(string $c): int {
return match ($c) {
'red' => 1,
'green' => 2,
default => 0,
};
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
assert_eq!(metric.cognitive.cognitive_max(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_no_cognitive() {
check_metrics::<RubyParser>("a = 42\n", "foo.rb", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
});
}
#[test]
fn ruby_simple_function() {
check_metrics::<RubyParser>("def foo\n a = 1\nend\n", "foo.rb", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 0.0);
insta::assert_json_snapshot!(metric.cognitive);
});
}
#[test]
fn ruby_1_level_nesting() {
check_metrics::<RubyParser>("def foo\n if a\n b\n end\nend\n", "foo.rb", |metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
});
}
#[test]
fn ruby_2_level_nesting() {
check_metrics::<RubyParser>(
"def foo\n if a\n if b\n c\n end\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_sequence_same_booleans() {
check_metrics::<RubyParser>(
"def foo\n if a && b && c\n d\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_sequence_different_booleans() {
check_metrics::<RubyParser>(
"def foo\n if a && b || c\n d\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_not_booleans() {
check_metrics::<RubyParser>(
"def foo\n if !a\n b\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 1.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_break_next() {
check_metrics::<RubyParser>(
"def foo\n while a\n break\n next\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
}
#[test]
fn ruby_else_if_chain() {
check_metrics::<RubyParser>(
"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",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 4.0);
insta::assert_json_snapshot!(metric.cognitive);
},
);
check_metrics::<RubyParser>(
"def foo\n if a\n if b\n if c\n 1\n end\n end\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 6.0);
},
);
}
#[test]
fn javascript_compound_short_circuit_assignment_236() {
check_metrics::<JavascriptParser>(
"function f(x) {
x ??= 1; // +1 (??=)
x &&= 2; // +1 (&&=)
x ||= 3; // +1 (||=)
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn typescript_compound_short_circuit_assignment_236() {
check_metrics::<TypescriptParser>(
"function f(x: number | null) {
x ??= 1; // +1 (??=)
x &&= 2; // +1 (&&=)
x ||= 3; // +1 (||=)
}",
"foo.ts",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn tsx_compound_short_circuit_assignment_236() {
check_metrics::<TsxParser>(
"function f(x: number | null) {
x ??= 1; // +1 (??=)
x &&= 2; // +1 (&&=)
x ||= 3; // +1 (||=)
}",
"foo.tsx",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn mozjs_compound_short_circuit_assignment_236() {
check_metrics::<MozjsParser>(
"function f(x) {
x ??= 1; // +1 (??=)
x &&= 2; // +1 (&&=)
x ||= 3; // +1 (||=)
}",
"foo.js",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 3.0);
assert_eq!(metric.cognitive.cognitive_max(), 3.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 3.0,
"average": 3.0,
"min": 0.0,
"max": 3.0
}"###
);
},
);
}
#[test]
fn csharp_compound_short_circuit_assignment_236() {
check_metrics::<CsharpParser>(
"class C {
int? F(int? x) {
x ??= 1; // +1 (??=)
return x ?? 0;
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
#[test]
fn php_compound_short_circuit_assignment_236() {
check_metrics::<PhpParser>(
"<?php
function f($x) {
$x ??= 1; // +1 (??=)
return $x ?? 0; // +1 (??)
}",
"foo.php",
|metric| {
assert_eq!(metric.cognitive.cognitive_sum(), 2.0);
assert_eq!(metric.cognitive.cognitive_max(), 2.0);
insta::assert_json_snapshot!(
metric.cognitive,
@r###"
{
"sum": 2.0,
"average": 2.0,
"min": 0.0,
"max": 2.0
}"###
);
},
);
}
}