llmcc_core/
block_rel.rs

1use dashmap::DashMap;
2use std::collections::{HashMap, HashSet};
3
4use crate::block::{BlockId, BlockKind, BlockRelation};
5
6/// Manages relationships between blocks in a clean, type-safe way
7#[derive(Debug, Default, Clone)]
8pub struct BlockRelationMap {
9    /// BlockId -> (Relation -> Vec<BlockId>)
10    relations: DashMap<BlockId, HashMap<BlockRelation, Vec<BlockId>>>,
11}
12
13impl BlockRelationMap {
14    /// Add a relationship between two blocks
15    pub fn add_relation_impl(&self, from: BlockId, relation: BlockRelation, to: BlockId) {
16        self.relations
17            .entry(from)
18            .or_default()
19            .entry(relation)
20            .or_default()
21            .push(to);
22    }
23
24    /// Add multiple relationships of the same type from one block
25    pub fn add_relation_impls(&self, from: BlockId, relation: BlockRelation, targets: &[BlockId]) {
26        let mut entry = self.relations.entry(from).or_default();
27        let relation_vec = entry.entry(relation).or_default();
28        relation_vec.extend_from_slice(targets);
29    }
30
31    /// Remove a specific relationship
32    pub fn remove_relation_impl(
33        &self,
34        from: BlockId,
35        relation: BlockRelation,
36        to: BlockId,
37    ) -> bool {
38        let mut removed = false;
39        if let Some(mut block_relations) = self.relations.get_mut(&from) {
40            if let Some(targets) = block_relations.get_mut(&relation)
41                && let Some(pos) = targets.iter().position(|&x| x == to)
42            {
43                targets.remove(pos);
44                removed = true;
45                // Clean up empty vectors
46                if targets.is_empty() {
47                    block_relations.remove(&relation);
48                }
49            }
50            // Clean up empty maps
51            if block_relations.is_empty() {
52                drop(block_relations);
53                self.relations.remove(&from);
54            }
55        }
56        removed
57    }
58
59    /// Remove all relationships of a specific type from a block
60    pub fn remove_all_relations(&self, from: BlockId, relation: BlockRelation) -> Vec<BlockId> {
61        let mut result = Vec::new();
62        if let Some(mut block_relations) = self.relations.get_mut(&from) {
63            if let Some(targets) = block_relations.remove(&relation) {
64                result = targets;
65            }
66            // Clean up empty maps
67            if block_relations.is_empty() {
68                drop(block_relations);
69                self.relations.remove(&from);
70            }
71        }
72        result
73    }
74
75    /// Remove all relationships for a block (useful when deleting a block)
76    pub fn remove_block_relations(&self, block_id: BlockId) {
77        self.relations.remove(&block_id);
78    }
79
80    /// Get all blocks related to a given block with a specific relationship
81    pub fn get_related(&self, from: BlockId, relation: BlockRelation) -> Vec<BlockId> {
82        self.relations
83            .get(&from)
84            .and_then(|block_relations| block_relations.get(&relation).cloned())
85            .unwrap_or_default()
86    }
87
88    /// Get all relationships for a specific block
89    pub fn get_all_relations(&self, from: BlockId) -> HashMap<BlockRelation, Vec<BlockId>> {
90        self.relations
91            .get(&from)
92            .map(|r| r.clone())
93            .unwrap_or_default()
94    }
95
96    /// Check if a specific relationship exists
97    pub fn has_relation(&self, from: BlockId, relation: BlockRelation, to: BlockId) -> bool {
98        self.relations
99            .get(&from)
100            .and_then(|block_relations| block_relations.get(&relation).map(|t| t.contains(&to)))
101            .unwrap_or(false)
102    }
103
104    /// Add a relation if it doesn't already exist (optimized: single borrow)
105    pub fn add_relation_if_not_exists(&self, from: BlockId, relation: BlockRelation, to: BlockId) {
106        let mut entry = self.relations.entry(from).or_default();
107        let targets = entry.entry(relation).or_default();
108        if !targets.contains(&to) {
109            targets.push(to);
110        }
111    }
112
113    /// Add bidirectional relation if it doesn't already exist (optimized: single borrow)
114    pub fn add_bidirectional_if_not_exists(&self, caller: BlockId, callee: BlockId) {
115        // Add caller -> callee (Calls)
116        {
117            let mut caller_entry = self.relations.entry(caller).or_default();
118            let caller_targets = caller_entry.entry(BlockRelation::Calls).or_default();
119            if !caller_targets.contains(&callee) {
120                caller_targets.push(callee);
121            }
122        }
123
124        // Add callee -> caller (CalledBy)
125        {
126            let mut callee_entry = self.relations.entry(callee).or_default();
127            let callee_targets = callee_entry.entry(BlockRelation::CalledBy).or_default();
128            if !callee_targets.contains(&caller) {
129                callee_targets.push(caller);
130            }
131        }
132    }
133
134    /// Check if any relationship of a type exists
135    pub fn has_relation_type(&self, from: BlockId, relation: BlockRelation) -> bool {
136        self.relations
137            .get(&from)
138            .and_then(|block_relations| block_relations.get(&relation).map(|t| !t.is_empty()))
139            .unwrap_or(false)
140    }
141
142    /// Get all blocks that have any relationships
143    pub fn get_connected_blocks(&self) -> Vec<BlockId> {
144        self.relations.iter().map(|r| *r.key()).collect()
145    }
146
147    /// Get all blocks related to a given block (regardless of relationship type)
148    pub fn get_all_related_blocks(&self, from: BlockId) -> HashSet<BlockId> {
149        let mut result = HashSet::new();
150        if let Some(block_relations) = self.relations.get(&from) {
151            for targets in block_relations.values() {
152                result.extend(targets.iter().copied());
153            }
154        }
155        result
156    }
157
158    /// Find all blocks that point to a given block with a specific relationship
159    pub fn find_reverse_relations(&self, to: BlockId, relation: BlockRelation) -> Vec<BlockId> {
160        let mut result = Vec::new();
161        for entry in self.relations.iter() {
162            let from_block = *entry.key();
163            let block_relations = entry.value();
164            if let Some(targets) = block_relations.get(&relation)
165                && targets.contains(&to)
166            {
167                result.push(from_block);
168            }
169        }
170        result
171    }
172
173    /// Get statistics about relationships
174    pub fn stats(&self) -> RelationStats {
175        let mut total_relations = 0;
176        let mut by_relation: HashMap<BlockRelation, usize> = HashMap::new();
177
178        for entry in self.relations.iter() {
179            let block_relations = entry.value();
180            for (&relation, targets) in block_relations.iter() {
181                by_relation
182                    .entry(relation)
183                    .and_modify(|count| *count += targets.len())
184                    .or_insert_with(|| targets.len());
185                total_relations += targets.len();
186            }
187        }
188
189        RelationStats {
190            total_blocks: self.relations.len(),
191            total_relations,
192            by_relation,
193        }
194    }
195
196    /// Clear all relationships
197    pub fn clear(&self) {
198        self.relations.clear();
199    }
200
201    /// Check if the map is empty
202    pub fn is_empty(&self) -> bool {
203        self.relations.is_empty()
204    }
205
206    /// Get the number of blocks with relationships
207    pub fn len(&self) -> usize {
208        self.relations.len()
209    }
210}
211
212/// Helper struct for building relationships fluently
213pub struct RelationBuilder<'a> {
214    map: &'a BlockRelationMap,
215    from: BlockId,
216}
217
218impl<'a> RelationBuilder<'a> {
219    fn new(map: &'a BlockRelationMap, from: BlockId) -> Self {
220        Self { map, from }
221    }
222
223    /// Add a "calls" relationship
224    pub fn calls(self, to: BlockId) -> Self {
225        self.map
226            .add_relation_impl(self.from, BlockRelation::Calls, to);
227        self
228    }
229
230    /// Add a "called by" relationship
231    pub fn called_by(self, to: BlockId) -> Self {
232        self.map
233            .add_relation_impl(self.from, BlockRelation::CalledBy, to);
234        self
235    }
236
237    /// Add a "contains" relationship
238    pub fn contains(self, to: BlockId) -> Self {
239        self.map
240            .add_relation_impl(self.from, BlockRelation::Contains, to);
241        self
242    }
243
244    /// Add a "contained by" relationship
245    pub fn contained_by(self, to: BlockId) -> Self {
246        self.map
247            .add_relation_impl(self.from, BlockRelation::ContainedBy, to);
248        self
249    }
250
251    /// Add a custom relationship
252    pub fn relation(self, relation: BlockRelation, to: BlockId) -> Self {
253        self.map.add_relation_impl(self.from, relation, to);
254        self
255    }
256
257    /// Add multiple relationships of the same type
258    pub fn relations(self, relation: BlockRelation, targets: &[BlockId]) -> Self {
259        self.map.add_relation_impls(self.from, relation, targets);
260        self
261    }
262}
263
264impl BlockRelationMap {
265    /// Create a fluent builder for adding relationships from a block
266    pub fn from_block(&self, from: BlockId) -> RelationBuilder<'_> {
267        RelationBuilder::new(self, from)
268    }
269}
270
271/// Statistics about relationships in the map
272#[derive(Debug, Default, Clone)]
273pub struct RelationStats {
274    pub total_blocks: usize,
275    pub total_relations: usize,
276    pub by_relation: HashMap<BlockRelation, usize>,
277}
278
279impl std::fmt::Display for RelationStats {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        writeln!(f, "Relation Stats:")?;
282        writeln!(f, "  Total blocks with relations: {}", self.total_blocks)?;
283        writeln!(f, "  Total relationships: {}", self.total_relations)?;
284        writeln!(f, "  By type:")?;
285        for (&relation, &count) in &self.by_relation {
286            writeln!(f, "    {relation}: {count}")?;
287        }
288        Ok(())
289    }
290}
291
292// Convenience functions for common relationship patterns
293impl BlockRelationMap {
294    /// Create a bidirectional call relationship
295    pub fn add_call_relation(&self, caller: BlockId, callee: BlockId) {
296        self.add_relation_impl(caller, BlockRelation::Calls, callee);
297        self.add_relation_impl(callee, BlockRelation::CalledBy, caller);
298    }
299
300    /// Remove a bidirectional call relationship
301    pub fn remove_call_relation(&self, caller: BlockId, callee: BlockId) {
302        self.remove_relation_impl(caller, BlockRelation::Calls, callee);
303        self.remove_relation_impl(callee, BlockRelation::CalledBy, caller);
304    }
305
306    pub fn get_callers(&self, block: BlockId) -> Vec<BlockId> {
307        self.get_related(block, BlockRelation::CalledBy)
308    }
309
310    pub fn get_callees(&self, block: BlockId) -> Vec<BlockId> {
311        self.get_related(block, BlockRelation::Calls)
312    }
313
314    /// Get all children of a block
315    pub fn get_children(&self, block: BlockId) -> Vec<BlockId> {
316        self.get_related(block, BlockRelation::Unknown)
317    }
318
319    /// Get the parent of a block (assumes single parent)
320    pub fn get_parent(&self, block: BlockId) -> Option<BlockId> {
321        self.find_reverse_relations(block, BlockRelation::Unknown)
322            .into_iter()
323            .next()
324    }
325
326    /// Get all ancestors of a block (walking up the containment hierarchy)
327    pub fn get_ancestors(&self, mut block: BlockId) -> Vec<BlockId> {
328        let mut ancestors = Vec::new();
329        let mut visited = HashSet::new();
330
331        while let Some(parent) = self.get_parent(block) {
332            if visited.contains(&parent) {
333                // Cycle detection
334                break;
335            }
336            visited.insert(parent);
337            ancestors.push(parent);
338            block = parent;
339        }
340
341        ancestors
342    }
343
344    /// Get all descendants of a block (walking down the containment hierarchy)
345    pub fn get_descendants(&self, block: BlockId) -> Vec<BlockId> {
346        let mut descendants = Vec::new();
347        let mut visited = HashSet::new();
348        let mut queue = vec![block];
349
350        while let Some(current) = queue.pop() {
351            if visited.contains(&current) {
352                continue;
353            }
354            visited.insert(current);
355
356            let children = self.get_children(current);
357            descendants.extend(&children);
358            queue.extend(children);
359        }
360
361        descendants
362    }
363}
364
365/// BlockIndexMaps provides efficient lookup of blocks by various indices.
366///
367/// Best practices for usage:
368/// - block_name_index: Use when you want to find blocks by name (multiple blocks can share the same name)
369/// - unit_index_map: Use when you want all blocks in a specific unit
370/// - block_kind_index: Use when you want all blocks of a specific kind (e.g., all functions)
371/// - block_id_index: Use for O(1) lookup of block metadata by BlockId
372///
373/// Important: The "name" field is optional since Root blocks and some other blocks may not have names.
374///
375/// Rationale for data structure choices:
376/// - DashMap is used for all indexes to allow concurrent access during parallel graph building
377/// - Vec is used for values to handle multiple blocks with the same index (same name/kind/unit)
378pub struct BlockIndexMaps {
379    /// block_name -> Vec<(unit_index, block_kind, block_id)>
380    /// Multiple blocks can share the same name across units or within the same unit
381    block_name_index: DashMap<String, Vec<(usize, BlockKind, BlockId)>>,
382
383    /// unit_index -> Vec<(block_name, block_kind, block_id)>
384    /// Allows retrieval of all blocks in a specific compilation unit
385    unit_index_map: DashMap<usize, Vec<(Option<String>, BlockKind, BlockId)>>,
386
387    /// block_kind -> Vec<(unit_index, block_name, block_id)>
388    /// Allows retrieval of all blocks of a specific kind across all units
389    block_kind_index: DashMap<BlockKind, Vec<(usize, Option<String>, BlockId)>>,
390
391    /// block_id -> (unit_index, block_name, block_kind)
392    /// Direct O(1) lookup of block metadata by ID
393    block_id_index: DashMap<BlockId, (usize, Option<String>, BlockKind)>,
394}
395
396impl Default for BlockIndexMaps {
397    fn default() -> Self {
398        Self::new()
399    }
400}
401
402impl BlockIndexMaps {
403    /// Create a new empty BlockIndexMaps
404    pub fn new() -> Self {
405        Self {
406            block_name_index: DashMap::new(),
407            unit_index_map: DashMap::new(),
408            block_kind_index: DashMap::new(),
409            block_id_index: DashMap::new(),
410        }
411    }
412
413    /// Register a new block in all indexes
414    ///
415    /// # Arguments
416    /// - `block_id`: The unique block identifier
417    /// - `block_name`: Optional name of the block (None for unnamed blocks)
418    /// - `block_kind`: The kind of block (Func, Class, Stmt, etc.)
419    /// - `unit_index`: The compilation unit index this block belongs to
420    pub fn insert_block(
421        &self,
422        block_id: BlockId,
423        block_name: Option<String>,
424        block_kind: BlockKind,
425        unit_index: usize,
426    ) {
427        // Insert into block_id_index for O(1) lookups
428        self.block_id_index
429            .insert(block_id, (unit_index, block_name.clone(), block_kind));
430
431        // Insert into block_name_index (if name exists)
432        if let Some(ref name) = block_name {
433            self.block_name_index
434                .entry(name.clone())
435                .or_default()
436                .push((unit_index, block_kind, block_id));
437        }
438
439        // Insert into unit_index_map
440        self.unit_index_map.entry(unit_index).or_default().push((
441            block_name.clone(),
442            block_kind,
443            block_id,
444        ));
445
446        // Insert into block_kind_index
447        self.block_kind_index
448            .entry(block_kind)
449            .or_default()
450            .push((unit_index, block_name, block_id));
451    }
452
453    /// Find all blocks with a given name (may return multiple blocks)
454    ///
455    /// Returns a vector of (unit_index, block_kind, block_id) tuples
456    pub fn find_by_name(&self, name: &str) -> Vec<(usize, BlockKind, BlockId)> {
457        self.block_name_index
458            .get(name)
459            .map(|v| v.clone())
460            .unwrap_or_default()
461    }
462
463    /// Find all blocks in a specific unit
464    ///
465    /// Returns a vector of (block_name, block_kind, block_id) tuples
466    pub fn find_by_unit(&self, unit_index: usize) -> Vec<(Option<String>, BlockKind, BlockId)> {
467        self.unit_index_map
468            .get(&unit_index)
469            .map(|v| v.clone())
470            .unwrap_or_default()
471    }
472
473    /// Find all blocks of a specific kind across all units
474    ///
475    /// Returns a vector of (unit_index, block_name, block_id) tuples
476    pub fn find_by_kind(&self, block_kind: BlockKind) -> Vec<(usize, Option<String>, BlockId)> {
477        self.block_kind_index
478            .get(&block_kind)
479            .map(|v| v.clone())
480            .unwrap_or_default()
481    }
482
483    /// Find all blocks of a specific kind in a specific unit
484    ///
485    /// Returns a vector of block_ids
486    pub fn find_by_kind_and_unit(&self, block_kind: BlockKind, unit_index: usize) -> Vec<BlockId> {
487        let by_kind = self.find_by_kind(block_kind);
488        by_kind
489            .into_iter()
490            .filter(|(unit, _, _)| *unit == unit_index)
491            .map(|(_, _, block_id)| block_id)
492            .collect()
493    }
494
495    /// Look up block metadata by BlockId for O(1) access
496    ///
497    /// Returns (unit_index, block_name, block_kind) if found
498    pub fn get_block_info(&self, block_id: BlockId) -> Option<(usize, Option<String>, BlockKind)> {
499        self.block_id_index.get(&block_id).map(|v| v.clone())
500    }
501
502    /// Get total number of blocks indexed
503    pub fn block_count(&self) -> usize {
504        self.block_id_index.len()
505    }
506
507    /// Get the number of unique block names
508    pub fn unique_names_count(&self) -> usize {
509        self.block_name_index.len()
510    }
511
512    /// Check if a block with the given ID exists
513    pub fn contains_block(&self, block_id: BlockId) -> bool {
514        self.block_id_index.contains_key(&block_id)
515    }
516
517    /// Get an iterator over all blocks with their metadata
518    /// Returns (block_id, unit_index, block_name, block_kind)
519    pub fn iter_all_blocks(&self) -> Vec<(BlockId, usize, Option<String>, BlockKind)> {
520        self.block_id_index
521            .iter()
522            .map(|entry| {
523                let block_id = *entry.key();
524                let (unit_index, name, kind) = entry.value();
525                (block_id, *unit_index, name.clone(), *kind)
526            })
527            .collect()
528    }
529
530    /// Clear all indexes
531    pub fn clear(&self) {
532        self.block_name_index.clear();
533        self.unit_index_map.clear();
534        self.block_kind_index.clear();
535        self.block_id_index.clear();
536    }
537}