devscout-rs 0.9.0

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

fn lib_with(body: &str) -> Vec<String> {
    edges_for(&[
        ("src/lib.rs", body),
        (
            "src/shelf.rs",
            "pub fn dust() {}\npub fn wipe() {}\npub mod deep { pub fn sweep() {} }\n",
        ),
    ])
}

#[test]
fn groups_nested_groups_and_self_in_a_group_bind() {
    let got = lib_with("mod shelf;\nuse shelf::{dust, deep::{sweep}, self as s};\nfn t() { dust(); sweep(); s::wipe(); }\n");
    assert_eq!(
        got,
        vec![
            "uses-member src/lib.rs:3 -> src/shelf.rs#dust",
            "uses-member src/lib.rs:3 -> src/shelf.rs#deep::sweep",
            "uses-member src/lib.rs:3 -> src/shelf.rs#wipe",
        ]
    );
    let none = lib_with("mod shelf;\nuse shelf::{dust};\nfn t() { wipe(); }\n");
    assert!(
        none.is_empty(),
        "a name the group does not import binds nothing: {none:?}"
    );
}

#[test]
fn a_glob_brings_in_every_name_and_a_private_item() {
    let got = lib_with("mod shelf;\nuse shelf::*;\nfn t() { dust(); deep::sweep(); }\n");
    assert_eq!(
        got,
        vec![
            "uses-member src/lib.rs:3 -> src/shelf.rs#dust",
            "uses-member src/lib.rs:3 -> src/shelf.rs#deep::sweep",
        ]
    );
    assert!(lib_with("mod shelf;\nuse shelf::deep::*;\nfn t() { dust(); }\n").is_empty());
}

#[test]
fn renames_bind_the_new_name_and_underscore_binds_nothing() {
    let got = lib_with("mod shelf;\nuse shelf::dust as polish;\nuse shelf::wipe as _;\nfn t() { polish(); dust(); wipe(); }\n");
    assert_eq!(got, vec!["uses-member src/lib.rs:4 -> src/shelf.rs#dust"]);
}

#[test]
fn super_chains_crate_and_self_name_modules() {
    let src = "mod a {\n    pub mod b {\n        pub fn t() { super::super::top(); super::mid(); crate::top(); self::low(); super::super::gone(); }\n        pub fn low() {}\n    }\n    pub fn mid() {}\n}\nfn top() {}\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec![
            "uses-member src/lib.rs:3 -> src/lib.rs#top",
            "uses-member src/lib.rs:3 -> src/lib.rs#a::mid",
            "uses-member src/lib.rs:3 -> src/lib.rs#top",
            "uses-member src/lib.rs:3 -> src/lib.rs#a::b::low",
        ]
    );
}

#[test]
fn the_library_name_binds_from_tests_and_main_but_not_from_build_or_orphans() {
    let files = [
        ("Cargo.toml", "[package]\nname = \"lamp-kit\"\n"),
        ("src/lib.rs", "pub fn glow() {}\n"),
        ("src/main.rs", "fn main() { lamp_kit::glow(); }\n"),
        (
            "tests/it.rs",
            "use lamp_kit::glow;\nfn t() { glow(); ::lamp_kit::glow(); }\n",
        ),
        ("build.rs", "fn main() { lamp_kit::glow(); }\n"),
        ("stray/loose.rs", "fn s() { lamp_kit::glow(); }\n"),
    ];
    assert_eq!(
        edges_for(&files),
        vec![
            "uses-member src/main.rs:1 -> src/lib.rs#glow",
            "uses-member tests/it.rs:2 -> src/lib.rs#glow",
            "uses-member tests/it.rs:2 -> src/lib.rs#glow",
        ]
    );
}

#[test]
fn a_pub_use_chain_reaches_the_defining_item_and_a_cycle_ends() {
    let src = "mod a { pub use crate::b::glow; }\nmod b { pub use crate::c::glow; }\nmod c { pub fn glow() {} }\nmod x { pub use crate::y::spin; }\nmod y { pub use crate::x::spin; }\nfn t() { a::glow(); x::spin(); }\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec!["uses-member src/lib.rs:6 -> src/lib.rs#c::glow"]
    );
}

#[test]
fn a_block_level_use_binds_inside_its_block_only() {
    let src = "mod shelf { pub fn dust() {} }\nfn t() {\n    {\n        use shelf::dust;\n        dust();\n    }\n    dust();\n}\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec!["uses-member src/lib.rs:5 -> src/lib.rs#shelf::dust"]
    );
}

#[test]
fn an_explicit_import_wins_over_a_glob_and_an_external_one_shadows_it() {
    let src = "mod a { pub fn glow() {} pub fn spin() {} }\nmod b { pub fn glow() {} }\nuse a::*;\nuse b::glow;\nuse std::thread::spin;\nfn t() { glow(); spin(); }\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec!["uses-member src/lib.rs:6 -> src/lib.rs#b::glow"]
    );
}

#[test]
fn a_leading_double_colon_names_only_an_external_crate() {
    let src = "mod shelf { pub fn dust() {} }\nfn t() { ::shelf::dust(); }\n";
    assert!(edges_for(&[("src/lib.rs", src)]).is_empty());
}

#[test]
fn a_use_declaration_gives_no_edge() {
    let src = "mod shelf { pub struct Lamp; pub fn dust() {} }\nuse shelf::{Lamp, dust};\n";
    assert!(edges_for(&[("src/lib.rs", src)]).is_empty());
}

#[test]
fn a_glob_cycle_ends_and_still_finds_a_name_one_side_declares() {
    let src = "mod a {\n    pub use crate::b::*;\n    pub fn glow() {}\n}\nmod b {\n    pub use crate::a::*;\n}\nfn t() {\n    b::glow();\n    b::missing();\n}\n";
    assert_eq!(
        edges_for(&[("src/lib.rs", src)]),
        vec!["uses-member src/lib.rs:9 -> src/lib.rs#a::glow"]
    );
}