#![allow(
clippy::enum_glob_use,
clippy::too_many_lines,
clippy::wildcard_imports
)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use super::{Abc, Stats};
use crate::macros::kotlin_bool_terminal_kinds;
use crate::*;
fn kotlin_eq_initializes_immutable_binding(eq_node: &Node) -> bool {
use Kotlin::*;
let Some(parent) = eq_node.parent() else {
return false;
};
if !matches!(
parent.kind_id().into(),
PropertyDeclaration | ClassParameter
) {
return false;
}
parent.children().any(|child| child.kind_id() == Val)
}
fn kotlin_inspect_container(container_node: &Node, conditions: &mut f64) {
use Kotlin::*;
let mut node = *container_node;
let mut node_kind = node.kind_id().into();
let Some(parent) = node.parent() else { return };
let mut has_boolean_content = matches!(
parent.kind_id().into(),
BinaryExpression | IfExpression | WhileStatement | DoWhileStatement | ForStatement
);
loop {
let is_parens = matches!(node_kind, ParenthesizedExpression);
let is_not = matches!(node_kind, UnaryExpression)
&& node.child(0).is_some_and(|c| c.kind_id() == BANG as u16);
if !is_parens && !is_not {
break;
}
if !has_boolean_content && is_not {
has_boolean_content = true;
}
let Some(child) = node.child(1) else { break };
node = child;
node_kind = node.kind_id().into();
if matches!(node_kind, kotlin_bool_terminal_kinds!()) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn kotlin_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
use Kotlin::*;
let list_kind = list_node.kind_id().into();
let mut cursor = list_node.cursor();
if cursor.goto_first_child() {
loop {
let node = cursor.node();
let node_kind = node.kind_id().into();
if matches!(node_kind, kotlin_bool_terminal_kinds!())
&& matches!(list_kind, BinaryExpression)
{
*conditions += 1.;
} else if node.is_named() {
kotlin_inspect_container(&node, conditions);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
fn kotlin_count_condition(condition: &Node, conditions: &mut f64) {
use Kotlin::*;
let kind = condition.kind_id().into();
if matches!(kind, kotlin_bool_terminal_kinds!()) {
*conditions += 1.;
} else if matches!(kind, ParenthesizedExpression | UnaryExpression) {
kotlin_inspect_container(condition, conditions);
}
}
impl Abc for KotlinCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Kotlin::*;
match node.kind_id().into() {
PLUSEQ | DASHEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSPLUS | DASHDASH => {
stats.assignments += 1.;
}
EQ if !kotlin_eq_initializes_immutable_binding(node) => {
stats.assignments += 1.;
}
CallExpression => {
stats.branches += 1.;
}
LTEQ | GTEQ | EQEQ | EQEQEQ | BANGEQ | BANGEQEQ | Try | CatchBlock | QMARKCOLON
| AsQMARK => {
stats.conditions += 1.;
}
IfExpression | WhileStatement | DoWhileStatement => {
if let Some(condition) = node.child_by_field_name("condition") {
kotlin_count_condition(&condition, &mut stats.conditions);
}
}
WhenEntry if !crate::metrics::cyclomatic::kotlin_when_entry_is_else(node) => {
stats.conditions += 1.;
}
Else if node.parent().is_some_and(|p| p.kind_id() == IfExpression) => {
stats.conditions += 1.;
}
LT | GT
if node.parent().is_some_and(|p| {
!matches!(p.kind_id().into(), TypeArguments | TypeParameters)
}) =>
{
stats.conditions += 1.;
}
AMPAMP | PIPEPIPE => {
if let Some(parent) = node.parent() {
kotlin_count_unary_conditions(&parent, &mut stats.conditions);
}
}
_ => {}
}
}
}