#![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_each_operand, count_field_operands,
count_negated_operand, last_operand, wrapped_operand,
};
use crate::*;
fn rust_wrapper_operand<'a>(node: &Node<'a>) -> Option<(Node<'a>, bool)> {
use Rust::*;
match node.kind_id().into() {
AwaitExpression => {
let mut operand = wrapped_operand(node)?;
while let Some(inner) = rust_transparent_operand(&operand) {
operand = inner;
}
if operand.kind_id() == AsyncBlock as u16 {
operand = wrapped_operand(&operand)?;
}
Some((operand, false))
}
UnaryExpression if node.child(0).is_some_and(|c| c.kind_id() == BANG as u16) => {
wrapped_operand(node).map(|o| (o, true))
}
_ => rust_transparent_operand(node).map(|o| (o, false)),
}
}
fn rust_transparent_operand<'a>(node: &Node<'a>) -> Option<Node<'a>> {
use Rust::*;
match node.kind_id().into() {
ParenthesizedExpression | UnsafeBlock => wrapped_operand(node),
ConstBlock => node.child_by_field_name("body"),
TypeCastExpression => node.child_by_field_name("value"),
Block | Block2 => last_operand(node),
ExpressionStatement if !node.is_child(SEMI as u16) => wrapped_operand(node),
_ => None,
}
}
fn rust_condition_scores_itself(expr: &Node) -> bool {
use Rust::*;
match expr.kind_id().into() {
LetCondition | LetChain | LetChain2 => true,
BinaryExpression => expr.child_by_field_name("operator").is_some_and(|op| {
matches!(
op.kind_id().into(),
EQEQ | BANGEQ | LT | GT | LTEQ | GTEQ | AMPAMP | PIPEPIPE
)
}),
_ => false,
}
}
fn rust_count_condition(condition: &Node, conditions: &mut f64) {
count_boolean_slot(
condition,
rust_wrapper_operand,
rust_condition_scores_itself,
conditions,
);
}
fn rust_count_negated(operand: &Node, conditions: &mut f64) {
count_negated_operand(
operand,
rust_wrapper_operand,
rust_condition_scores_itself,
conditions,
);
}
fn rust_count_slot(slot: Option<Node>, conditions: &mut f64) {
if let Some(condition) = slot {
rust_count_condition(&condition, conditions);
}
}
fn rust_count_match_arm(node: &Node, conditions: &mut f64) {
let Some(pattern) = node.child_by_field_name("pattern") else {
*conditions += 1.;
return;
};
if !super::npa::pattern_is_bare_underscore(&pattern, Rust::UNDERSCORE as u16) {
*conditions += 1.;
}
rust_count_slot(pattern.child_by_field_name("condition"), conditions);
}
fn rust_count_chain_operands(token: &Node, chain: &Node, conditions: &mut f64) {
if chain.kind_id() == Rust::BinaryExpression {
count_field_operands(chain, rust_count_condition, conditions);
} else if matches!(chain.kind_id().into(), Rust::LetChain | Rust::LetChain2)
&& chain
.children()
.find(|child| child.kind_id() == Rust::AMPAMP)
.is_some_and(|first| first.id() == token.id())
{
count_each_operand(chain, rust_count_condition, conditions);
}
}
impl Abc for RustCode {
fn compute<'a>(
node: &Node<'a>,
_code: &'a [u8],
ancestors: Ancestors<'a, '_>,
stats: &mut Stats,
) {
use Rust::*;
match node.kind_id().into() {
AssignmentExpression | CompoundAssignmentExpr => {
stats.assignments += 1.;
}
LetDeclaration if node.child_by_field_name("value").is_some() => {
stats.assignments += 1.;
}
CallExpression | TryExpression => {
stats.branches += 1.;
}
LTEQ | GTEQ | EQEQ | BANGEQ | LetCondition | Else => {
stats.conditions += 1.;
}
LT | GT
if ancestors
.parent(node)
.is_some_and(|p| matches!(p.kind_id().into(), BinaryExpression)) =>
{
stats.conditions += 1.;
}
MatchArm | MatchArm2 => rust_count_match_arm(node, &mut stats.conditions),
AMPAMP | PIPEPIPE => {
if let Some(chain) = ancestors.parent(node) {
rust_count_chain_operands(node, &chain, &mut stats.conditions);
}
}
IfExpression | WhileExpression => {
rust_count_slot(node.child_by_field_name("condition"), &mut stats.conditions);
}
ReturnExpression => {
if let Some(value) = wrapped_operand(node) {
rust_count_negated(&value, &mut stats.conditions);
}
}
Arguments => count_each_operand(node, rust_count_negated, &mut stats.conditions),
_ => {}
}
}
}