Skip to main content

ryo_analysis/query/
typeflow_v2.rs

1//! TypeFlowGraph V2 - Data-Oriented Design implementation
2//!
3//! High-performance type relationship tracking using:
4//! - SoA (Structure of Arrays) for cache efficiency
5//! - Inverted indices for O(1) impact analysis
6//! - Span-free, String-free design following PureAST philosophy
7//!
8//! # Architecture
9//!
10//! ```text
11//! TypeFlowGraphV2
12//! ├── Data Storage (SoA)
13//! │   ├── definitions: Vec<DefinitionData>
14//! │   ├── usages: Vec<UsageData>
15//! │   ├── generic_args: Vec<GenericArgData>
16//! │   └── trait_bounds: Vec<TraitBoundData>
17//! │
18//! ├── Edge Relations (Side Tables)
19//! │   ├── usage_to_args: Vec<SmallVec<[u32; 4]>>
20//! │   └── node_to_bounds: FxHashMap<TypeNodeId, SmallVec<[u32; 2]>>
21//! │
22//! └── Lookup Tables (Inverted Index)
23//!     ├── symbol_to_def: SecondaryMap<SymbolId, u32>
24//!     ├── symbol_to_usages: FxHashMap<SymbolId, Vec<u32>>
25//!     ├── trait_to_bounds: FxHashMap<SymbolId, Vec<u32>>
26//!     └── container_fields: FxHashMap<SymbolId, Vec<SymbolId>>
27//! ```
28
29use crate::symbol::SymbolId;
30use serde::{Deserialize, Serialize};
31use slotmap::SecondaryMap;
32use smallvec::SmallVec;
33use std::collections::{HashMap, HashSet, VecDeque};
34
35use super::graph_v2::{ChainDirection, ChainNode, ChainResult};
36
37/// Reference kind for type usage.
38///
39/// Tracks ownership state: `T` vs `&T` vs `&mut T`.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
41pub enum RefKind {
42    /// Owned value: `T`
43    #[default]
44    Owned,
45    /// Shared reference: `&T`
46    Ref,
47    /// Mutable reference: `&mut T`
48    RefMut,
49}
50
51/// Kind of type definition.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub enum TypeDefKind {
54    /// Struct definition.
55    Struct,
56    /// Enum definition.
57    Enum,
58    /// Trait definition.
59    Trait,
60    /// Type alias (type A = B).
61    TypeAlias,
62    /// Generic parameter definition (T in `fn foo<T>`).
63    GenericParam,
64    /// Associated type (type Output in trait).
65    AssociatedType,
66}
67
68/// Context in which a type is used.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
70pub enum UsageContext {
71    /// Struct field type.
72    FieldType,
73    /// Function parameter type.
74    ParamType,
75    /// Return type.
76    ReturnType,
77    /// Local variable type annotation.
78    LocalVar,
79    /// Where clause or trait bound.
80    WhereBound,
81    /// Impl target (impl Foo).
82    ImplTarget,
83    /// Impl trait (impl Trait for Foo).
84    ImplTrait,
85    /// Use statement.
86    UseStatement,
87    /// Type cast (as Type).
88    Cast,
89    /// Turbofish (`::<Type>`).
90    Turbofish,
91    /// Enum variant field.
92    VariantField,
93    /// Const/static type.
94    ConstType,
95    /// Match expression scrutinee (the value being matched).
96    /// Used to track where enums are matched for cascade analysis.
97    MatchScrutinee,
98    /// Associated function call: `Foo::new()`, `Foo::default()`, etc.
99    /// Tracks the type referenced by the path prefix of the call.
100    ConstructorCall,
101    /// Struct literal: `Foo { field: value }`.
102    /// Tracks the type being instantiated.
103    StructLiteral,
104}
105
106// ============================================================================
107// Node ID Types
108// ============================================================================
109
110/// Unified node ID with kind encoded in top 2 bits.
111/// Allows a single ID type to reference any node kind.
112#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
113#[repr(transparent)]
114pub struct TypeNodeId(u32);
115
116impl TypeNodeId {
117    const KIND_SHIFT: u32 = 30;
118    const KIND_MASK: u32 = 0xC000_0000;
119    const INDEX_MASK: u32 = 0x3FFF_FFFF;
120
121    const KIND_DEF: u32 = 0;
122    const KIND_USAGE: u32 = 1;
123    const KIND_ARG: u32 = 2;
124    const KIND_BOUND: u32 = 3;
125
126    /// Create a definition node ID.
127    #[inline]
128    pub const fn def(idx: u32) -> Self {
129        Self((Self::KIND_DEF << Self::KIND_SHIFT) | idx)
130    }
131
132    /// Create a usage node ID.
133    #[inline]
134    pub const fn usage(idx: u32) -> Self {
135        Self((Self::KIND_USAGE << Self::KIND_SHIFT) | idx)
136    }
137
138    /// Create a generic arg node ID.
139    #[inline]
140    pub const fn arg(idx: u32) -> Self {
141        Self((Self::KIND_ARG << Self::KIND_SHIFT) | idx)
142    }
143
144    /// Create a trait bound node ID.
145    #[inline]
146    pub const fn bound(idx: u32) -> Self {
147        Self((Self::KIND_BOUND << Self::KIND_SHIFT) | idx)
148    }
149
150    /// Get the node kind.
151    #[inline]
152    pub const fn kind(self) -> NodeKind {
153        match (self.0 & Self::KIND_MASK) >> Self::KIND_SHIFT {
154            Self::KIND_DEF => NodeKind::Definition,
155            Self::KIND_USAGE => NodeKind::Usage,
156            Self::KIND_ARG => NodeKind::GenericArg,
157            Self::KIND_BOUND => NodeKind::TraitBound,
158            _ => unreachable!(),
159        }
160    }
161
162    /// Get the raw index within the kind's array.
163    #[inline]
164    pub const fn index(self) -> u32 {
165        self.0 & Self::INDEX_MASK
166    }
167
168    /// Get raw u32 value.
169    #[inline]
170    pub const fn as_u32(self) -> u32 {
171        self.0
172    }
173}
174
175/// Node kind discriminant.
176#[derive(Copy, Clone, Eq, PartialEq, Debug)]
177pub enum NodeKind {
178    /// Type definition site (where the type is declared).
179    Definition,
180    /// Concrete usage of a type at a value position.
181    Usage,
182    /// Generic argument position (e.g. `T` in `Vec<T>`).
183    GenericArg,
184    /// Trait-bound position (e.g. `T: Trait`).
185    TraitBound,
186}
187
188// ============================================================================
189// Data Structures (SoA Components)
190// ============================================================================
191
192/// Definition node data (16 bytes).
193#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
194pub struct DefinitionData {
195    /// Symbol ID of the defined type.
196    pub symbol_id: SymbolId,
197    /// Kind of type definition.
198    pub kind: TypeDefKind,
199}
200
201/// Usage node data - Span-free, String-free.
202#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
203pub struct UsageData {
204    /// Resolved type definition (if available).
205    pub resolved: Option<SymbolId>,
206    /// Context in which the type is used.
207    pub context: UsageContext,
208    /// Reference kind (owned, ref, ref mut).
209    pub ref_kind: RefKind,
210    /// Container symbol that owns this usage (e.g., the function or struct).
211    ///
212    /// For `UsageContext::ParamType` / `ReturnType`, this is the function's SymbolId.
213    /// For `UsageContext::FieldType`, this is the struct/enum's SymbolId.
214    pub container: Option<SymbolId>,
215}
216
217/// Generic argument node data (16 bytes).
218#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
219pub struct GenericArgData {
220    /// Parent usage's index.
221    pub parent_usage: u32,
222    /// Argument index (0, 1, 2...).
223    pub arg_index: u8,
224    /// Resolved type (if concrete).
225    pub resolved: Option<SymbolId>,
226}
227
228/// Trait bound node data (16 bytes).
229#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
230pub struct TraitBoundData {
231    /// Target node being bounded.
232    pub target: TypeNodeId,
233    /// Trait's SymbolId (if resolved).
234    pub trait_id: Option<SymbolId>,
235}
236
237// ============================================================================
238// Lookup Table (Inverted Index)
239// ============================================================================
240
241/// Inverted indices for O(1) lookups.
242#[derive(Clone, Default, Debug, Serialize, Deserialize)]
243pub struct LookupTable {
244    /// SymbolId → definition index (1:1).
245    pub symbol_to_def: SecondaryMap<SymbolId, u32>,
246
247    /// Resolved type SymbolId → usage indices (1:N).
248    /// Reverse lookup: "what usages reference this type?"
249    pub symbol_to_usages: HashMap<SymbolId, Vec<u32>>,
250
251    /// Container SymbolId → usage indices (1:N).
252    /// Forward lookup: "what types does this container use?"
253    pub container_to_usages: HashMap<SymbolId, Vec<u32>>,
254
255    /// Trait SymbolId → bound indices (1:N).
256    pub trait_to_bounds: HashMap<SymbolId, Vec<u32>>,
257
258    /// Container SymbolId → field type SymbolIds (Contains relation).
259    pub container_fields: HashMap<SymbolId, Vec<SymbolId>>,
260}
261
262// ============================================================================
263// TypeFlowGraph V2
264// ============================================================================
265
266/// Type relationship graph (V2 - Data-Oriented Design).
267///
268/// NOTE: Deserialize is NOT derived because TypeFlowGraphV2 contains
269/// SecondaryMap<SymbolId, ...> and HashMap<SymbolId, ...>.
270/// SymbolId is process-specific. Serialize is kept for debugging/inspection.
271#[derive(Clone, Default, Debug, Serialize)]
272pub struct TypeFlowGraphV2 {
273    // === Data Storage (SoA) ===
274    /// Definition nodes.
275    pub definitions: Vec<DefinitionData>,
276
277    /// Usage nodes.
278    pub usages: Vec<UsageData>,
279
280    /// Generic argument nodes.
281    pub generic_args: Vec<GenericArgData>,
282
283    /// Trait bound nodes.
284    pub trait_bounds: Vec<TraitBoundData>,
285
286    // === Edge Relations (Side Tables) ===
287    /// Usage → generic args (1:N).
288    pub usage_to_args: Vec<SmallVec<[u32; 4]>>,
289
290    /// Node → trait bounds (1:N).
291    pub node_to_bounds: HashMap<TypeNodeId, SmallVec<[u32; 2]>>,
292
293    // === Lookup Tables ===
294    /// Reverse-lookup tables (`SymbolId` → node indices, etc.) used for
295    /// fast querying.
296    pub lookup: LookupTable,
297}
298
299/// Impact of changing a type.
300#[derive(Debug, Clone, Default)]
301pub struct TypeImpactV2 {
302    /// Direct usage indices.
303    pub direct_usages: Vec<u32>,
304    /// Trait bound indices.
305    pub bound_usages: Vec<u32>,
306    /// Types that contain this type as a field.
307    pub containing_types: Vec<SymbolId>,
308    /// Generic usage indices.
309    pub generic_usages: Vec<u32>,
310}
311
312impl TypeFlowGraphV2 {
313    /// Create a new empty graph.
314    pub fn new() -> Self {
315        Self::default()
316    }
317
318    /// Create with pre-allocated capacity.
319    pub fn with_capacity(defs: usize, usages: usize) -> Self {
320        Self {
321            definitions: Vec::with_capacity(defs),
322            usages: Vec::with_capacity(usages),
323            generic_args: Vec::with_capacity(usages), // Usually similar count
324            trait_bounds: Vec::new(),
325            usage_to_args: Vec::with_capacity(usages),
326            node_to_bounds: HashMap::new(),
327            lookup: LookupTable::default(),
328        }
329    }
330
331    // ========================================================================
332    // Node Management
333    // ========================================================================
334
335    /// Add a type definition node.
336    pub fn add_definition(&mut self, symbol_id: SymbolId, kind: TypeDefKind) -> TypeNodeId {
337        // Return existing if already registered
338        if let Some(&idx) = self.lookup.symbol_to_def.get(symbol_id) {
339            return TypeNodeId::def(idx);
340        }
341
342        let idx = self.definitions.len() as u32;
343        self.definitions.push(DefinitionData { symbol_id, kind });
344        self.lookup.symbol_to_def.insert(symbol_id, idx);
345
346        TypeNodeId::def(idx)
347    }
348
349    /// Add a type usage node.
350    pub fn add_usage(
351        &mut self,
352        context: UsageContext,
353        ref_kind: RefKind,
354        resolved: Option<SymbolId>,
355        container: Option<SymbolId>,
356    ) -> TypeNodeId {
357        let idx = self.usages.len() as u32;
358        self.usages.push(UsageData {
359            resolved,
360            context,
361            ref_kind,
362            container,
363        });
364
365        // Initialize empty args list
366        self.usage_to_args.push(SmallVec::new());
367
368        // Register in reverse lookup (type → usages)
369        if let Some(symbol_id) = resolved {
370            self.lookup
371                .symbol_to_usages
372                .entry(symbol_id)
373                .or_default()
374                .push(idx);
375        }
376
377        // Register in forward lookup (container → usages)
378        if let Some(cid) = container {
379            self.lookup
380                .container_to_usages
381                .entry(cid)
382                .or_default()
383                .push(idx);
384        }
385
386        TypeNodeId::usage(idx)
387    }
388
389    /// Add a generic argument node.
390    pub fn add_generic_arg(
391        &mut self,
392        parent: TypeNodeId,
393        arg_index: u8,
394        resolved: Option<SymbolId>,
395    ) -> TypeNodeId {
396        let idx = self.generic_args.len() as u32;
397        self.generic_args.push(GenericArgData {
398            parent_usage: parent.index(),
399            arg_index,
400            resolved,
401        });
402
403        // Link to parent usage
404        if parent.kind() == NodeKind::Usage {
405            if let Some(args) = self.usage_to_args.get_mut(parent.index() as usize) {
406                args.push(idx);
407            }
408
409            // Register in reverse lookup (type → usages) so type_users() can find
410            // containers that reference this type via generic arguments (e.g., Vec<Foo>).
411            if let Some(symbol_id) = resolved {
412                self.lookup
413                    .symbol_to_usages
414                    .entry(symbol_id)
415                    .or_default()
416                    .push(parent.index());
417            }
418        }
419
420        TypeNodeId::arg(idx)
421    }
422
423    /// Add a trait bound node.
424    pub fn add_trait_bound(
425        &mut self,
426        target: TypeNodeId,
427        trait_id: Option<SymbolId>,
428    ) -> TypeNodeId {
429        let idx = self.trait_bounds.len() as u32;
430        self.trait_bounds.push(TraitBoundData { target, trait_id });
431
432        // Link to target node
433        self.node_to_bounds.entry(target).or_default().push(idx);
434
435        // Register in lookup table
436        if let Some(tid) = trait_id {
437            self.lookup
438                .trait_to_bounds
439                .entry(tid)
440                .or_default()
441                .push(idx);
442        }
443
444        TypeNodeId::bound(idx)
445    }
446
447    /// Register a container-field relationship.
448    pub fn add_contains(&mut self, container: SymbolId, field_type: SymbolId) {
449        self.lookup
450            .container_fields
451            .entry(container)
452            .or_default()
453            .push(field_type);
454    }
455
456    // ========================================================================
457    // Query Methods
458    // ========================================================================
459
460    /// Get the definition node for a symbol (O(1)).
461    #[inline]
462    pub fn definition(&self, id: SymbolId) -> Option<TypeNodeId> {
463        self.lookup
464            .symbol_to_def
465            .get(id)
466            .map(|&idx| TypeNodeId::def(idx))
467    }
468
469    /// Get all usage indices for a type (O(1)).
470    #[inline]
471    pub fn usages(&self, id: SymbolId) -> impl Iterator<Item = TypeNodeId> + '_ {
472        self.lookup
473            .symbol_to_usages
474            .get(&id)
475            .into_iter()
476            .flat_map(|v| v.iter().map(|&idx| TypeNodeId::usage(idx)))
477    }
478
479    /// Get the number of usages for a type (O(1)).
480    #[inline]
481    pub fn usage_count(&self, id: SymbolId) -> usize {
482        self.lookup.symbol_to_usages.get(&id).map_or(0, |v| v.len())
483    }
484
485    /// Forward lookup: what types does this container use? (O(1) + result count)
486    ///
487    /// Returns resolved SymbolIds of types referenced by the container.
488    /// Includes both directly resolved types and types referenced via generic arguments
489    /// (e.g., `Foo` in `Vec<Foo>`).
490    pub fn types_used_by(&self, container: SymbolId) -> impl Iterator<Item = SymbolId> + '_ {
491        self.lookup
492            .container_to_usages
493            .get(&container)
494            .into_iter()
495            .flat_map(|indices| {
496                indices.iter().flat_map(|&idx| {
497                    let mut types = SmallVec::<[SymbolId; 4]>::new();
498                    // Direct type reference (e.g., field: Foo)
499                    if let Some(u) = self.usages.get(idx as usize) {
500                        if let Some(resolved) = u.resolved {
501                            types.push(resolved);
502                        }
503                    }
504                    // Generic argument types (e.g., Foo in Vec<Foo>)
505                    if let Some(args) = self.usage_to_args.get(idx as usize) {
506                        for &arg_idx in args.iter() {
507                            if let Some(arg) = self.generic_args.get(arg_idx as usize) {
508                                if let Some(resolved) = arg.resolved {
509                                    types.push(resolved);
510                                }
511                            }
512                        }
513                    }
514                    types.into_iter()
515                })
516            })
517    }
518
519    /// Reverse lookup: what containers use this type? (O(1) + result count)
520    ///
521    /// Returns SymbolIds of containers that reference the given type.
522    pub fn type_users(&self, type_id: SymbolId) -> impl Iterator<Item = SymbolId> + '_ {
523        self.lookup
524            .symbol_to_usages
525            .get(&type_id)
526            .into_iter()
527            .flat_map(|indices| {
528                indices
529                    .iter()
530                    .filter_map(|&idx| self.usages.get(idx as usize).and_then(|u| u.container))
531            })
532    }
533
534    // ========================================================================
535    // Chain traversal (BFS)
536    // ========================================================================
537
538    /// BFS traversal: find all containers that use this type, transitively.
539    ///
540    /// "Type A is used by container B; B (as a type) is used by container C" etc.
541    pub fn type_users_chain(&self, start: SymbolId, max_depth: usize) -> Vec<ChainNode> {
542        self.traverse_type_chain(start, max_depth, ChainDirection::TypeUsers)
543    }
544
545    /// BFS traversal: find all types used by this container, transitively.
546    ///
547    /// "Container A uses type B; B (as a container) uses type C" etc.
548    pub fn types_used_by_chain(&self, start: SymbolId, max_depth: usize) -> Vec<ChainNode> {
549        self.traverse_type_chain(start, max_depth, ChainDirection::TypeDeps)
550    }
551
552    /// Full chain analysis with statistics.
553    ///
554    /// Returns `ChainResult` compatible with CodeGraphV2's `analyze_chain()`.
555    pub fn analyze_type_chain(
556        &self,
557        start: SymbolId,
558        max_depth: usize,
559        direction: ChainDirection,
560    ) -> ChainResult {
561        let nodes = self.traverse_type_chain(start, max_depth, direction);
562
563        let mut by_depth: HashMap<usize, usize> = HashMap::new();
564        for node in &nodes {
565            *by_depth.entry(node.depth).or_default() += 1;
566        }
567
568        let max_actual_depth = nodes.iter().map(|n| n.depth).max().unwrap_or(0);
569
570        ChainResult {
571            start,
572            direction,
573            max_depth,
574            nodes,
575            max_actual_depth,
576            by_depth,
577        }
578    }
579
580    /// Internal BFS implementation for type chain traversal.
581    fn traverse_type_chain(
582        &self,
583        start: SymbolId,
584        max_depth: usize,
585        direction: ChainDirection,
586    ) -> Vec<ChainNode> {
587        let mut result = Vec::new();
588        let mut visited = HashSet::new();
589        let mut queue = VecDeque::new();
590
591        visited.insert(start);
592        queue.push_back((start, 0usize));
593
594        while let Some((current, depth)) = queue.pop_front() {
595            if depth > 0 {
596                result.push(ChainNode {
597                    symbol: current,
598                    depth,
599                });
600            }
601
602            if depth >= max_depth {
603                continue;
604            }
605
606            let neighbors: Vec<SymbolId> = match direction {
607                ChainDirection::TypeUsers => self.type_users(current).collect(),
608                ChainDirection::TypeDeps => self.types_used_by(current).collect(),
609                ChainDirection::Callers | ChainDirection::Callees => {
610                    unreachable!("Callers/Callees must use CodeGraphV2")
611                }
612            };
613
614            for neighbor in neighbors {
615                if !visited.contains(&neighbor) {
616                    visited.insert(neighbor);
617                    queue.push_back((neighbor, depth + 1));
618                }
619            }
620        }
621
622        result
623    }
624
625    /// Get generic arguments for a usage node (O(1)).
626    #[inline]
627    pub fn generic_args(&self, node: TypeNodeId) -> impl Iterator<Item = TypeNodeId> + '_ {
628        let args = if node.kind() == NodeKind::Usage {
629            self.usage_to_args.get(node.index() as usize)
630        } else {
631            None
632        };
633        args.into_iter()
634            .flat_map(|v| v.iter().map(|&idx| TypeNodeId::arg(idx)))
635    }
636
637    /// Get trait bounds for a node (O(1)).
638    #[inline]
639    pub fn trait_bounds(&self, node: TypeNodeId) -> impl Iterator<Item = TypeNodeId> + '_ {
640        self.node_to_bounds
641            .get(&node)
642            .into_iter()
643            .flat_map(|v| v.iter().map(|&idx| TypeNodeId::bound(idx)))
644    }
645
646    /// Get definition data by ID.
647    #[inline]
648    pub fn get_definition(&self, id: TypeNodeId) -> Option<&DefinitionData> {
649        if id.kind() == NodeKind::Definition {
650            self.definitions.get(id.index() as usize)
651        } else {
652            None
653        }
654    }
655
656    /// Get usage data by ID.
657    #[inline]
658    pub fn get_usage(&self, id: TypeNodeId) -> Option<&UsageData> {
659        if id.kind() == NodeKind::Usage {
660            self.usages.get(id.index() as usize)
661        } else {
662            None
663        }
664    }
665
666    /// Get generic arg data by ID.
667    #[inline]
668    pub fn get_generic_arg(&self, id: TypeNodeId) -> Option<&GenericArgData> {
669        if id.kind() == NodeKind::GenericArg {
670            self.generic_args.get(id.index() as usize)
671        } else {
672            None
673        }
674    }
675
676    /// Get trait bound data by ID.
677    #[inline]
678    pub fn get_trait_bound(&self, id: TypeNodeId) -> Option<&TraitBoundData> {
679        if id.kind() == NodeKind::TraitBound {
680            self.trait_bounds.get(id.index() as usize)
681        } else {
682            None
683        }
684    }
685
686    // ========================================================================
687    // Impact Analysis
688    // ========================================================================
689
690    /// Analyze the impact of changing a type (O(1) + result count).
691    pub fn impact(&self, id: SymbolId) -> TypeImpactV2 {
692        TypeImpactV2 {
693            // Direct usages: O(1)
694            direct_usages: self
695                .lookup
696                .symbol_to_usages
697                .get(&id)
698                .cloned()
699                .unwrap_or_default(),
700
701            // Trait bounds: O(1)
702            bound_usages: self
703                .lookup
704                .trait_to_bounds
705                .get(&id)
706                .cloned()
707                .unwrap_or_default(),
708
709            // Containing types: O(containers)
710            containing_types: self
711                .lookup
712                .container_fields
713                .iter()
714                .filter(|(_, fields)| fields.contains(&id))
715                .map(|(&container, _)| container)
716                .collect(),
717
718            // Generic usages: computed separately if needed
719            generic_usages: vec![],
720        }
721    }
722
723    // ========================================================================
724    // Statistics
725    // ========================================================================
726
727    /// Total number of nodes.
728    pub fn node_count(&self) -> usize {
729        self.definitions.len()
730            + self.usages.len()
731            + self.generic_args.len()
732            + self.trait_bounds.len()
733    }
734
735    /// Number of definitions.
736    pub fn definition_count(&self) -> usize {
737        self.definitions.len()
738    }
739
740    /// Number of usages.
741    pub fn usages_count(&self) -> usize {
742        self.usages.len()
743    }
744
745    /// Check if empty.
746    pub fn is_empty(&self) -> bool {
747        self.definitions.is_empty() && self.usages.is_empty()
748    }
749}
750
751// ============================================================================
752// Tests
753// ============================================================================
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758    use crate::symbol::{SymbolPath, SymbolRegistry};
759    use crate::SymbolKind;
760
761    fn create_test_registry() -> SymbolRegistry {
762        let mut registry = SymbolRegistry::new();
763        let _ = registry.register(SymbolPath::parse("test::Foo").unwrap(), SymbolKind::Struct);
764        let _ = registry.register(SymbolPath::parse("test::Bar").unwrap(), SymbolKind::Struct);
765        let _ = registry.register(SymbolPath::parse("test::Clone").unwrap(), SymbolKind::Trait);
766        registry
767    }
768
769    fn lookup(registry: &SymbolRegistry, path: &str) -> SymbolId {
770        let symbol_path = SymbolPath::parse(path).unwrap();
771        registry.lookup(&symbol_path).unwrap()
772    }
773
774    #[test]
775    fn test_new_graph() {
776        let graph = TypeFlowGraphV2::new();
777        assert!(graph.is_empty());
778        assert_eq!(graph.node_count(), 0);
779    }
780
781    #[test]
782    fn test_add_definition() {
783        let mut graph = TypeFlowGraphV2::new();
784        let registry = create_test_registry();
785
786        let foo_id = lookup(&registry, "test::Foo");
787        let node = graph.add_definition(foo_id, TypeDefKind::Struct);
788
789        assert_eq!(graph.definition_count(), 1);
790        assert_eq!(graph.definition(foo_id), Some(node));
791        assert_eq!(node.kind(), NodeKind::Definition);
792
793        // Adding same definition returns existing node
794        let node2 = graph.add_definition(foo_id, TypeDefKind::Struct);
795        assert_eq!(node, node2);
796        assert_eq!(graph.definition_count(), 1);
797    }
798
799    #[test]
800    fn test_add_usage() {
801        let mut graph = TypeFlowGraphV2::new();
802        let registry = create_test_registry();
803
804        let foo_id = lookup(&registry, "test::Foo");
805        graph.add_definition(foo_id, TypeDefKind::Struct);
806
807        let usage = graph.add_usage(UsageContext::FieldType, RefKind::Owned, Some(foo_id), None);
808
809        assert_eq!(graph.usage_count(foo_id), 1);
810        assert_eq!(usage.kind(), NodeKind::Usage);
811
812        let usages: Vec<_> = graph.usages(foo_id).collect();
813        assert_eq!(usages.len(), 1);
814        assert_eq!(usages[0], usage);
815    }
816
817    #[test]
818    fn test_add_usage_with_container() {
819        let mut graph = TypeFlowGraphV2::new();
820        let registry = create_test_registry();
821
822        let foo_id = lookup(&registry, "test::Foo");
823        let bar_id = lookup(&registry, "test::Bar");
824        graph.add_definition(foo_id, TypeDefKind::Struct);
825        graph.add_definition(bar_id, TypeDefKind::Struct);
826
827        // Bar has a field of type Foo
828        let usage = graph.add_usage(
829            UsageContext::FieldType,
830            RefKind::Owned,
831            Some(foo_id),
832            Some(bar_id),
833        );
834
835        // Verify container is stored
836        let data = graph.get_usage(usage).unwrap();
837        assert_eq!(data.container, Some(bar_id));
838        assert_eq!(data.resolved, Some(foo_id));
839    }
840
841    #[test]
842    fn test_container_to_usages_index() {
843        let mut graph = TypeFlowGraphV2::new();
844        let registry = create_test_registry();
845
846        let foo_id = lookup(&registry, "test::Foo");
847        let bar_id = lookup(&registry, "test::Bar");
848        let clone_id = lookup(&registry, "test::Clone");
849        graph.add_definition(foo_id, TypeDefKind::Struct);
850        graph.add_definition(bar_id, TypeDefKind::Struct);
851        graph.add_definition(clone_id, TypeDefKind::Trait);
852
853        // Bar uses Foo (field) and Clone (trait bound) — two usages from same container
854        graph.add_usage(
855            UsageContext::FieldType,
856            RefKind::Owned,
857            Some(foo_id),
858            Some(bar_id),
859        );
860        graph.add_usage(
861            UsageContext::FieldType,
862            RefKind::Owned,
863            Some(clone_id),
864            Some(bar_id),
865        );
866
867        // container_to_usages should have 2 entries for bar_id
868        let indices = graph.lookup.container_to_usages.get(&bar_id).unwrap();
869        assert_eq!(indices.len(), 2);
870    }
871
872    #[test]
873    fn test_types_used_by() {
874        let mut graph = TypeFlowGraphV2::new();
875        let registry = create_test_registry();
876
877        let foo_id = lookup(&registry, "test::Foo");
878        let bar_id = lookup(&registry, "test::Bar");
879        let clone_id = lookup(&registry, "test::Clone");
880        graph.add_definition(foo_id, TypeDefKind::Struct);
881        graph.add_definition(bar_id, TypeDefKind::Struct);
882        graph.add_definition(clone_id, TypeDefKind::Trait);
883
884        // Bar uses Foo and Clone
885        graph.add_usage(
886            UsageContext::FieldType,
887            RefKind::Owned,
888            Some(foo_id),
889            Some(bar_id),
890        );
891        graph.add_usage(
892            UsageContext::FieldType,
893            RefKind::Owned,
894            Some(clone_id),
895            Some(bar_id),
896        );
897
898        let used: Vec<_> = graph.types_used_by(bar_id).collect();
899        assert_eq!(used.len(), 2);
900        assert!(used.contains(&foo_id));
901        assert!(used.contains(&clone_id));
902
903        // Foo uses nothing
904        let used_by_foo: Vec<_> = graph.types_used_by(foo_id).collect();
905        assert!(used_by_foo.is_empty());
906    }
907
908    /// Reproducer: types_used_by should return types referenced via generic wrappers.
909    ///
910    /// Simulates: `struct Bar { items: Vec<Foo> }`
911    /// The builder produces:
912    ///   add_usage(FieldType, Owned, resolved=None, container=Some(bar_id))  ← Vec unresolved
913    ///   add_generic_arg(parent_usage, 0, resolved=Some(foo_id))             ← Foo as arg
914    ///
915    /// Expected: types_used_by(bar_id) should include foo_id.
916    #[test]
917    fn test_types_used_by_includes_generic_arg_types() {
918        let mut graph = TypeFlowGraphV2::new();
919        let registry = create_test_registry();
920
921        let foo_id = lookup(&registry, "test::Foo");
922        let bar_id = lookup(&registry, "test::Bar");
923        graph.add_definition(foo_id, TypeDefKind::Struct);
924        graph.add_definition(bar_id, TypeDefKind::Struct);
925
926        // Bar has field: Vec<Foo>
927        // Vec is unresolved (std type) → resolved=None
928        let vec_usage = graph.add_usage(
929            UsageContext::FieldType,
930            RefKind::Owned,
931            None, // Vec can't be resolved
932            Some(bar_id),
933        );
934
935        // Foo is the generic argument of Vec
936        graph.add_generic_arg(vec_usage, 0, Some(foo_id));
937
938        // types_used_by(bar_id) should still find Foo via the generic arg
939        let used: Vec<_> = graph.types_used_by(bar_id).collect();
940        assert!(
941            used.contains(&foo_id),
942            "types_used_by should include Foo referenced via Vec<Foo>, got: {:?}",
943            used
944        );
945    }
946
947    #[test]
948    fn test_type_users() {
949        let mut graph = TypeFlowGraphV2::new();
950        let registry = create_test_registry();
951
952        let foo_id = lookup(&registry, "test::Foo");
953        let bar_id = lookup(&registry, "test::Bar");
954        let clone_id = lookup(&registry, "test::Clone");
955        graph.add_definition(foo_id, TypeDefKind::Struct);
956        graph.add_definition(bar_id, TypeDefKind::Struct);
957        graph.add_definition(clone_id, TypeDefKind::Trait);
958
959        // Bar and Clone both use Foo
960        graph.add_usage(
961            UsageContext::FieldType,
962            RefKind::Owned,
963            Some(foo_id),
964            Some(bar_id),
965        );
966        graph.add_usage(
967            UsageContext::ParamType,
968            RefKind::Ref,
969            Some(foo_id),
970            Some(clone_id),
971        );
972
973        let users: Vec<_> = graph.type_users(foo_id).collect();
974        assert_eq!(users.len(), 2);
975        assert!(users.contains(&bar_id));
976        assert!(users.contains(&clone_id));
977
978        // Nobody uses Clone
979        let users_of_clone: Vec<_> = graph.type_users(clone_id).collect();
980        assert!(users_of_clone.is_empty());
981    }
982
983    /// Reproducer: type_users should return containers that reference a type via generic args.
984    ///
985    /// Simulates: `struct Bar { items: Vec<Foo> }`
986    /// Expected: type_users(foo_id) should include bar_id.
987    #[test]
988    fn test_type_users_includes_generic_arg_references() {
989        let mut graph = TypeFlowGraphV2::new();
990        let registry = create_test_registry();
991
992        let foo_id = lookup(&registry, "test::Foo");
993        let bar_id = lookup(&registry, "test::Bar");
994        graph.add_definition(foo_id, TypeDefKind::Struct);
995        graph.add_definition(bar_id, TypeDefKind::Struct);
996
997        // Bar has field: Vec<Foo>
998        // Vec is unresolved → resolved=None
999        let vec_usage =
1000            graph.add_usage(UsageContext::FieldType, RefKind::Owned, None, Some(bar_id));
1001
1002        // Foo is the generic argument of Vec
1003        graph.add_generic_arg(vec_usage, 0, Some(foo_id));
1004
1005        // type_users(foo_id) should find Bar as a user via the generic arg
1006        let users: Vec<_> = graph.type_users(foo_id).collect();
1007        assert!(
1008            users.contains(&bar_id),
1009            "type_users should include Bar which references Foo via Vec<Foo>, got: {:?}",
1010            users
1011        );
1012    }
1013
1014    #[test]
1015    fn test_type_users_without_container() {
1016        let mut graph = TypeFlowGraphV2::new();
1017        let registry = create_test_registry();
1018
1019        let foo_id = lookup(&registry, "test::Foo");
1020        graph.add_definition(foo_id, TypeDefKind::Struct);
1021
1022        // Usage without container (legacy behavior)
1023        graph.add_usage(UsageContext::FieldType, RefKind::Owned, Some(foo_id), None);
1024
1025        // type_users should return empty since no container is set
1026        let users: Vec<_> = graph.type_users(foo_id).collect();
1027        assert!(users.is_empty());
1028
1029        // But usages() still works (returns TypeNodeId)
1030        assert_eq!(graph.usage_count(foo_id), 1);
1031    }
1032
1033    #[test]
1034    fn test_generic_args() {
1035        let mut graph = TypeFlowGraphV2::new();
1036        let registry = create_test_registry();
1037
1038        let foo_id = lookup(&registry, "test::Foo");
1039        graph.add_definition(foo_id, TypeDefKind::Struct);
1040
1041        // Vec<Foo>
1042        let vec_usage = graph.add_usage(UsageContext::FieldType, RefKind::Owned, None, None);
1043        let arg = graph.add_generic_arg(vec_usage, 0, Some(foo_id));
1044
1045        let args: Vec<_> = graph.generic_args(vec_usage).collect();
1046        assert_eq!(args.len(), 1);
1047        assert_eq!(args[0], arg);
1048        assert_eq!(arg.kind(), NodeKind::GenericArg);
1049    }
1050
1051    #[test]
1052    fn test_trait_bounds() {
1053        let mut graph = TypeFlowGraphV2::new();
1054        let registry = create_test_registry();
1055
1056        let clone_id = lookup(&registry, "test::Clone");
1057        graph.add_definition(clone_id, TypeDefKind::Trait);
1058
1059        // T: Clone
1060        let param = graph.add_usage(UsageContext::WhereBound, RefKind::Owned, None, None);
1061        let bound = graph.add_trait_bound(param, Some(clone_id));
1062
1063        let bounds: Vec<_> = graph.trait_bounds(param).collect();
1064        assert_eq!(bounds.len(), 1);
1065        assert_eq!(bounds[0], bound);
1066        assert_eq!(bound.kind(), NodeKind::TraitBound);
1067    }
1068
1069    #[test]
1070    fn test_impact_analysis() {
1071        let mut graph = TypeFlowGraphV2::new();
1072        let registry = create_test_registry();
1073
1074        let foo_id = lookup(&registry, "test::Foo");
1075        let bar_id = lookup(&registry, "test::Bar");
1076
1077        graph.add_definition(foo_id, TypeDefKind::Struct);
1078        graph.add_definition(bar_id, TypeDefKind::Struct);
1079
1080        // Bar has a field of type Foo
1081        graph.add_usage(
1082            UsageContext::FieldType,
1083            RefKind::Owned,
1084            Some(foo_id),
1085            Some(bar_id),
1086        );
1087        graph.add_contains(bar_id, foo_id);
1088
1089        let impact = graph.impact(foo_id);
1090        assert_eq!(impact.direct_usages.len(), 1);
1091        assert!(impact.containing_types.contains(&bar_id));
1092    }
1093
1094    #[test]
1095    fn test_type_node_id_encoding() {
1096        // Test that different kinds produce different IDs
1097        let def = TypeNodeId::def(100);
1098        let usage = TypeNodeId::usage(100);
1099        let arg = TypeNodeId::arg(100);
1100        let bound = TypeNodeId::bound(100);
1101
1102        assert_ne!(def, usage);
1103        assert_ne!(usage, arg);
1104        assert_ne!(arg, bound);
1105
1106        // Test round-trip
1107        assert_eq!(def.kind(), NodeKind::Definition);
1108        assert_eq!(def.index(), 100);
1109
1110        assert_eq!(usage.kind(), NodeKind::Usage);
1111        assert_eq!(usage.index(), 100);
1112
1113        assert_eq!(arg.kind(), NodeKind::GenericArg);
1114        assert_eq!(arg.index(), 100);
1115
1116        assert_eq!(bound.kind(), NodeKind::TraitBound);
1117        assert_eq!(bound.index(), 100);
1118    }
1119
1120    // ====================================================================
1121    // BFS chain traversal tests
1122    // ====================================================================
1123
1124    /// Build a type graph:
1125    /// fn process(x: Foo) -> Bar   (process uses Foo, Bar)
1126    /// fn render(y: Bar)           (render uses Bar)
1127    /// struct Baz { f: Foo }       (Baz uses Foo)
1128    fn build_chain_test_graph() -> (
1129        TypeFlowGraphV2,
1130        SymbolId,
1131        SymbolId,
1132        SymbolId,
1133        SymbolId,
1134        SymbolId,
1135    ) {
1136        let mut registry = SymbolRegistry::new();
1137        let foo_id = registry
1138            .register(SymbolPath::parse("test::Foo").unwrap(), SymbolKind::Struct)
1139            .unwrap();
1140        let bar_id = registry
1141            .register(SymbolPath::parse("test::Bar").unwrap(), SymbolKind::Struct)
1142            .unwrap();
1143        let baz_id = registry
1144            .register(SymbolPath::parse("test::Baz").unwrap(), SymbolKind::Struct)
1145            .unwrap();
1146        let process_id = registry
1147            .register(
1148                SymbolPath::parse("test::process").unwrap(),
1149                SymbolKind::Function,
1150            )
1151            .unwrap();
1152        let render_id = registry
1153            .register(
1154                SymbolPath::parse("test::render").unwrap(),
1155                SymbolKind::Function,
1156            )
1157            .unwrap();
1158
1159        let mut graph = TypeFlowGraphV2::new();
1160        graph.add_definition(foo_id, TypeDefKind::Struct);
1161        graph.add_definition(bar_id, TypeDefKind::Struct);
1162        graph.add_definition(baz_id, TypeDefKind::Struct);
1163
1164        // process uses Foo (param) and Bar (return)
1165        graph.add_usage(
1166            UsageContext::ParamType,
1167            RefKind::Owned,
1168            Some(foo_id),
1169            Some(process_id),
1170        );
1171        graph.add_usage(
1172            UsageContext::ReturnType,
1173            RefKind::Owned,
1174            Some(bar_id),
1175            Some(process_id),
1176        );
1177
1178        // render uses Bar (param)
1179        graph.add_usage(
1180            UsageContext::ParamType,
1181            RefKind::Owned,
1182            Some(bar_id),
1183            Some(render_id),
1184        );
1185
1186        // Baz uses Foo (field)
1187        graph.add_usage(
1188            UsageContext::FieldType,
1189            RefKind::Owned,
1190            Some(foo_id),
1191            Some(baz_id),
1192        );
1193
1194        (graph, foo_id, bar_id, baz_id, process_id, render_id)
1195    }
1196
1197    #[test]
1198    fn test_type_users_chain_depth1() {
1199        let (graph, foo_id, _, baz_id, process_id, _) = build_chain_test_graph();
1200
1201        // Foo is used by process (param) and Baz (field)
1202        let chain = graph.type_users_chain(foo_id, 1);
1203        let symbols: Vec<_> = chain.iter().map(|n| n.symbol).collect();
1204        assert_eq!(chain.len(), 2);
1205        assert!(symbols.contains(&process_id));
1206        assert!(symbols.contains(&baz_id));
1207        assert!(chain.iter().all(|n| n.depth == 1));
1208    }
1209
1210    #[test]
1211    fn test_types_used_by_chain_depth1() {
1212        let (graph, foo_id, bar_id, _, process_id, _) = build_chain_test_graph();
1213
1214        // process uses Foo and Bar
1215        let chain = graph.types_used_by_chain(process_id, 1);
1216        let symbols: Vec<_> = chain.iter().map(|n| n.symbol).collect();
1217        assert_eq!(chain.len(), 2);
1218        assert!(symbols.contains(&foo_id));
1219        assert!(symbols.contains(&bar_id));
1220    }
1221
1222    #[test]
1223    fn test_type_users_chain_transitive() {
1224        // Build a chain: StructA is used by StructB (field), StructB is used by fn_c (param)
1225        let mut registry = SymbolRegistry::new();
1226        let a_id = registry
1227            .register(SymbolPath::parse("test::A").unwrap(), SymbolKind::Struct)
1228            .unwrap();
1229        let b_id = registry
1230            .register(SymbolPath::parse("test::B").unwrap(), SymbolKind::Struct)
1231            .unwrap();
1232        let c_id = registry
1233            .register(SymbolPath::parse("test::c").unwrap(), SymbolKind::Function)
1234            .unwrap();
1235
1236        let mut graph = TypeFlowGraphV2::new();
1237        graph.add_definition(a_id, TypeDefKind::Struct);
1238        graph.add_definition(b_id, TypeDefKind::Struct);
1239
1240        // B uses A (field)
1241        graph.add_usage(
1242            UsageContext::FieldType,
1243            RefKind::Owned,
1244            Some(a_id),
1245            Some(b_id),
1246        );
1247        // c uses B (param)
1248        graph.add_usage(
1249            UsageContext::ParamType,
1250            RefKind::Owned,
1251            Some(b_id),
1252            Some(c_id),
1253        );
1254
1255        // type_users_chain(A, 2): A ← B (depth 1), B ← c (depth 2)
1256        let chain = graph.type_users_chain(a_id, 2);
1257        assert_eq!(chain.len(), 2);
1258        assert_eq!(chain[0].symbol, b_id);
1259        assert_eq!(chain[0].depth, 1);
1260        assert_eq!(chain[1].symbol, c_id);
1261        assert_eq!(chain[1].depth, 2);
1262    }
1263
1264    #[test]
1265    fn test_type_users_chain_respects_max_depth() {
1266        let mut registry = SymbolRegistry::new();
1267        let a_id = registry
1268            .register(SymbolPath::parse("test::A").unwrap(), SymbolKind::Struct)
1269            .unwrap();
1270        let b_id = registry
1271            .register(SymbolPath::parse("test::B").unwrap(), SymbolKind::Struct)
1272            .unwrap();
1273        let c_id = registry
1274            .register(SymbolPath::parse("test::c").unwrap(), SymbolKind::Function)
1275            .unwrap();
1276
1277        let mut graph = TypeFlowGraphV2::new();
1278        graph.add_definition(a_id, TypeDefKind::Struct);
1279        graph.add_definition(b_id, TypeDefKind::Struct);
1280
1281        graph.add_usage(
1282            UsageContext::FieldType,
1283            RefKind::Owned,
1284            Some(a_id),
1285            Some(b_id),
1286        );
1287        graph.add_usage(
1288            UsageContext::ParamType,
1289            RefKind::Owned,
1290            Some(b_id),
1291            Some(c_id),
1292        );
1293
1294        // max_depth=1: should only find B, not c
1295        let chain = graph.type_users_chain(a_id, 1);
1296        assert_eq!(chain.len(), 1);
1297        assert_eq!(chain[0].symbol, b_id);
1298        assert_eq!(chain[0].depth, 1);
1299    }
1300
1301    #[test]
1302    fn test_analyze_type_chain_statistics() {
1303        let (graph, foo_id, _, _, _, _) = build_chain_test_graph();
1304
1305        let result = graph.analyze_type_chain(foo_id, 5, ChainDirection::TypeUsers);
1306        assert_eq!(result.start, foo_id);
1307        assert_eq!(result.direction, ChainDirection::TypeUsers);
1308        assert_eq!(result.max_depth, 5);
1309        assert_eq!(result.nodes.len(), 2); // process and Baz
1310        assert_eq!(result.max_actual_depth, 1);
1311        assert_eq!(*result.by_depth.get(&1).unwrap_or(&0), 2);
1312    }
1313
1314    #[test]
1315    fn test_chain_no_cycles() {
1316        // Ensure BFS doesn't loop on circular references
1317        let mut registry = SymbolRegistry::new();
1318        let a_id = registry
1319            .register(SymbolPath::parse("test::A").unwrap(), SymbolKind::Struct)
1320            .unwrap();
1321        let b_id = registry
1322            .register(SymbolPath::parse("test::B").unwrap(), SymbolKind::Struct)
1323            .unwrap();
1324
1325        let mut graph = TypeFlowGraphV2::new();
1326        graph.add_definition(a_id, TypeDefKind::Struct);
1327        graph.add_definition(b_id, TypeDefKind::Struct);
1328
1329        // A uses B, B uses A (cycle)
1330        graph.add_usage(
1331            UsageContext::FieldType,
1332            RefKind::Owned,
1333            Some(b_id),
1334            Some(a_id),
1335        );
1336        graph.add_usage(
1337            UsageContext::FieldType,
1338            RefKind::Owned,
1339            Some(a_id),
1340            Some(b_id),
1341        );
1342
1343        // Should not infinite loop; visited set prevents revisiting
1344        let chain = graph.type_users_chain(a_id, 10);
1345        assert_eq!(chain.len(), 1); // Only B at depth 1
1346        assert_eq!(chain[0].symbol, b_id);
1347    }
1348}