sinter-extract 0.53.0

Language-agnostic tree-sitter extraction: languages are data, the engine never forks
Documentation
use sinter_core::Relation;
use sinter_extract::{Extractor, spec_for_path};

fn refs(src: &str) -> Vec<(String, Option<String>, Relation)> {
    let spec = spec_for_path("x.rs").unwrap();
    Extractor::new(spec)
        .unwrap()
        .extract("x.rs", src)
        .unwrap()
        .references
        .into_iter()
        .map(|r| (r.name, r.path, r.relation))
        .collect()
}

#[test]
fn method_chain_segments_are_separate_calls_with_immediate_receiver() {
    let out = refs("fn f() { store.commit_hashes(&[a.clone()]).unwrap().len(); }");
    let calls: Vec<_> = out
        .iter()
        .filter(|(_, _, rel)| *rel == Relation::Calls)
        .map(|(n, p, _)| (n.as_str(), p.as_deref().unwrap()))
        .collect();
    assert_eq!(
        calls,
        vec![
            ("commit_hashes", "store.commit_hashes"),
            ("clone", "a.clone"),
            ("unwrap", "store.commit_hashes(&[a.clone()]).unwrap"),
            ("len", "store.commit_hashes(&[a.clone()]).unwrap().len"),
        ]
    );
}

#[test]
fn wildcard_and_prelude_names_emit_no_reference() {
    let out = refs(
        "fn f() -> Result<Vec<String>, u32> { let _: HashSet<_> = x; Self::High; Ok(Vec::new()) }",
    );
    let names: Vec<&str> = out.iter().map(|(n, _, _)| n.as_str()).collect();
    for noise in ["_", "Self", "Result", "Vec", "String", "u32", "Ok"] {
        assert!(!names.contains(&noise), "{noise} in {names:?}");
    }
    assert!(names.contains(&"HashSet"));
    assert!(names.contains(&"new"), "Vec::new call kept: {names:?}");
}

#[test]
fn shadowed_prelude_name_is_still_referenced() {
    let out = refs("struct Vec; fn f(v: Vec) {}");
    assert!(out.iter().any(|(n, _, _)| n == "Vec"));
}