deslop 0.2.0

A static analyzer that spots low-context and AI-assisted code patterns across naming, concurrency, security, performance, and test quality.
Documentation
use tree_sitter::Node;

pub(crate) fn has_complete_type_hints(function_node: Node<'_>, source: &str) -> bool {
    let Some(parameters_node) = function_node.child_by_field_name("parameters") else {
        return false;
    };
    let Some(function_text) = source.get(function_node.byte_range()) else {
        return false;
    };
    let Some(parameters_text) = source.get(parameters_node.byte_range()) else {
        return false;
    };

    let has_return_annotation = function_text.contains(") ->");
    let all_parameters_annotated = parameters_text
        .trim()
        .trim_start_matches('(')
        .trim_end_matches(')')
        .split(',')
        .map(str::trim)
        .filter(|entry| !entry.is_empty() && *entry != "/" && *entry != "*")
        .filter(|entry| {
            let trimmed = entry.trim_start_matches('*').trim();
            !matches!(trimmed, "self" | "cls")
        })
        .all(|entry| {
            let trimmed = entry.trim_start_matches('*').trim();
            let subject = trimmed.split('=').next().unwrap_or(trimmed).trim();
            subject.contains(':')
        });

    has_return_annotation && all_parameters_annotated
}

pub(crate) fn parameter_flags(function_node: Node<'_>, source: &str) -> (bool, bool) {
    let Some(parameters_node) = function_node.child_by_field_name("parameters") else {
        return (false, false);
    };
    let Some(parameters_text) = source.get(parameters_node.byte_range()) else {
        return (false, false);
    };

    let mut has_varargs = false;
    let mut has_kwargs = false;
    for entry in parameters_text
        .trim()
        .trim_start_matches('(')
        .trim_end_matches(')')
        .split(',')
        .map(str::trim)
        .filter(|entry| !entry.is_empty())
    {
        if entry.starts_with("**") {
            has_kwargs = true;
        } else if entry.starts_with('*') && entry != "*" {
            has_varargs = true;
        }
    }

    (has_varargs, has_kwargs)
}