#![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, DeclKind, Stats};
use crate::macros::groovy_bool_terminal_kinds;
use crate::*;
fn groovy_inspect_container(container_node: &Node, conditions: &mut f64) {
use Groovy::*;
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 = match parent.kind_id().into() {
BinaryExpression | IfStatement | WhileStatement | DoWhileStatement | ForStatement => true,
TernaryExpression => node
.previous_sibling()
.is_none_or(|prev_node| !matches!(prev_node.kind_id().into(), QMARK | COLON)),
_ => false,
};
loop {
let is_parenthesised_exp = matches!(node_kind, ParenthesizedExpression);
let is_not_operator = matches!(node_kind, UnaryExpression)
&& node
.child(0)
.is_some_and(|c| matches!(c.kind_id().into(), BANG));
if !is_parenthesised_exp && !is_not_operator {
break;
}
if !has_boolean_content && is_not_operator {
has_boolean_content = true;
}
let Some(child) = node.child(1) else { break };
node = child;
node_kind = node.kind_id().into();
if matches!(node_kind, groovy_bool_terminal_kinds!()) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn groovy_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
use Groovy::*;
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, groovy_bool_terminal_kinds!())
&& matches!(list_kind, BinaryExpression)
{
*conditions += 1.;
} else {
groovy_inspect_container(&node, conditions);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
fn groovy_inspect_child(node: &Node, idx: usize, conditions: &mut f64) {
if let Some(child) = node.child(idx) {
groovy_inspect_container(&child, conditions);
}
}
fn groovy_count_token_assignment(node: &Node, stats: &mut Stats) -> bool {
use Groovy::*;
match node.kind_id().into() {
STAREQ | SLASHEQ | PERCENTEQ | DASHEQ | PLUSEQ | LTLTEQ | GTGTEQ | AMPEQ | PIPEEQ
| CARETEQ | GTGTGTEQ | PLUSPLUS | DASHDASH => {
stats.assignments += 1.;
}
FieldDeclaration | LocalVariableDeclaration => {
stats.declaration.push(DeclKind::Var);
}
Final => {
if let Some(DeclKind::Var) = stats.declaration.last() {
stats.declaration.push(DeclKind::Const);
}
}
SEMI => {
if let Some(DeclKind::Const | DeclKind::Var) = stats.declaration.last() {
stats.declaration.clear();
}
}
EQ => {
if stats
.declaration
.last()
.is_none_or(|decl| matches!(decl, DeclKind::Var))
{
stats.assignments += 1.;
}
}
_ => return false,
}
true
}
fn groovy_count_token_branch(node: &Node, stats: &mut Stats) -> bool {
use Groovy::*;
if matches!(node.kind_id().into(), MethodInvocation | CommandChain | New) {
stats.branches += 1.;
return true;
}
false
}
fn groovy_count_token_condition(node: &Node, stats: &mut Stats) -> bool {
use Groovy::*;
match node.kind_id().into() {
GTEQ | LTEQ | EQEQ | BANGEQ | Else | Case | QMARK | Try | Catch => {
stats.conditions += 1.;
}
GT | LT => {
if let Some(parent) = node.parent()
&& !matches!(parent.kind_id().into(), TypeArguments)
{
stats.conditions += 1.;
}
}
_ => return false,
}
true
}
fn groovy_walk_for_conditions(node: &Node, stats: &mut Stats) {
use Groovy::*;
let conds = &mut stats.conditions;
match node.kind_id().into() {
AMPAMP | PIPEPIPE => {
if let Some(parent) = node.parent() {
groovy_count_unary_conditions(&parent, conds);
}
}
ArgumentList => groovy_count_unary_conditions(node, conds),
VariableDeclarator | AssignmentExpression => groovy_inspect_child(node, 2, conds),
IfStatement | WhileStatement => {
if let Some(condition) = node.child(2) {
groovy_count_condition(&condition, conds);
}
}
DoWhileStatement => {
if let Some(condition) = node.child(4) {
groovy_count_condition(&condition, conds);
}
}
ReturnStatement => groovy_inspect_child(node, 1, conds),
TernaryExpression => groovy_walk_ternary(node, stats),
ForStatement => groovy_walk_for_statement(node, stats),
_ => {}
}
}
fn groovy_walk_ternary(node: &Node, stats: &mut Stats) {
let conds = &mut stats.conditions;
if let Some(condition) = node.child(0) {
groovy_count_condition(&condition, conds);
}
groovy_inspect_child(node, 2, conds);
groovy_inspect_child(node, 4, conds);
}
fn groovy_walk_for_statement(node: &Node, stats: &mut Stats) {
use Groovy::*;
let Some(condition) = node.child(3) else {
return;
};
if !matches!(condition.kind_id().into(), SEMI) {
groovy_count_condition(&condition, &mut stats.conditions);
return;
}
let Some(cond) = node.child(4) else { return };
if matches!(cond.kind_id().into(), SEMI | RPAREN) {
stats.conditions += 1.;
} else {
groovy_count_condition(&cond, &mut stats.conditions);
}
}
impl Abc for GroovyCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
if groovy_count_token_assignment(node, stats) {
return;
}
if groovy_count_token_branch(node, stats) {
return;
}
if groovy_count_token_condition(node, stats) {
return;
}
groovy_walk_for_conditions(node, stats);
}
}
fn groovy_count_condition(condition: &Node, conditions: &mut f64) {
use Groovy::*;
match condition.kind_id().into() {
groovy_bool_terminal_kinds!() => {
*conditions += 1.;
}
ParenthesizedExpression | UnaryExpression => {
groovy_inspect_container(condition, conditions);
}
_ => {}
}
}