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
mod ai_smells;
mod duplication;
mod framework;
mod hotpath;
mod hotpath_ext;
mod maintainability;
mod mlops;
mod packaging;
mod performance;
mod quality;
mod specs;
mod structure;

pub(crate) const AI_SMELLS_BINDING_LOCATION: &str = ai_smells::BINDING_LOCATION;
pub(crate) const DUPLICATION_BINDING_LOCATION: &str = duplication::BINDING_LOCATION;
pub(crate) const FRAMEWORK_BINDING_LOCATION: &str = framework::BINDING_LOCATION;
pub(crate) const HOTPATH_BINDING_LOCATION: &str = hotpath::BINDING_LOCATION;
pub(crate) const HOTPATH_EXT_BINDING_LOCATION: &str = hotpath_ext::BINDING_LOCATION;
pub(crate) const MAINTAINABILITY_BINDING_LOCATION: &str = maintainability::BINDING_LOCATION;
pub(crate) const MLOPS_BINDING_LOCATION: &str = mlops::BINDING_LOCATION;
pub(crate) const PACKAGING_BINDING_LOCATION: &str = packaging::BINDING_LOCATION;
pub(crate) const PERFORMANCE_BINDING_LOCATION: &str = performance::BINDING_LOCATION;
pub(crate) const QUALITY_BINDING_LOCATION: &str = quality::BINDING_LOCATION;
pub(crate) const STRUCTURE_BINDING_LOCATION: &str = structure::BINDING_LOCATION;

use crate::analysis::{ParsedFile, ParsedFunction};
use crate::index::RepositoryIndex;
use crate::model::Finding;

use self::specs::{
    FILE_RULE_SPECS, FUNCTION_RULE_SPECS, REPO_RULE_SPECS, evaluate_file_specs,
    evaluate_function_specs, evaluate_repo_specs,
};

pub(super) fn is_python_package_entrypoint(file: &ParsedFile) -> bool {
    file.path.file_name().and_then(|name| name.to_str()) == Some("__init__.py")
}

pub(super) fn is_to_dict_wrapper(function: &ParsedFunction) -> bool {
    if function.fingerprint.receiver_type.is_none() || function.fingerprint.name != "to_dict" {
        return false;
    }

    let body_lines = function
        .body_text
        .lines()
        .map(str::trim)
        .filter(|line| !line.is_empty() && !line.starts_with('#'))
        .collect::<Vec<_>>();

    matches!(body_lines.as_slice(), [line] if line.starts_with("return _to_dict(self"))
}

pub(crate) fn should_skip_python_wide_contract(
    file: &ParsedFile,
    function: &ParsedFunction,
) -> bool {
    quality::should_skip_wide_contract_function(file, function)
}

pub(crate) fn should_skip_python_weak_typing(file: &ParsedFile, function: &ParsedFunction) -> bool {
    quality::should_skip_weak_typing_function(file, function)
}

pub(crate) fn python_findings(file: &ParsedFile, function: &ParsedFunction) -> Vec<Finding> {
    evaluate_function_specs(FUNCTION_RULE_SPECS, file, function)
}

pub(crate) fn python_file_findings(file: &ParsedFile) -> Vec<Finding> {
    evaluate_file_specs(FILE_RULE_SPECS, file)
}

pub(crate) fn python_repo_findings(files: &[&ParsedFile], index: &RepositoryIndex) -> Vec<Finding> {
    evaluate_repo_specs(REPO_RULE_SPECS, files, index)
}