#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use super::*;
fn elixir_is_anonymous_fn_head_clause<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> bool {
use Elixir as E;
let Some(parent) = ancestors.parent(node) else {
return false;
};
if parent.kind_id() != E::AnonymousFunction as u16 {
return false;
}
parent
.children()
.find(|child| child.kind_id() == E::StabClause as u16)
.is_some_and(|first| first.id() == node.id())
}
fn elixir_sole_unguarded_pattern<'a>(node: &Node<'a>) -> Option<Node<'a>> {
let left = node
.child_by_field_name("left")
.filter(|left| left.kind() == "arguments")?;
let mut patterns = left.children().filter(Node::is_named);
let sole = patterns.next()?;
patterns.next().is_none().then_some(sole)
}
fn elixir_is_default_clause<'a>(
node: &Node<'a>,
code: &'a [u8],
ancestors: Ancestors<'a, '_>,
) -> bool {
use Elixir as E;
let Some(pattern) = elixir_sole_unguarded_pattern(node) else {
return false;
};
match pattern.kind_id().into() {
E::Identifier => {
pattern.utf8_text(code) == Some("_")
&& ancestors
.parent(node)
.is_none_or(|parent| parent.kind_id() != E::AnonymousFunction as u16)
}
E::Boolean => {
if pattern.utf8_text(code) != Some("true") {
return false;
}
let mut chain = ancestors.iter(node);
chain
.next()
.is_some_and(|(parent, _)| parent.kind_id() == E::DoBlock as u16)
&& chain.next().is_some_and(|(grandparent, _)| {
crate::metrics::cognitive::elixir_call_keyword(&grandparent, code)
== Some("cond")
})
}
_ => false,
}
}
impl Cyclomatic for ElixirCode {
fn compute<'a>(
node: &Node<'a>,
code: &'a [u8],
ancestors: Ancestors<'a, '_>,
stats: &mut Stats,
) {
use Elixir as E;
match node.kind_id().into() {
E::StabClause
if elixir_is_anonymous_fn_head_clause(node, ancestors)
|| elixir_is_default_clause(node, code, ancestors) => {}
E::StabClause => {
stats.cyclomatic += 1.;
}
E::AMPAMP | E::PIPEPIPE | E::And | E::Or => {
stats.cyclomatic += 1.;
stats.cyclomatic_modified += 1.;
}
E::Call => {
if let Some(target) = node.child_by_field_name("target")
&& target.kind_id() == E::Identifier
&& let Some(name) = target.utf8_text(code)
{
match name {
"if" | "unless" | "for" | "while" => {
stats.cyclomatic += 1.;
stats.cyclomatic_modified += 1.;
}
"case" | "cond" | "with" | "try" => {
stats.cyclomatic_modified += 1.;
}
_ => {}
}
}
}
_ => {}
}
}
}