sniff-cli 0.1.0

An exhaustive LLM-backed slop finder for codebases
Documentation
use crate::config::ResolvedConfig;
use crate::languages;
use crate::report_types::StaticFlag;
use crate::roles::is_protocol_surface_module;
use crate::roles::{is_analysis_finding_support_module, is_intentional_surface_record};
use crate::types::FileRecord;
use std::path::Path;

use super::rules;
use super::tiers;

pub(super) struct MethodFlagInput<'a> {
    pub file: &'a FileRecord,
    pub config: &'a ResolvedConfig,
    pub flags: &'a mut Vec<StaticFlag>,
}

fn is_placeholder_stub_method(method: &crate::types::MethodRecord) -> bool {
    let source = method.source.trim().to_lowercase();
    if source.is_empty() || method.loc > 8 {
        return false;
    }

    let body_line_count = source
        .lines()
        .skip(1)
        .filter(|line| !line.trim().is_empty())
        .count();
    if body_line_count > 4 {
        return false;
    }

    let discards_args = source.contains("del ");
    let returns_constant = source.contains("return 0")
        || source.contains("return none")
        || source.contains("return false")
        || source.contains("return {}")
        || source.contains("return []");

    discards_args && returns_constant
}

pub(super) fn is_cfg_test_module_method(
    file: &FileRecord,
    method: &crate::types::MethodRecord,
) -> bool {
    if !file.file_path.ends_with(".rs") {
        return false;
    }

    if let (Some(cfg_test_pos), Some(method_pos)) = (
        file.source.find("#[cfg(test)]"),
        file.source.find(&method.source),
    ) {
        return method_pos > cfg_test_pos;
    }

    let Some(cfg_test_line) = file
        .source
        .lines()
        .position(|line| line.trim().starts_with("#[cfg(test)]"))
        .map(|idx| idx + 1)
    else {
        return false;
    };

    method.start_line > cfg_test_line
}

pub(super) fn collect_method_flags(input: MethodFlagInput<'_>) {
    if is_intentional_surface_record(input.file)
        || is_analysis_finding_support_module(&input.file.file_path)
    {
        return;
    }

    let path_obj = Path::new(&input.file.file_path);
    let ext = match path_obj.extension().and_then(|e| e.to_str()) {
        Some(e) => e,
        None => return,
    };
    let adapter = match languages::get_adapter(ext) {
        Some(a) => a,
        None => return,
    };

    for method in &input.file.methods {
        if is_cfg_test_module_method(input.file, method) {
            continue;
        }

        let mut reasons = rules::score_method_reasons(method, input.config, &adapter);

        if !is_protocol_surface_module(input.file) && is_placeholder_stub_method(method) {
            reasons.push("placeholder implementation".to_string());
        }

        if reasons.len() == 1 && reasons[0] == "name is vague" {
            continue;
        }

        if !reasons.is_empty() {
            let tier = tiers::tier_for_reasons(&reasons);
            input.flags.push(StaticFlag {
                flag_type: "method".to_string(),
                file_path: method.file_path.clone(),
                method_name: Some(method.name.clone()),
                reasons,
                tier,
                gate: "scorer".to_string(),
                loc: method.loc,
                start_line: method.start_line,
                end_line: method.end_line,
            });
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::ResolvedConfig;
    use crate::types::{FileRecord, MethodRecord};

    #[test]
    fn placeholder_methods_are_flagged() {
        let file = FileRecord {
            file_path: "src/demo.rs".to_string(),
            source: String::new(),
            language: "rust".to_string(),
            methods: vec![MethodRecord {
                name: "record_approval".to_string(),
                file_path: "src/demo.rs".to_string(),
                source: "def record_approval(...):\n    del approval, commit_sha\n    return 0"
                    .to_string(),
                loc: 4,
                param_count: 0,
                start_line: 1,
                end_line: 4,
                is_exported: false,
                language: "python".to_string(),
                nesting_depth: 0,
                references: Vec::new(),
                real_ref_count: 0,
            }],
        };
        let mut flags = Vec::new();
        collect_method_flags(MethodFlagInput {
            file: &file,
            config: &ResolvedConfig::default(),
            flags: &mut flags,
        });

        assert!(
            flags.iter().any(|flag| flag
                .reasons
                .iter()
                .any(|reason| reason == "placeholder implementation")),
            "expected placeholder implementation to be flagged: {flags:?}"
        );
    }

    #[test]
    fn cfg_test_module_methods_are_ignored() {
        let file = FileRecord {
            file_path: "src/demo.rs".to_string(),
            source: r#"
pub fn build() {}

#[cfg(test)]
mod tests {
    fn spawn_http_status_server() {}
}
"#
            .to_string(),
            language: "rust".to_string(),
            methods: vec![
                MethodRecord {
                    name: "build".to_string(),
                    file_path: "src/demo.rs".to_string(),
                    source: "pub fn build() {}".to_string(),
                    loc: 1,
                    param_count: 0,
                    start_line: 2,
                    end_line: 2,
                    is_exported: true,
                    language: "rust".to_string(),
                    nesting_depth: 0,
                    references: Vec::new(),
                    real_ref_count: 0,
                },
                MethodRecord {
                    name: "spawn_http_status_server".to_string(),
                    file_path: "src/demo.rs".to_string(),
                    source: "fn spawn_http_status_server() {}".to_string(),
                    loc: 1,
                    param_count: 0,
                    start_line: 6,
                    end_line: 6,
                    is_exported: false,
                    language: "rust".to_string(),
                    nesting_depth: 0,
                    references: Vec::new(),
                    real_ref_count: 0,
                },
            ],
        };

        assert!(is_cfg_test_module_method(&file, &file.methods[1]));
        assert!(!is_cfg_test_module_method(&file, &file.methods[0]));
    }
}