debtmap 0.21.1

Code complexity and technical debt analyzer
Documentation
use tree_sitter::Node;

use crate::analyzers::solidity::parser::node_text;
use crate::analyzers::solidity::types::SolidityFunctionSignature;

pub(super) fn signature_from_function(node: Node, source: &str) -> SolidityFunctionSignature {
    let return_type = node.child_by_field_name("return_type");
    SolidityFunctionSignature {
        params: parameter_names(node, source, return_type),
        return_slots: return_slot_count(return_type),
    }
}

fn parameter_names(function: Node, source: &str, return_type: Option<Node>) -> Vec<String> {
    let mut names = Vec::new();
    let excluded = [function.child_by_field_name("body"), return_type]
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();
    collect_parameter_names(function, source, &excluded, &mut names);
    names
}

fn collect_parameter_names(node: Node, source: &str, excluded: &[Node], names: &mut Vec<String>) {
    if excluded.contains(&node) {
        return;
    }
    if node.kind() == "parameter"
        && let Some(name) = node.child_by_field_name("name")
    {
        names.push(node_text(&name, source).to_string());
    }
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        collect_parameter_names(child, source, excluded, names);
    }
}

fn return_slot_count(return_type: Option<Node>) -> usize {
    let Some(return_type) = return_type else {
        return 0;
    };
    let count = count_return_parameters(return_type);
    if count > 0 {
        count
    } else {
        usize::from(has_child_kind(return_type, "type_name"))
    }
}

fn count_return_parameters(node: Node) -> usize {
    let own = usize::from(matches!(node.kind(), "parameter" | "return_parameter"));
    let mut cursor = node.walk();
    own + node
        .children(&mut cursor)
        .map(count_return_parameters)
        .sum::<usize>()
}

fn has_child_kind(node: Node, kind: &str) -> bool {
    let mut cursor = node.walk();
    node.children(&mut cursor).any(|child| child.kind() == kind)
}