Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/nornir-release-core/src/knowledge.rs

//! The call-graph MODEL of the release doctor's SEMANTIC-BLAST analysis.
//!
//! ## The reader itself is not here
//!
//! [`KnowledgeView`], its memoized inverted index, `callers_of` / `callees_of` /
//! `call_path`, and the two-phase snapshot-pushdown [`load_latest`] /
//! [`load_latest_audited`] loader are the ONE definition in the shared
//! `nornir-knowledge-view` edda leaf, which reasons only over the engine-agnostic
//! [`nornir_warehouse_trait::Warehouse`] seam + its POD types + std — so hosting
//! the release core here costs no `nornir` dep (PRE-PROMPT L5: consumed, never
//! twinned).
//!
//! ## What is gone (Mímir v2, 2026-10-13)
//!
//! `load_preferred_merged` and the `scip` shaping helpers — the cross-binary
//! moniker join over `scip_occurrences`, with a `symbol_facts` / `call_edges`
//! fallback — read three tables nornir no longer writes (its syn scan and its
//! `rust-analyzer scip` ingest are deleted; the code graph is the `mimir_v2`
//! lake, which the `Warehouse` seam does not carry). They are deleted with
//! their `scip` feature rather than left to answer from an empty or a stale
//! table. [`crate::semantic_blast::compute`] says so by name
//! ([`crate::semantic_blast::CallGraphNotProduced`]); a producer that holds a
//! call graph builds a [`KnowledgeView`] and calls
//! [`crate::semantic_blast::rank`].

pub use nornir_knowledge_view::{load_latest, KnowledgeView};
#[allow(unused_imports)]
pub(crate) use nornir_knowledge_view::{load_latest_audited, ScanAudit};

#[cfg(test)]
mod tests {
    use super::*;
    use nornir_warehouse_trait::CallEdgeRow;

    fn edge(callee: &str) -> CallEdgeRow {
        CallEdgeRow {
            crate_name: "demo".into(),
            caller_path: "demo::f".into(),
            callee_ident: callee.into(),
            call_kind: "call".into(),
            file: "src/lib.rs".into(),
            line: 1,
        }
    }

    fn edge_from(caller: &str, callee: &str) -> CallEdgeRow {
        CallEdgeRow {
            crate_name: "demo".into(),
            caller_path: caller.into(),
            callee_ident: callee.into(),
            call_kind: "call".into(),
            file: "src/lib.rs".into(),
            line: 1,
        }
    }

    /// The indexed `callers_of` matches every shape the reverse-lookup depends on:
    /// bare method call, path-qualified, and the `::`-boundary decoy (`new` must
    /// NOT match `renew`). This is the exact match semantics SEMANTIC-BLAST relies
    /// on to attribute a changed symbol to its call sites.
    #[test]
    fn callers_of_matches_last_segment_and_bare() {
        let view = KnowledgeView::new(
            vec![],
            vec![edge("new"), edge("Arc::new"), edge("Foo::new"), edge("renew"), edge("Foo::make")],
        );
        let hits: Vec<&str> =
            view.callers_of("new").iter().map(|c| c.callee_ident.as_str()).collect();
        let ok = hits.contains(&"new")
            && hits.contains(&"Arc::new")
            && hits.contains(&"Foo::new")
            && !hits.contains(&"renew")
            && !hits.contains(&"Foo::make")
            && hits.len() == 3
            && view.callers_of("Arc::new").iter().map(|c| c.callee_ident.as_str()).collect::<Vec<_>>()
                == vec!["Arc::new"];

        nornir_testmatrix::functional_status(
            "release-knowledge-view",
            "callers_of_matches_last_segment_and_bare",
            ok,
            &format!("hits={hits:?}"),
        );
        assert!(ok, "reverse-lookup match shapes wrong: {hits:?}");
    }

    /// The memoized-CSR `call_path` BFS walks caller → callee at identifier
    /// granularity, normalizing fully-qualified inputs to their last segment.
    #[test]
    fn call_path_bfs_over_call_edges() {
        let view = KnowledgeView::new(
            vec![],
            vec![
                edge_from("a::run", "step"),
                edge_from("b::step", "Repo::commit"),
                edge_from("a::run", "noop"),
            ],
        );
        let ok = view.call_path("run", "commit") == Some(vec!["run".into(), "step".into(), "commit".into()])
            && view.call_path("a::run", "Repo::commit")
                == Some(vec!["run".into(), "step".into(), "commit".into()])
            && view.call_path("step", "step") == Some(vec!["step".to_string()])
            && view.call_path("commit", "run").is_none()
            && view.call_path("ghost", "run").is_none();

        nornir_testmatrix::functional_status(
            "release-knowledge-view",
            "call_path_bfs_over_call_edges",
            ok,
            &format!("run->commit={:?}", view.call_path("run", "commit")),
        );
        assert!(ok, "call_path BFS wrong");
    }
}