1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
41pub enum RefKind {
42 #[default]
44 Owned,
45 Ref,
47 RefMut,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub enum TypeDefKind {
54 Struct,
56 Enum,
58 Trait,
60 TypeAlias,
62 GenericParam,
64 AssociatedType,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
70pub enum UsageContext {
71 FieldType,
73 ParamType,
75 ReturnType,
77 LocalVar,
79 WhereBound,
81 ImplTarget,
83 ImplTrait,
85 UseStatement,
87 Cast,
89 Turbofish,
91 VariantField,
93 ConstType,
95 MatchScrutinee,
98 ConstructorCall,
101 StructLiteral,
104}
105
106#[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 #[inline]
128 pub const fn def(idx: u32) -> Self {
129 Self((Self::KIND_DEF << Self::KIND_SHIFT) | idx)
130 }
131
132 #[inline]
134 pub const fn usage(idx: u32) -> Self {
135 Self((Self::KIND_USAGE << Self::KIND_SHIFT) | idx)
136 }
137
138 #[inline]
140 pub const fn arg(idx: u32) -> Self {
141 Self((Self::KIND_ARG << Self::KIND_SHIFT) | idx)
142 }
143
144 #[inline]
146 pub const fn bound(idx: u32) -> Self {
147 Self((Self::KIND_BOUND << Self::KIND_SHIFT) | idx)
148 }
149
150 #[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 #[inline]
164 pub const fn index(self) -> u32 {
165 self.0 & Self::INDEX_MASK
166 }
167
168 #[inline]
170 pub const fn as_u32(self) -> u32 {
171 self.0
172 }
173}
174
175#[derive(Copy, Clone, Eq, PartialEq, Debug)]
177pub enum NodeKind {
178 Definition,
180 Usage,
182 GenericArg,
184 TraitBound,
186}
187
188#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
194pub struct DefinitionData {
195 pub symbol_id: SymbolId,
197 pub kind: TypeDefKind,
199}
200
201#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
203pub struct UsageData {
204 pub resolved: Option<SymbolId>,
206 pub context: UsageContext,
208 pub ref_kind: RefKind,
210 pub container: Option<SymbolId>,
215}
216
217#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
219pub struct GenericArgData {
220 pub parent_usage: u32,
222 pub arg_index: u8,
224 pub resolved: Option<SymbolId>,
226}
227
228#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
230pub struct TraitBoundData {
231 pub target: TypeNodeId,
233 pub trait_id: Option<SymbolId>,
235}
236
237#[derive(Clone, Default, Debug, Serialize, Deserialize)]
243pub struct LookupTable {
244 pub symbol_to_def: SecondaryMap<SymbolId, u32>,
246
247 pub symbol_to_usages: HashMap<SymbolId, Vec<u32>>,
250
251 pub container_to_usages: HashMap<SymbolId, Vec<u32>>,
254
255 pub trait_to_bounds: HashMap<SymbolId, Vec<u32>>,
257
258 pub container_fields: HashMap<SymbolId, Vec<SymbolId>>,
260}
261
262#[derive(Clone, Default, Debug, Serialize)]
272pub struct TypeFlowGraphV2 {
273 pub definitions: Vec<DefinitionData>,
276
277 pub usages: Vec<UsageData>,
279
280 pub generic_args: Vec<GenericArgData>,
282
283 pub trait_bounds: Vec<TraitBoundData>,
285
286 pub usage_to_args: Vec<SmallVec<[u32; 4]>>,
289
290 pub node_to_bounds: HashMap<TypeNodeId, SmallVec<[u32; 2]>>,
292
293 pub lookup: LookupTable,
297}
298
299#[derive(Debug, Clone, Default)]
301pub struct TypeImpactV2 {
302 pub direct_usages: Vec<u32>,
304 pub bound_usages: Vec<u32>,
306 pub containing_types: Vec<SymbolId>,
308 pub generic_usages: Vec<u32>,
310}
311
312impl TypeFlowGraphV2 {
313 pub fn new() -> Self {
315 Self::default()
316 }
317
318 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), 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 pub fn add_definition(&mut self, symbol_id: SymbolId, kind: TypeDefKind) -> TypeNodeId {
337 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 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 self.usage_to_args.push(SmallVec::new());
367
368 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 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 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 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 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 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 self.node_to_bounds.entry(target).or_default().push(idx);
434
435 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 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 #[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 #[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 #[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 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 if let Some(u) = self.usages.get(idx as usize) {
500 if let Some(resolved) = u.resolved {
501 types.push(resolved);
502 }
503 }
504 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 pub fn impact(&self, id: SymbolId) -> TypeImpactV2 {
692 TypeImpactV2 {
693 direct_usages: self
695 .lookup
696 .symbol_to_usages
697 .get(&id)
698 .cloned()
699 .unwrap_or_default(),
700
701 bound_usages: self
703 .lookup
704 .trait_to_bounds
705 .get(&id)
706 .cloned()
707 .unwrap_or_default(),
708
709 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: vec![],
720 }
721 }
722
723 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 pub fn definition_count(&self) -> usize {
737 self.definitions.len()
738 }
739
740 pub fn usages_count(&self) -> usize {
742 self.usages.len()
743 }
744
745 pub fn is_empty(&self) -> bool {
747 self.definitions.is_empty() && self.usages.is_empty()
748 }
749}
750
751#[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(®istry, "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 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(®istry, "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(®istry, "test::Foo");
823 let bar_id = lookup(®istry, "test::Bar");
824 graph.add_definition(foo_id, TypeDefKind::Struct);
825 graph.add_definition(bar_id, TypeDefKind::Struct);
826
827 let usage = graph.add_usage(
829 UsageContext::FieldType,
830 RefKind::Owned,
831 Some(foo_id),
832 Some(bar_id),
833 );
834
835 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(®istry, "test::Foo");
847 let bar_id = lookup(®istry, "test::Bar");
848 let clone_id = lookup(®istry, "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 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 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(®istry, "test::Foo");
878 let bar_id = lookup(®istry, "test::Bar");
879 let clone_id = lookup(®istry, "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 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 let used_by_foo: Vec<_> = graph.types_used_by(foo_id).collect();
905 assert!(used_by_foo.is_empty());
906 }
907
908 #[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(®istry, "test::Foo");
922 let bar_id = lookup(®istry, "test::Bar");
923 graph.add_definition(foo_id, TypeDefKind::Struct);
924 graph.add_definition(bar_id, TypeDefKind::Struct);
925
926 let vec_usage = graph.add_usage(
929 UsageContext::FieldType,
930 RefKind::Owned,
931 None, Some(bar_id),
933 );
934
935 graph.add_generic_arg(vec_usage, 0, Some(foo_id));
937
938 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(®istry, "test::Foo");
953 let bar_id = lookup(®istry, "test::Bar");
954 let clone_id = lookup(®istry, "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 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 let users_of_clone: Vec<_> = graph.type_users(clone_id).collect();
980 assert!(users_of_clone.is_empty());
981 }
982
983 #[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(®istry, "test::Foo");
993 let bar_id = lookup(®istry, "test::Bar");
994 graph.add_definition(foo_id, TypeDefKind::Struct);
995 graph.add_definition(bar_id, TypeDefKind::Struct);
996
997 let vec_usage =
1000 graph.add_usage(UsageContext::FieldType, RefKind::Owned, None, Some(bar_id));
1001
1002 graph.add_generic_arg(vec_usage, 0, Some(foo_id));
1004
1005 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(®istry, "test::Foo");
1020 graph.add_definition(foo_id, TypeDefKind::Struct);
1021
1022 graph.add_usage(UsageContext::FieldType, RefKind::Owned, Some(foo_id), None);
1024
1025 let users: Vec<_> = graph.type_users(foo_id).collect();
1027 assert!(users.is_empty());
1028
1029 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(®istry, "test::Foo");
1039 graph.add_definition(foo_id, TypeDefKind::Struct);
1040
1041 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(®istry, "test::Clone");
1057 graph.add_definition(clone_id, TypeDefKind::Trait);
1058
1059 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(®istry, "test::Foo");
1075 let bar_id = lookup(®istry, "test::Bar");
1076
1077 graph.add_definition(foo_id, TypeDefKind::Struct);
1078 graph.add_definition(bar_id, TypeDefKind::Struct);
1079
1080 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 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 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 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 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 graph.add_usage(
1180 UsageContext::ParamType,
1181 RefKind::Owned,
1182 Some(bar_id),
1183 Some(render_id),
1184 );
1185
1186 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 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 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 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 graph.add_usage(
1242 UsageContext::FieldType,
1243 RefKind::Owned,
1244 Some(a_id),
1245 Some(b_id),
1246 );
1247 graph.add_usage(
1249 UsageContext::ParamType,
1250 RefKind::Owned,
1251 Some(b_id),
1252 Some(c_id),
1253 );
1254
1255 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 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); 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 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 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 let chain = graph.type_users_chain(a_id, 10);
1345 assert_eq!(chain.len(), 1); assert_eq!(chain[0].symbol, b_id);
1347 }
1348}