#![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, count_boolean_slot, count_field_operands, count_negated_operand, is_operand,
wrapped_operand,
};
use crate::*;
fn php_wrapper_operand<'a>(node: &Node<'a>) -> Option<(Node<'a>, bool)> {
use Php::*;
match node.kind_id().into() {
ParenthesizedExpression | ErrorSuppressionExpression => {
wrapped_operand(node).map(|o| (o, false))
}
CastExpression | CastExpression2 => node.child_by_field_name("value").map(|o| (o, false)),
AssignmentExpression | ReferenceAssignmentExpression => {
node.child_by_field_name("right").map(|o| (o, false))
}
UnaryOpExpression | UnaryOpExpression2
if node
.child_by_field_name("operator")
.is_some_and(|op| op.kind_id() == BANG) =>
{
node.child_by_field_name("argument").map(|o| (o, true))
}
_ => None,
}
}
fn php_condition_scores_itself(expr: &Node) -> bool {
use Php::*;
match expr.kind_id().into() {
ConditionalExpression => true,
BinaryExpression => expr.child_by_field_name("operator").is_some_and(|op| {
matches!(
op.kind_id().into(),
EQEQ | EQEQEQ
| BANGEQ
| BANGEQEQ
| LT
| GT
| LTEQ
| GTEQ
| LTEQGT
| LTGT
| Instanceof
| AMPAMP
| PIPEPIPE
| And
| Or
)
}),
_ => false,
}
}
fn php_count_condition(condition: &Node, conditions: &mut f64) {
count_boolean_slot(
condition,
php_wrapper_operand,
php_condition_scores_itself,
conditions,
);
}
fn php_count_negated(operand: &Node, conditions: &mut f64) {
count_negated_operand(
operand,
php_wrapper_operand,
php_condition_scores_itself,
conditions,
);
}
fn php_count_slot(slot: Option<Node>, conditions: &mut f64) {
if let Some(condition) = slot {
php_count_condition(&condition, conditions);
}
}
fn php_walk_ternary(node: &Node, conditions: &mut f64) {
php_count_slot(node.child_by_field_name("condition"), conditions);
for field in ["body", "alternative"] {
if let Some(branch) = node.child_by_field_name(field) {
php_count_negated(&branch, conditions);
}
}
}
fn php_argument_value<'a>(argument: &Node<'a>) -> Option<Node<'a>> {
argument.children().filter(Node::is_named).last()
}
fn php_count_arguments(arguments: &Node, conditions: &mut f64) {
for argument in arguments.children() {
let value = if argument.kind_id() == Php::Argument {
php_argument_value(&argument)
} else {
Some(argument).filter(is_operand)
};
if let Some(value) = value {
php_count_negated(&value, 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
| CaseStatement
| MatchConditionalExpression
| CatchClause => {
stats.conditions += 1.;
}
AMPAMP | PIPEPIPE | And | Or => {
if let Some(chain) = ancestors
.parent(node)
.filter(|p| p.kind_id() == BinaryExpression)
{
count_field_operands(&chain, php_count_condition, &mut stats.conditions);
}
}
ElseIfClause | ElseIfClause2 => {
stats.conditions += 1.;
php_count_slot(node.child_by_field_name("condition"), &mut stats.conditions);
}
IfStatement | WhileStatement | DoStatement | ForStatement => {
php_count_slot(node.child_by_field_name("condition"), &mut stats.conditions);
}
ReturnStatement => {
if let Some(value) = wrapped_operand(node) {
php_count_negated(&value, &mut stats.conditions);
}
}
Arguments => php_count_arguments(node, &mut stats.conditions),
ConditionalExpression => {
stats.conditions += 1.;
php_walk_ternary(node, &mut stats.conditions);
}
_ => {}
}
}
}