sniff-cli 0.1.4

An exhaustive LLM-backed slop finder for codebases
Documentation
use sniff::callgraph::build_callee_context;
use sniff::parser::{parse_file_checked, parse_file_symbols_checked};
use sniff::symbol_graph::SymbolGraph;
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};

#[test]
fn resolved_callee_context_reaches_the_calling_method() {
    let nonce = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("clock before unix epoch")
        .as_nanos();
    let root = std::env::temp_dir().join(format!("sniff-callee-context-{nonce}"));
    let package = root.join("src");
    fs::create_dir_all(&package).expect("create fixture directory");
    let caller_path = package.join("caller.py");
    let callee_path = package.join("callee.py");
    fs::write(
        &caller_path,
        "from .callee import compute\n\ndef run(value):\n    return compute(value)\n",
    )
    .expect("write caller fixture");
    fs::write(&callee_path, "def compute(value):\n    return value + 1\n")
        .expect("write callee fixture");

    let caller_path = caller_path.to_string_lossy().to_string();
    let callee_path = callee_path.to_string_lossy().to_string();
    let files = vec![
        parse_file_checked(&caller_path).expect("parse caller fixture"),
        parse_file_checked(&callee_path).expect("parse callee fixture"),
    ];
    let mut graph = SymbolGraph::new(&root.to_string_lossy());
    graph.add_file(parse_file_symbols_checked(&caller_path).expect("index caller fixture"));
    graph.add_file(parse_file_symbols_checked(&callee_path).expect("index callee fixture"));
    graph.resolve_all();

    let contexts = build_callee_context(&files, &graph);
    let context = contexts
        .get(&(caller_path.clone(), "run".to_string(), 3))
        .expect("run should have a resolved callee");
    assert_eq!(context.len(), 1);
    assert_eq!(context[0].file_path, callee_path);
    assert_eq!(context[0].line, 1);
    assert!(context[0].snippet.contains("Callee Method: compute"));

    fs::remove_dir_all(root).ok();
}

#[test]
fn module_qualified_python_reference_counts_as_a_real_use() {
    let nonce = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("clock before unix epoch")
        .as_nanos();
    let root = std::env::temp_dir().join(format!("sniff-qualified-reference-{nonce}"));
    let src = root.join("src");
    let analysis = src.join("bumpkin").join("analysis");
    fs::create_dir_all(&analysis).expect("create fixture directory");
    let callee_path = analysis.join("diff_git.py");
    let caller_path = src.join("diff.py");
    fs::write(
        &callee_path,
        "def run_command(args):\n    return args\n\ndef run_git(args):\n    return args\n",
    )
    .expect("write callee fixture");
    fs::write(
        &caller_path,
        "from bumpkin.analysis import diff_git\n\ndef run_command_adapter(args):\n    return diff_git.run_command(args)\n",
    )
    .expect("write caller fixture");

    let caller_path = caller_path.to_string_lossy().to_string();
    let callee_path = callee_path.to_string_lossy().to_string();
    let mut files = vec![
        parse_file_checked(&caller_path).expect("parse caller fixture"),
        parse_file_checked(&callee_path).expect("parse callee fixture"),
    ];
    let mut graph = SymbolGraph::new(&root.to_string_lossy());
    graph.add_file(parse_file_symbols_checked(&caller_path).expect("index caller fixture"));
    graph.add_file(parse_file_symbols_checked(&callee_path).expect("index callee fixture"));
    graph.resolve_all();
    sniff::callgraph::build_references(&mut files, &graph);

    let callee = files[1]
        .methods
        .iter()
        .find(|method| method.name == "run_command")
        .expect("run_command should be parsed");
    assert_eq!(callee.real_ref_count, 1);
    assert_eq!(callee.references[0].file_path, caller_path);

    fs::remove_dir_all(root).ok();
}

#[test]
fn same_file_javascript_calls_count_as_real_references() {
    let nonce = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("clock before unix epoch")
        .as_nanos();
    let root = std::env::temp_dir().join(format!("sniff-js-local-reference-{nonce}"));
    fs::create_dir_all(&root).expect("create fixture directory");
    let path = root.join("extractor.ts");
    fs::write(
        &path,
        "function push_method(methods: unknown[], method: unknown) {\n    methods.push(method);\n}\n\nfunction visit() {\n    push_method([], {});\n}\n",
    )
    .expect("write fixture");

    let path = path.to_string_lossy().to_string();
    let mut files = vec![parse_file_checked(&path).expect("parse fixture")];
    let mut graph = SymbolGraph::new(&root.to_string_lossy());
    graph.add_file(parse_file_symbols_checked(&path).expect("index fixture"));
    graph.resolve_all();
    sniff::callgraph::build_references(&mut files, &graph);

    let helper = files[0]
        .methods
        .iter()
        .find(|method| method.name == "push_method")
        .expect("push_method should be parsed");
    assert_eq!(helper.real_ref_count, 1);
    assert!(!helper.references[0].snippet.is_empty());

    fs::remove_dir_all(root).ok();
}

#[test]
fn sniff_javascript_extractor_helper_has_resolved_local_callers() {
    let path =
        std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/parser_impl/js_ts_extractor.rs");
    let path = path.to_string_lossy().to_string();
    let mut files = vec![parse_file_checked(&path).expect("parse Sniff extractor")];
    let mut graph = SymbolGraph::new(env!("CARGO_MANIFEST_DIR"));
    graph.add_file(parse_file_symbols_checked(&path).expect("index Sniff extractor"));
    graph.resolve_all();
    sniff::callgraph::build_references(&mut files, &graph);

    let helper = files[0]
        .methods
        .iter()
        .find(|method| method.name == "push_method")
        .expect("push_method should be parsed in Sniff itself");
    assert!(
        helper.real_ref_count >= 2,
        "expected both local calls to resolve, got {}",
        helper.real_ref_count
    );
}