Skip to main content

sqry_db/queries/
callees.rs

1//! `callees:X` derived query.
2//!
3//! Under the planner's relation convention (shared with the DB12 inline
4//! `relation_matches` path), `callees:X` filters a node set to those nodes
5//! whose **outgoing** `Calls` edges target an endpoint whose name matches
6//! `X`. The returned set consists of callers of an `X`-named symbol.
7//!
8//! The real computation lives in
9//! [`super::relation::compute_relation_source_set`].
10
11use std::sync::Arc;
12
13use sqry_core::graph::unified::concurrent::GraphSnapshot;
14use sqry_core::graph::unified::node::id::NodeId;
15
16use crate::QueryDb;
17use crate::query::DerivedQuery;
18
19use super::relation::{RelationKey, RelationKind, compute_relation_source_set};
20
21/// `callees:X` — filter to nodes where `X` is one of the callees.
22///
23/// # Invalidation
24///
25/// `TRACKS_EDGE_REVISION = true`: any change in the global `Calls`
26/// topology can introduce or remove callees of a given name.
27pub struct CalleesQuery;
28
29impl DerivedQuery for CalleesQuery {
30    type Key = RelationKey;
31    type Value = Arc<Vec<NodeId>>;
32    const QUERY_TYPE_ID: u32 = crate::queries::type_ids::CALLEES;
33    const TRACKS_EDGE_REVISION: bool = true;
34
35    fn execute(key: &RelationKey, _db: &QueryDb, snapshot: &GraphSnapshot) -> Arc<Vec<NodeId>> {
36        compute_relation_source_set(RelationKind::Callees, key, snapshot)
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use crate::QueryDbConfig;
44    use sqry_core::graph::unified::concurrent::CodeGraph;
45    use sqry_core::graph::unified::edge::kind::EdgeKind;
46    use sqry_core::graph::unified::node::kind::NodeKind;
47    use sqry_core::graph::unified::storage::arena::NodeEntry;
48    use std::path::Path;
49    use std::sync::Arc;
50
51    #[test]
52    fn callees_query_returns_callers_of_named_target() {
53        // main --Calls--> helper, main --Calls--> other, isolated_fn has no
54        // edges. `callees:helper` under the planner convention = {main}.
55        let mut graph = CodeGraph::new();
56        let file = graph.files_mut().register(Path::new("lib.rs")).unwrap();
57        let main_name = graph.strings_mut().intern("main").unwrap();
58        let helper_name = graph.strings_mut().intern("helper").unwrap();
59        let other_name = graph.strings_mut().intern("other").unwrap();
60        let isolated_name = graph.strings_mut().intern("isolated").unwrap();
61
62        let main_id = graph
63            .nodes_mut()
64            .alloc(
65                NodeEntry::new(NodeKind::Function, main_name, file).with_qualified_name(main_name),
66            )
67            .unwrap();
68        let helper_id = graph
69            .nodes_mut()
70            .alloc(
71                NodeEntry::new(NodeKind::Function, helper_name, file)
72                    .with_qualified_name(helper_name),
73            )
74            .unwrap();
75        let other_id = graph
76            .nodes_mut()
77            .alloc(
78                NodeEntry::new(NodeKind::Function, other_name, file)
79                    .with_qualified_name(other_name),
80            )
81            .unwrap();
82        let _ = graph
83            .nodes_mut()
84            .alloc(
85                NodeEntry::new(NodeKind::Function, isolated_name, file)
86                    .with_qualified_name(isolated_name),
87            )
88            .unwrap();
89
90        graph.edges_mut().add_edge(
91            main_id,
92            helper_id,
93            EdgeKind::Calls {
94                argument_count: 0,
95                is_async: false,
96            },
97            file,
98        );
99        graph.edges_mut().add_edge(
100            main_id,
101            other_id,
102            EdgeKind::Calls {
103                argument_count: 0,
104                is_async: false,
105            },
106            file,
107        );
108
109        let snapshot = Arc::new(graph.snapshot());
110        let mut db = QueryDb::new(Arc::clone(&snapshot), QueryDbConfig::default());
111        db.register::<CalleesQuery>();
112
113        let matches = db.get::<CalleesQuery>(&RelationKey::exact("helper"));
114        assert_eq!(matches.as_ref(), &vec![main_id]);
115    }
116}