#![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::perl_bool_terminal_kinds;
use crate::*;
fn perl_inspect_container(container_node: &Node, conditions: &mut f64) {
use Perl as P;
let mut node = *container_node;
let mut node_kind = node.kind_id().into();
let Some(parent) = node.parent() else { return };
let parent_kind = parent.kind_id().into();
let mut has_boolean_content = matches!(
parent_kind,
P::BinaryExpression
| P::IfStatement
| P::UnlessStatement
| P::WhileStatement
| P::UntilStatement
) || (matches!(parent_kind, P::TernaryExpression)
&& node
.previous_sibling()
.is_none_or(|prev| !matches!(prev.kind_id().into(), P::QMARK | P::COLON)));
loop {
let is_parens = matches!(node_kind, P::ParenthesizedArgument | P::Array);
let is_not = matches!(node_kind, P::UnaryExpression)
&& node.child(0).is_some_and(|c| c.kind_id() == P::BANG as u16);
if !is_parens && !is_not {
break;
}
if !has_boolean_content && is_not {
has_boolean_content = true;
}
let next = if matches!(node_kind, P::Array) {
perl_last_named_child(&node)
} else {
node.child(1)
};
let Some(child) = next else { break };
node = child;
node_kind = node.kind_id().into();
if matches!(node_kind, perl_bool_terminal_kinds!()) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn perl_inspect_child(node: &Node, idx: usize, conditions: &mut f64) {
if let Some(child) = node.child(idx) {
perl_inspect_container(&child, conditions);
}
}
fn perl_last_named_child<'a>(node: &Node<'a>) -> Option<Node<'a>> {
let mut cursor = node.cursor();
let mut last_named = None;
if cursor.goto_first_child() {
loop {
let child = cursor.node();
if child.is_named() {
last_named = Some(child);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
last_named
}
fn perl_is_call_argument_parent(parent: Node) -> bool {
use Perl as P;
matches!(
parent.kind_id().into(),
P::CallExpressionWithArgsWithBrackets
| P::CallExpressionWithSpacedArgs
| P::CallExpressionWithSub
| P::CallExpressionWithVariable
| P::CallExpressionRecursive
| P::MethodInvocation
)
}
fn perl_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
use Perl as P;
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, perl_bool_terminal_kinds!())
&& matches!(list_kind, P::BinaryExpression)
{
*conditions += 1.;
} else if node.is_named() {
perl_inspect_container(&node, conditions);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
impl Abc for PerlCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Perl as P;
match node.kind_id().into() {
P::EQ
| P::PLUSEQ
| P::DASHEQ
| P::STAREQ
| P::SLASHEQ
| P::PERCENTEQ
| P::STARSTAREQ
| P::DOTEQ
| P::XEQ
| P::AMPEQ
| P::PIPEEQ
| P::CARETEQ
| P::LTLTEQ
| P::GTGTEQ
| P::AMPAMPEQ
| P::PIPEPIPEEQ
| P::SLASHSLASHEQ
| P::AMPDOTEQ
| P::PIPEDOTEQ
| P::CARETDOTEQ => {
stats.assignments += 1.;
}
P::CallExpressionWithSpacedArgs
| P::CallExpressionWithSub
| P::CallExpressionWithArgsWithBrackets
| P::CallExpressionWithVariable
| P::CallExpressionRecursive
| P::MethodInvocation => {
stats.branches += 1.;
}
P::CallExpressionWithBareword
if !node.parent().is_some_and(|p| {
matches!(
p.kind_id().into(),
P::CallExpressionWithSpacedArgs
| P::CallExpressionWithSub
| P::CallExpressionWithArgsWithBrackets
| P::CallExpressionWithVariable
| P::CallExpressionRecursive
)
}) =>
{
stats.branches += 1.;
}
P::EQEQ | P::BANGEQ | P::LT | P::GT | P::LTEQ | P::GTEQ | P::LTEQGT
| P::Eq | P::Ne | P::Lt | P::Gt | P::Le | P::Ge | P::Cmp
| P::EQTILDE | P::BANGTILDE
| P::TernaryExpression
| P::ElsifClause
| P::ElseClause => {
stats.conditions += 1.;
}
P::AMPAMP | P::PIPEPIPE | P::SLASHSLASH | P::And | P::Or | P::Xor => {
if let Some(parent) = node.parent() {
perl_count_unary_conditions(&parent, &mut stats.conditions);
}
}
P::IfStatement
| P::UnlessStatement
| P::WhileStatement
| P::UntilStatement
| P::ReturnExpression => {
perl_inspect_child(node, 1, &mut stats.conditions);
}
P::Array if node.parent().is_some_and(perl_is_call_argument_parent) => {
perl_count_unary_conditions(node, &mut stats.conditions);
}
_ => {}
}
}
}