devscout-rs 0.9.0

Fast code index for C# and TypeScript codebases -- map, find, refs, impact from the CLI.
use super::*;

#[test]
fn two_functions_of_one_name_each_receive_only_their_own_calls() {
    let files = [
        (
            "src/lib.rs",
            "mod a;\nmod b;\nfn t() { a::glow(); b::glow(); a::glow(); }\n",
        ),
        ("src/a.rs", "pub fn glow() {}\nfn inner() { glow(); }\n"),
        ("src/b.rs", "pub fn glow() {}\n"),
    ];
    assert_eq!(
        edges_to(&files, "src/a.rs#glow"),
        vec![
            "uses-member src/lib.rs:3 -> src/a.rs#glow",
            "uses-member src/lib.rs:3 -> src/a.rs#glow",
            "uses-member src/a.rs:2 -> src/a.rs#glow",
        ]
    );
    assert_eq!(
        edges_to(&files, "src/b.rs#glow"),
        vec!["uses-member src/lib.rs:3 -> src/b.rs#glow"]
    );
}

#[test]
fn a_name_two_globs_provide_is_one_ambiguous_edge_with_both_candidates() {
    let files = [(
        "src/lib.rs",
        "mod a { pub fn glow() {} }\nmod b { pub fn glow() {} }\nuse a::*;\nuse b::*;\nfn t() { glow(); }\n",
    )];
    let graph = graph_for(&files);
    let rust: Vec<&Edge> = graph.edges.iter().collect();
    assert_eq!(rust.len(), 1);
    assert_eq!(
        serde_json::to_string(rust[0]).unwrap(),
        r#"{"kind":"ambiguous","origin":"uses-member","from_file":"src/lib.rs","from_line":5,"raw":"glow","candidates":[{"id":"src/lib.rs#a::glow","file":"src/lib.rs"},{"id":"src/lib.rs#b::glow","file":"src/lib.rs"}],"candidate_count":2}"#
    );
    assert_eq!(graph.stats.edges_by_kind.uses_member, 0);
    assert_eq!(
        graph.stats.ambiguous_count, 0,
        "the C# ambiguity counter keeps its meaning"
    );
}

#[test]
fn candidates_are_capped_and_counted_uncapped() {
    let mut src = String::new();
    for m in ["a", "b", "c", "d", "e", "f", "g"] {
        src.push_str(&format!("mod {m} {{ pub fn glow() {{}} }}\nuse {m}::*;\n"));
    }
    src.push_str("fn t() { glow(); }\n");
    let graph = graph_for(&[("src/lib.rs", src.as_str())]);
    match &graph.edges[0] {
        Edge::Ambiguous {
            candidates,
            candidate_count,
            ..
        } => {
            assert_eq!(candidates.len(), crate::resolve::AMBIGUOUS_CAP);
            assert_eq!(*candidate_count, 7);
        }
        other => panic!("expected an ambiguous edge, got {other:?}"),
    }
}

#[test]
fn a_name_nothing_provides_gives_no_edge() {
    let files = [(
        "src/lib.rs",
        "fn t() { glow(); std::mem::drop(1); Vec::new(); }\n",
    )];
    assert!(edges_for(&files).is_empty());
}

#[test]
fn a_changed_glob_requeues_every_import_that_shared_the_cached_probe() {
    let src = "mod consumer {\n    pub use crate::relay::hit as first;\n    pub use crate::relay::hit as second;\n}\nmod relay { pub use crate::sources::*; }\nmod sources { pub fn hit() {} }\nfn call() { consumer::first(); consumer::second(); }\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec![
            "uses-member src/lib.rs:7 -> src/lib.rs#sources::hit",
            "uses-member src/lib.rs:7 -> src/lib.rs#sources::hit",
        ]
    );
}

#[test]
fn a_long_pub_use_chain_and_deep_module_nesting_resolve_without_recursion() {
    let depth = 400;
    let mut chain = String::new();
    for i in 0..depth {
        chain.push_str(&format!(
            "mod m{i} {{ pub use crate::m{}::glow; }}\n",
            i + 1
        ));
    }
    chain.push_str(&format!(
        "mod m{depth} {{ pub fn glow() {{}} }}\nfn t() {{ m0::glow(); }}\n"
    ));
    let nested = format!(
        "{}pub fn deep() {{}}{}\nfn u() {{ {}deep(); }}\n",
        "pub mod n { ".repeat(depth),
        " }".repeat(depth),
        "n::".repeat(depth)
    );
    let handle = std::thread::Builder::new()
        .stack_size(256 * 1024)
        .spawn(move || {
            (
                edges_for(&[("src/lib.rs", chain.as_str())]),
                edges_for(&[("src/lib.rs", nested.as_str())]),
            )
        })
        .unwrap();
    let (chain_edges, nested_edges) = handle.join().unwrap();
    assert_eq!(
        chain_edges,
        vec![format!(
            "uses-member src/lib.rs:{} -> src/lib.rs#m{depth}::glow",
            depth + 2
        )]
    );
    assert_eq!(nested_edges.len(), 1);
    assert!(nested_edges[0].ends_with("::deep"), "{nested_edges:?}");
}