#![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, for_each_named_child, wrapped_operand};
use crate::macros::go_bool_terminal_kinds;
use crate::*;
fn go_wrapper_operand<'a>(node: &Node<'a>) -> Option<(Node<'a>, bool)> {
use Go as G;
match node.kind_id().into() {
G::ParenthesizedExpression => wrapped_operand(node).map(|o| (o, false)),
G::UnaryExpression if node.child(0).is_some_and(|c| c.kind_id() == G::BANG as u16) => {
node.child_by_field_name("operand").map(|o| (o, true))
}
_ => None,
}
}
fn go_is_bool_operand(node: &Node) -> bool {
use Go as G;
matches!(node.kind_id().into(), go_bool_terminal_kinds!())
|| (node.kind_id() == G::UnaryExpression
&& node
.child(0)
.is_some_and(|op| matches!(op.kind_id().into(), G::STAR | G::LTDASH)))
}
fn go_inspect_container(container_node: &Node, parent: &Node, conditions: &mut f64) {
use Go as G;
let mut node = *container_node;
let mut has_boolean_content = matches!(
parent.kind_id().into(),
G::BinaryExpression | G::IfStatement | G::ForStatement | G::ExpressionCase
);
while let Some((operand, proves_boolean)) = go_wrapper_operand(&node) {
has_boolean_content |= proves_boolean;
node = operand;
if go_is_bool_operand(&node) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn go_count_condition(condition: &Node, parent: &Node, conditions: &mut f64) {
if go_is_bool_operand(condition) {
*conditions += 1.;
} else if go_wrapper_operand(condition).is_some() {
go_inspect_container(condition, parent, conditions);
}
}
fn go_walk_for_statement(node: &Node, conditions: &mut f64) {
use Go as G;
let Some(header) = node
.children()
.find(|child| child.is_named() && !matches!(child.kind_id().into(), G::Comment | G::Block))
else {
return;
};
let slot = if matches!(header.kind_id().into(), G::ForClause) {
header.child_by_field_name("condition")
} else {
Some(header)
};
if let Some(condition) = slot {
go_count_condition(&condition, node, conditions);
}
}
fn go_count_expression_case<'a>(
case: &Node<'a>,
ancestors: Ancestors<'a, '_>,
conditions: &mut f64,
) {
let tagless = ancestors.parent(case).is_some_and(|switch| {
switch.kind_id() == Go::ExpressionSwitchStatement
&& switch.child_by_field_name("value").is_none()
});
if !tagless {
*conditions += 1.;
return;
}
for expr in case
.child_by_field_name("value")
.into_iter()
.flat_map(|list| list.children())
{
go_count_condition(&expr, case, conditions);
}
}
fn go_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
use Go as G;
let list_kind = list_node.kind_id().into();
let mut cursor = list_node.cursor();
if cursor.goto_first_child() {
loop {
let node = cursor.node();
if go_is_bool_operand(&node) && matches!(list_kind, G::BinaryExpression) {
*conditions += 1.;
} else if node.is_named() {
go_inspect_container(&node, list_node, conditions);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
impl Abc for GoCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
ancestors: Ancestors<'a, '_>,
stats: &mut Stats,
) {
use Go as G;
match node.kind_id().into() {
G::AssignmentStatement | G::ShortVarDeclaration | G::IncStatement | G::DecStatement => {
stats.assignments += 1.;
}
G::VarSpec if node.child_by_field_name("value").is_some() => {
stats.assignments += 1.;
}
G::CallExpression => {
stats.branches += 1.;
}
G::EQEQ
| G::BANGEQ
| G::LTEQ
| G::GTEQ
| G::Else
| G::TypeCase
| G::CommunicationCase => {
stats.conditions += 1.;
}
G::ExpressionCase => {
go_count_expression_case(node, ancestors, &mut stats.conditions);
}
G::LT | G::GT
if ancestors
.parent(node)
.is_some_and(|p| matches!(p.kind_id().into(), G::BinaryExpression)) =>
{
stats.conditions += 1.;
}
G::AMPAMP | G::PIPEPIPE => {
if let Some(parent) = ancestors.parent(node) {
go_count_unary_conditions(&parent, &mut stats.conditions);
}
}
G::IfStatement => {
if let Some(cond) = node.child_by_field_name("condition") {
go_count_condition(&cond, node, &mut stats.conditions);
}
}
G::ForStatement => {
go_walk_for_statement(node, &mut stats.conditions);
}
G::ReturnStatement => {
if let Some(expr_list) = wrapped_operand(node) {
for_each_named_child(&expr_list, &mut stats.conditions, go_inspect_container);
}
}
G::ArgumentList | G::ArgumentList2 => {
go_count_unary_conditions(node, &mut stats.conditions);
}
_ => {}
}
}
}