#![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::php_bool_terminal_kinds;
use crate::*;
fn php_inspect_container(container_node: &Node, parent: &Node, conditions: &mut f64) {
use Php::*;
let mut node = *container_node;
let mut node_kind = node.kind_id().into();
let parent_kind = parent.kind_id().into();
let mut has_boolean_content = matches!(
parent_kind,
BinaryExpression | IfStatement | WhileStatement | DoStatement | ForStatement
) || (matches!(parent_kind, ConditionalExpression)
&& parent
.child_by_field_name("condition")
.is_some_and(|condition| condition.id() == node.id()));
loop {
let is_parens = matches!(node_kind, ParenthesizedExpression);
let is_not = matches!(node_kind, UnaryOpExpression)
&& 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, php_bool_terminal_kinds!()) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn php_inspect_child(node: &Node, idx: usize, conditions: &mut f64) {
if let Some(child) = node.child(idx) {
php_inspect_container(&child, node, conditions);
}
}
fn php_walk_ternary(node: &Node, conditions: &mut f64) {
if let Some(condition) = node.child_by_field_name("condition") {
if matches!(condition.kind_id().into(), php_bool_terminal_kinds!()) {
*conditions += 1.;
} else {
php_inspect_container(&condition, node, conditions);
}
}
for field in ["body", "alternative"] {
if let Some(branch) = node.child_by_field_name(field) {
php_inspect_container(&branch, node, conditions);
}
}
}
fn php_argument_value<'a>(argument: &Node<'a>) -> Option<Node<'a>> {
let mut cursor = argument.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 php_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
use Php::*;
let list_kind = list_node.kind_id().into();
for node in list_node.children() {
let node_kind = node.kind_id().into();
let (inner, inner_parent) = if matches!(node_kind, Argument) {
match php_argument_value(&node) {
Some(value) => (value, node),
None => continue,
}
} else {
(node, *list_node)
};
let inner_kind = inner.kind_id().into();
if matches!(inner_kind, php_bool_terminal_kinds!()) && matches!(list_kind, BinaryExpression)
{
*conditions += 1.;
} else if inner.is_named() {
php_inspect_container(&inner, &inner_parent, conditions);
}
}
}
impl Abc for PhpCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
ancestors: Ancestors<'a, '_>,
stats: &mut Stats,
) {
use Php::*;
match node.kind_id().into() {
AssignmentExpression
| AugmentedAssignmentExpression
| ReferenceAssignmentExpression
| PLUSPLUS
| DASHDASH => {
stats.assignments += 1.;
}
FunctionCallExpression
| MemberCallExpression
| ScopedCallExpression
| NullsafeMemberCallExpression
| ObjectCreationExpression => {
stats.branches += 1.;
}
EQEQ
| EQEQEQ
| BANGEQ
| BANGEQEQ
| LT
| GT
| LTEQ
| GTEQ
| LTEQGT
| LTGT
| Instanceof
| ElseClause
| ElseClause2
| ElseIfClause
| ElseIfClause2
| CaseStatement
| MatchConditionalExpression
| CatchClause => {
stats.conditions += 1.;
}
AMPAMP | PIPEPIPE | And | Or | Xor => {
if let Some(parent) = ancestors.parent(node) {
php_count_unary_conditions(&parent, &mut stats.conditions);
}
}
IfStatement | WhileStatement | ReturnStatement => {
php_inspect_child(node, 1, &mut stats.conditions);
}
DoStatement => {
php_inspect_child(node, 3, &mut stats.conditions);
}
Arguments => {
php_count_unary_conditions(node, &mut stats.conditions);
}
ConditionalExpression => {
stats.conditions += 1.;
php_walk_ternary(node, &mut stats.conditions);
}
_ => {}
}
}
}