1use super::var_id::{VarId, VarSymbolMapping};
10use crate::define_index;
11use crate::symbol::{SymbolId, SymbolRegistry};
12use serde::{Deserialize, Serialize};
13use slotmap::SecondaryMap;
14use smallvec::SmallVec;
15use std::collections::{HashMap, HashSet};
16
17use super::borrow_v2::{
19 ActiveBorrowV2, BorrowAnalysis, BorrowConflict, BorrowKind, BorrowTrackerV2, MoveError,
20};
21use super::lock_v2::LockTrackerV2;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub enum FlowKind {
30 Assign,
33 Derive,
35 Read,
37 Write,
39 Return,
41 Argument,
43 Reference,
45 Dereference,
47 Conditional,
49
50 SharedBorrow,
53 MutBorrow,
55 Move,
57 Clone,
59 Copy,
61 Drop,
63 Reborrow,
65 Index,
67}
68
69impl FlowKind {
70 pub fn transfers_ownership(&self) -> bool {
72 matches!(self, FlowKind::Move | FlowKind::Return)
73 }
74
75 pub fn is_borrow(&self) -> bool {
77 matches!(
78 self,
79 FlowKind::SharedBorrow | FlowKind::MutBorrow | FlowKind::Reborrow
80 )
81 }
82
83 pub fn is_mutable(&self) -> bool {
85 matches!(
86 self,
87 FlowKind::MutBorrow | FlowKind::Write | FlowKind::Assign
88 )
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
94pub enum VarKind {
95 Local,
97 Parameter,
99 Field,
101 Static,
103 Return,
105 Temp,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
111pub struct FlowEdge {
112 pub kind: FlowKind,
114 pub line: u32,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub struct VarNode {
121 pub name: String,
123 pub kind: VarKind,
125 pub line: u32,
127}
128
129#[derive(Debug, Clone, Default, Serialize, Deserialize)]
131pub struct DataFlowStats {
132 pub var_count: usize,
134 pub flow_count: usize,
136 pub vars_by_kind: HashMap<String, usize>,
138 pub flows_by_kind: HashMap<String, usize>,
140}
141
142#[derive(Debug, Clone)]
144pub struct FlowChain {
145 pub steps: Vec<FlowStep>,
147}
148
149#[derive(Debug, Clone)]
151pub struct FlowStep {
152 pub var_id: VarId,
154 pub flow_kind: Option<FlowKind>,
156}
157
158define_index! {
163 pub struct FlowId;
165}
166
167define_index! {
168 pub struct ScopeId;
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
178pub enum ScopeKind {
179 Function,
181 IfThen,
183 IfElse,
185 WhileBody,
187 ForBody,
189 LoopBody,
191 MatchArm,
193 Block,
195 Closure,
197}
198
199#[derive(Debug, Clone)]
201pub struct ScopeData {
202 pub parent: Option<ScopeId>,
204 pub kind: ScopeKind,
206 pub guard: Option<Guard>,
208}
209
210#[derive(Debug, Clone)]
212pub struct Guard {
213 pub kind: GuardKind,
215 pub var_names: SmallVec<[String; 2]>,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
221pub enum GuardKind {
222 LessThan,
224 LessEqual,
226 GreaterThan,
228 GreaterEqual,
230 LetSome,
232 LetOk,
234 IsSome,
236 IsOk,
238 NonEmpty,
240}
241
242#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
262pub struct VarData {
263 pub parent: SymbolId,
265 pub kind: VarKind,
267 pub line: u32,
269 pub is_mut: bool,
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
277pub struct FlowEdgeData {
278 pub from: VarId,
280 pub to: VarId,
282}
283
284#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
288pub struct FlowData {
289 pub kind: FlowKind,
291 pub line: u32,
293 pub scope: ScopeId,
295}
296
297#[derive(Debug, Clone, Default)]
330pub struct DataFlowGraphV2 {
331 vars: SecondaryMap<VarId, VarData>,
334
335 var_names: SecondaryMap<VarId, String>,
337
338 flows: Vec<FlowData>,
340
341 edges: Vec<FlowEdgeData>,
343
344 scopes: Vec<ScopeData>,
347
348 outgoing: SecondaryMap<VarId, SmallVec<[FlowId; 4]>>,
351
352 incoming: SecondaryMap<VarId, SmallVec<[FlowId; 4]>>,
354
355 var_mapping: VarSymbolMapping,
358
359 symbol_to_vars: HashMap<SymbolId, SmallVec<[VarId; 8]>>,
361
362 borrow_tracker: BorrowTrackerV2,
365
366 lock_tracker: LockTrackerV2,
368}
369
370impl DataFlowGraphV2 {
371 pub fn new() -> Self {
373 Self::default()
374 }
375
376 pub fn with_capacity(var_capacity: usize, flow_capacity: usize) -> Self {
378 Self {
379 vars: SecondaryMap::new(),
380 var_names: SecondaryMap::new(),
381 flows: Vec::with_capacity(flow_capacity),
382 edges: Vec::with_capacity(flow_capacity),
383 scopes: Vec::new(),
384 outgoing: SecondaryMap::new(),
385 incoming: SecondaryMap::new(),
386 var_mapping: VarSymbolMapping::with_capacity(var_capacity),
387 symbol_to_vars: HashMap::with_capacity(var_capacity / 4),
388 borrow_tracker: BorrowTrackerV2::new(),
389 lock_tracker: LockTrackerV2::new(),
390 }
391 }
392
393 pub fn add_var(
408 &mut self,
409 data: VarData,
410 name: String,
411 var_symbol_id: Option<SymbolId>,
412 ) -> VarId {
413 let var_id = match var_symbol_id {
414 Some(sid) => self.var_mapping.register(sid),
415 None => self.var_mapping.allocate(),
416 };
417
418 self.vars.insert(var_id, data);
420 self.var_names.insert(var_id, name);
421 self.outgoing.insert(var_id, SmallVec::new());
422 self.incoming.insert(var_id, SmallVec::new());
423
424 self.symbol_to_vars
426 .entry(data.parent)
427 .or_default()
428 .push(var_id);
429
430 var_id
431 }
432
433 #[inline]
435 pub fn var(&self, id: VarId) -> Option<&VarData> {
436 self.vars.get(id)
437 }
438
439 #[inline]
441 pub fn var_name(&self, id: VarId) -> Option<&str> {
442 self.var_names.get(id).map(|s| s.as_str())
443 }
444
445 #[inline]
447 pub fn var_mut(&mut self, id: VarId) -> Option<&mut VarData> {
448 self.vars.get_mut(id)
449 }
450
451 #[inline]
453 pub fn vars_in_symbol(&self, symbol: SymbolId) -> &[VarId] {
454 self.symbol_to_vars
455 .get(&symbol)
456 .map(|v| v.as_slice())
457 .unwrap_or(&[])
458 }
459
460 #[inline]
462 pub fn var_count(&self) -> usize {
463 self.vars.len()
464 }
465
466 pub fn add_flow(&mut self, from: VarId, to: VarId, data: FlowData) -> FlowId {
472 let flow_id = FlowId::from_raw(self.flows.len() as u32);
473
474 self.flows.push(data);
475 self.edges.push(FlowEdgeData { from, to });
476
477 if let Some(out) = self.outgoing.get_mut(from) {
479 out.push(flow_id);
480 }
481 if let Some(inc) = self.incoming.get_mut(to) {
482 inc.push(flow_id);
483 }
484
485 flow_id
486 }
487
488 #[inline]
490 pub fn flow(&self, id: FlowId) -> Option<&FlowData> {
491 self.flows.get(id.as_usize())
492 }
493
494 #[inline]
496 pub fn edge(&self, id: FlowId) -> Option<&FlowEdgeData> {
497 self.edges.get(id.as_usize())
498 }
499
500 #[inline]
502 pub fn flow_count(&self) -> usize {
503 self.flows.len()
504 }
505
506 #[inline]
510 pub fn outgoing(&self, var: VarId) -> &[FlowId] {
511 self.outgoing.get(var).map(|v| v.as_slice()).unwrap_or(&[])
512 }
513
514 #[inline]
516 pub fn incoming(&self, var: VarId) -> &[FlowId] {
517 self.incoming.get(var).map(|v| v.as_slice()).unwrap_or(&[])
518 }
519
520 pub fn successors(&self, var: VarId) -> impl Iterator<Item = VarId> + '_ {
522 self.outgoing(var)
523 .iter()
524 .filter_map(|&flow_id| self.edges.get(flow_id.as_usize()).map(|e| e.to))
525 }
526
527 pub fn predecessors(&self, var: VarId) -> impl Iterator<Item = VarId> + '_ {
529 self.incoming(var)
530 .iter()
531 .filter_map(|&flow_id| self.edges.get(flow_id.as_usize()).map(|e| e.from))
532 }
533
534 pub fn impact(&self, start: VarId) -> Vec<VarId> {
540 let mut visited: HashSet<VarId> = HashSet::new();
541 let mut result = Vec::new();
542 let mut stack = vec![start];
543
544 while let Some(var) = stack.pop() {
545 if visited.contains(&var) {
546 continue;
547 }
548 visited.insert(var);
549 result.push(var);
550
551 for next in self.successors(var) {
552 if !visited.contains(&next) {
553 stack.push(next);
554 }
555 }
556 }
557
558 result
559 }
560
561 pub fn provenance(&self, start: VarId) -> Vec<VarId> {
565 let mut visited: HashSet<VarId> = HashSet::new();
566 let mut result = Vec::new();
567 let mut stack = vec![start];
568
569 while let Some(var) = stack.pop() {
570 if visited.contains(&var) {
571 continue;
572 }
573 visited.insert(var);
574 result.push(var);
575
576 for prev in self.predecessors(var) {
577 if !visited.contains(&prev) {
578 stack.push(prev);
579 }
580 }
581 }
582
583 result
584 }
585
586 #[inline]
590 pub fn var_to_symbol(&self, var: VarId) -> Option<SymbolId> {
591 self.var_mapping.to_symbol(var)
592 }
593
594 #[inline]
596 pub fn symbol_to_var(&self, symbol: SymbolId) -> Option<VarId> {
597 self.var_mapping.to_var(symbol)
598 }
599
600 pub fn iter_vars(&self) -> impl Iterator<Item = (VarId, &VarData)> {
602 self.vars.iter()
603 }
604
605 pub fn resolve_vars(
614 &self,
615 symbol_id: Option<SymbolId>,
616 name: Option<&str>,
617 registry: &SymbolRegistry,
618 ) -> Option<(Vec<VarId>, String)> {
619 if let Some(sid) = symbol_id {
620 let vars = self.vars_in_symbol(sid).to_vec();
621 let display = registry
622 .resolve(sid)
623 .map(|p| p.name().to_string())
624 .unwrap_or_else(|| format!("{:?}", sid));
625 return Some((vars, display));
626 }
627
628 let name = name?;
629
630 let symbol_ids = registry.find_by_name(name);
632 let mut vars = Vec::new();
633 for &sid in &symbol_ids {
634 vars.extend_from_slice(self.vars_in_symbol(sid));
635 }
636 if !vars.is_empty() {
637 return Some((vars, name.to_string()));
638 }
639
640 let vars: Vec<VarId> = self
642 .iter_vars()
643 .filter_map(|(var_id, _)| {
644 self.var_name(var_id).and_then(|n| {
645 if n == name || n.contains(name) {
646 Some(var_id)
647 } else {
648 None
649 }
650 })
651 })
652 .collect();
653 Some((vars, name.to_string()))
654 }
655
656 pub fn iter_flows(&self) -> impl Iterator<Item = (FlowId, &FlowData, &FlowEdgeData)> {
658 self.flows
659 .iter()
660 .zip(self.edges.iter())
661 .enumerate()
662 .map(|(i, (data, edge))| (FlowId::from_raw(i as u32), data, edge))
663 }
664
665 pub fn clear(&mut self) {
667 self.vars.clear();
668 self.var_names.clear();
669 self.flows.clear();
670 self.edges.clear();
671 self.scopes.clear();
672 self.outgoing.clear();
673 self.incoming.clear();
674 self.var_mapping.clear();
675 self.symbol_to_vars.clear();
676 self.borrow_tracker.clear();
677 self.lock_tracker.clear();
678 }
679
680 pub fn clear_for_symbols(&mut self, symbols: &[SymbolId]) {
689 for &symbol_id in symbols {
690 if let Some(var_ids) = self.symbol_to_vars.remove(&symbol_id) {
692 for var_id in var_ids {
693 self.vars.remove(var_id);
695 self.var_names.remove(var_id);
696 self.outgoing.remove(var_id);
697 self.incoming.remove(var_id);
698
699 self.var_mapping.remove(var_id);
701 }
702 }
703 }
704
705 }
709
710 pub fn add_scope(&mut self, data: ScopeData) -> ScopeId {
714 let id = ScopeId::from_raw(self.scopes.len() as u32);
715 self.scopes.push(data);
716 id
717 }
718
719 #[inline]
721 pub fn scope(&self, id: ScopeId) -> Option<&ScopeData> {
722 self.scopes.get(id.as_usize())
723 }
724
725 #[inline]
727 pub fn scope_count(&self) -> usize {
728 self.scopes.len()
729 }
730
731 pub fn is_guarded(&self, var_name: &str, scope: ScopeId, kinds: &[GuardKind]) -> bool {
738 let mut current = Some(scope);
739 while let Some(sid) = current {
740 if let Some(scope_data) = self.scopes.get(sid.as_usize()) {
741 if let Some(guard) = &scope_data.guard {
742 if kinds.contains(&guard.kind) && guard.var_names.iter().any(|n| n == var_name)
743 {
744 return true;
745 }
746 }
747 current = scope_data.parent;
748 } else {
749 break;
750 }
751 }
752 false
753 }
754
755 pub fn active_guards(&self, scope: ScopeId) -> Vec<&Guard> {
757 let mut guards = Vec::new();
758 let mut current = Some(scope);
759 while let Some(sid) = current {
760 if let Some(scope_data) = self.scopes.get(sid.as_usize()) {
761 if let Some(guard) = &scope_data.guard {
762 guards.push(guard);
763 }
764 current = scope_data.parent;
765 } else {
766 break;
767 }
768 }
769 guards
770 }
771
772 pub fn flows_of_kind_in_symbol(
774 &self,
775 symbol_id: SymbolId,
776 kind: FlowKind,
777 ) -> Vec<(FlowId, &FlowData, &FlowEdgeData)> {
778 let var_ids = self.vars_in_symbol(symbol_id);
779 let mut result = Vec::new();
780 for &var_id in var_ids {
781 for &flow_id in self.outgoing(var_id) {
782 if let (Some(flow), Some(edge)) = (self.flow(flow_id), self.edge(flow_id)) {
783 if flow.kind == kind {
784 result.push((flow_id, flow, edge));
785 }
786 }
787 }
788 }
789 result
790 }
791
792 #[inline]
796 pub fn borrow_tracker(&self) -> &BorrowTrackerV2 {
797 &self.borrow_tracker
798 }
799
800 #[inline]
802 pub fn borrow_tracker_mut(&mut self) -> &mut BorrowTrackerV2 {
803 &mut self.borrow_tracker
804 }
805
806 #[inline]
808 pub fn lock_tracker(&self) -> &LockTrackerV2 {
809 &self.lock_tracker
810 }
811
812 #[inline]
814 pub fn lock_tracker_mut(&mut self) -> &mut LockTrackerV2 {
815 &mut self.lock_tracker
816 }
817
818 pub fn add_borrow(&mut self, source: VarId, borrow: VarId, kind: BorrowKind, line: u32) {
822 self.borrow_tracker.add_borrow(source, borrow, kind, line);
823 }
824
825 pub fn end_borrow(&mut self, borrow: VarId, line: u32) {
827 self.borrow_tracker.end_borrow(borrow, line);
828 }
829
830 pub fn borrow_conflicts(
832 &self,
833 source: VarId,
834 kind: BorrowKind,
835 at_line: u32,
836 ) -> Vec<BorrowConflict> {
837 self.borrow_tracker.conflicts(source, kind, at_line)
838 }
839
840 pub fn borrow_analysis(&self, symbol_id: SymbolId) -> BorrowAnalysis {
849 let mut conflicts = Vec::new();
850 let mut move_errors = Vec::new();
851
852 let var_ids = self.vars_in_symbol(symbol_id);
853
854 let mut active_borrows: HashMap<VarId, Vec<(VarId, BorrowKind, u32)>> = HashMap::new();
856 let mut moved_vars: HashMap<VarId, u32> = HashMap::new();
857
858 for &var_id in var_ids {
859 for &flow_id in self.outgoing(var_id) {
860 if let (Some(flow), Some(edge)) = (self.flow(flow_id), self.edge(flow_id)) {
861 match flow.kind {
862 FlowKind::SharedBorrow => {
863 let source = edge.from;
864 let borrower = edge.to;
865 if let Some(borrows) = active_borrows.get(&source) {
866 for &(existing_borrower, existing_kind, existing_line) in borrows {
867 if existing_kind.conflicts_with(BorrowKind::Shared) {
868 conflicts.push(BorrowConflict::new(
869 source,
870 ActiveBorrowV2::new(
871 existing_borrower,
872 existing_kind,
873 existing_line,
874 ),
875 BorrowKind::Shared,
876 flow.line,
877 ));
878 }
879 }
880 }
881 active_borrows.entry(source).or_default().push((
882 borrower,
883 BorrowKind::Shared,
884 flow.line,
885 ));
886 }
887 FlowKind::MutBorrow => {
888 let source = edge.from;
889 let borrower = edge.to;
890 if let Some(borrows) = active_borrows.get(&source) {
891 for &(existing_borrower, existing_kind, existing_line) in borrows {
892 conflicts.push(BorrowConflict::new(
893 source,
894 ActiveBorrowV2::new(
895 existing_borrower,
896 existing_kind,
897 existing_line,
898 ),
899 BorrowKind::Mutable,
900 flow.line,
901 ));
902 }
903 }
904 active_borrows.entry(source).or_default().push((
905 borrower,
906 BorrowKind::Mutable,
907 flow.line,
908 ));
909 }
910 FlowKind::Move => {
911 let source = edge.from;
912 moved_vars.insert(source, flow.line);
913 }
914 FlowKind::Read | FlowKind::Derive => {
915 let source = edge.from;
916 if let Some(&move_line) = moved_vars.get(&source) {
917 if flow.line > move_line {
918 move_errors.push(MoveError {
919 variable: source,
920 moved_at: move_line,
921 used_at: flow.line,
922 });
923 }
924 }
925 }
926 FlowKind::Drop => {
927 let source = edge.from;
928 active_borrows.remove(&source);
929 }
930 _ => {}
931 }
932 }
933 }
934 }
935
936 BorrowAnalysis {
937 conflicts,
938 move_errors,
939 }
940 }
941}
942
943#[cfg(test)]
944mod tests {
945 use super::*;
946 use slotmap::SlotMap;
947
948 fn setup_symbols() -> (
949 SlotMap<SymbolId, &'static str>,
950 SymbolId,
951 SymbolId,
952 SymbolId,
953 ) {
954 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
955 let fn_sym = symbols.insert("my_fn");
956 let x_sym = symbols.insert("x");
957 let y_sym = symbols.insert("y");
958 (symbols, fn_sym, x_sym, y_sym)
959 }
960
961 #[test]
962 fn test_add_var() {
963 let (_symbols, fn_sym, x_sym, _y_sym) = setup_symbols();
964 let mut graph = DataFlowGraphV2::new();
965
966 let var_id = graph.add_var(
967 VarData {
968 parent: fn_sym,
969 kind: VarKind::Local,
970 line: 10,
971 is_mut: false,
972 },
973 "x".to_string(),
974 Some(x_sym),
975 );
976
977 assert_eq!(graph.var_count(), 1);
978 let data = graph.var(var_id).unwrap();
979 assert_eq!(data.parent, fn_sym);
980 assert_eq!(data.kind, VarKind::Local);
981 assert_eq!(data.line, 10);
982 assert_eq!(graph.var_name(var_id), Some("x"));
983 assert_eq!(graph.var_to_symbol(var_id), Some(x_sym));
985 }
986
987 #[test]
988 fn test_add_flow() {
989 let (_symbols, fn_sym, x_sym, y_sym) = setup_symbols();
990 let mut graph = DataFlowGraphV2::new();
991
992 let x = graph.add_var(
993 VarData {
994 parent: fn_sym,
995 kind: VarKind::Local,
996 line: 10,
997 is_mut: false,
998 },
999 "x".to_string(),
1000 Some(x_sym),
1001 );
1002
1003 let y = graph.add_var(
1004 VarData {
1005 parent: fn_sym,
1006 kind: VarKind::Local,
1007 line: 11,
1008 is_mut: false,
1009 },
1010 "y".to_string(),
1011 Some(y_sym),
1012 );
1013
1014 let flow_id = graph.add_flow(
1015 x,
1016 y,
1017 FlowData {
1018 kind: FlowKind::Assign,
1019 line: 11,
1020 scope: ScopeId::from_raw(0),
1021 },
1022 );
1023
1024 assert_eq!(graph.flow_count(), 1);
1025
1026 let edge = graph.edge(flow_id).unwrap();
1027 assert_eq!(edge.from, x);
1028 assert_eq!(edge.to, y);
1029
1030 let flow = graph.flow(flow_id).unwrap();
1031 assert_eq!(flow.kind, FlowKind::Assign);
1032 }
1033
1034 #[test]
1035 fn test_successors_predecessors() {
1036 let (_symbols, fn_sym, x_sym, y_sym) = setup_symbols();
1037 let mut graph = DataFlowGraphV2::new();
1038
1039 let x = graph.add_var(
1040 VarData {
1041 parent: fn_sym,
1042 kind: VarKind::Local,
1043 line: 10,
1044 is_mut: false,
1045 },
1046 "x".to_string(),
1047 Some(x_sym),
1048 );
1049
1050 let y = graph.add_var(
1051 VarData {
1052 parent: fn_sym,
1053 kind: VarKind::Local,
1054 line: 11,
1055 is_mut: false,
1056 },
1057 "y".to_string(),
1058 Some(y_sym),
1059 );
1060
1061 graph.add_flow(
1062 x,
1063 y,
1064 FlowData {
1065 kind: FlowKind::Assign,
1066 line: 11,
1067 scope: ScopeId::from_raw(0),
1068 },
1069 );
1070
1071 let succs: Vec<_> = graph.successors(x).collect();
1073 assert_eq!(succs, vec![y]);
1074
1075 let preds: Vec<_> = graph.predecessors(y).collect();
1076 assert_eq!(preds, vec![x]);
1077 }
1078
1079 #[test]
1080 fn test_vars_in_symbol() {
1081 let (_symbols, fn_sym, x_sym, y_sym) = setup_symbols();
1082 let mut graph = DataFlowGraphV2::new();
1083
1084 let x = graph.add_var(
1085 VarData {
1086 parent: fn_sym,
1087 kind: VarKind::Local,
1088 line: 10,
1089 is_mut: false,
1090 },
1091 "x".to_string(),
1092 Some(x_sym),
1093 );
1094
1095 let y = graph.add_var(
1096 VarData {
1097 parent: fn_sym,
1098 kind: VarKind::Local,
1099 line: 11,
1100 is_mut: false,
1101 },
1102 "y".to_string(),
1103 Some(y_sym),
1104 );
1105
1106 let vars = graph.vars_in_symbol(fn_sym);
1107 assert_eq!(vars.len(), 2);
1108 assert!(vars.contains(&x));
1109 assert!(vars.contains(&y));
1110 }
1111
1112 #[test]
1113 fn test_impact_analysis() {
1114 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
1115 let fn_sym = symbols.insert("my_fn");
1116 let a_sym = symbols.insert("a");
1117 let b_sym = symbols.insert("b");
1118 let c_sym = symbols.insert("c");
1119
1120 let mut graph = DataFlowGraphV2::new();
1121
1122 let a = graph.add_var(
1123 VarData {
1124 parent: fn_sym,
1125 kind: VarKind::Local,
1126 line: 1,
1127 is_mut: false,
1128 },
1129 "a".to_string(),
1130 Some(a_sym),
1131 );
1132 let b = graph.add_var(
1133 VarData {
1134 parent: fn_sym,
1135 kind: VarKind::Local,
1136 line: 2,
1137 is_mut: false,
1138 },
1139 "b".to_string(),
1140 Some(b_sym),
1141 );
1142 let c = graph.add_var(
1143 VarData {
1144 parent: fn_sym,
1145 kind: VarKind::Local,
1146 line: 3,
1147 is_mut: false,
1148 },
1149 "c".to_string(),
1150 Some(c_sym),
1151 );
1152
1153 graph.add_flow(
1155 a,
1156 b,
1157 FlowData {
1158 kind: FlowKind::Assign,
1159 line: 2,
1160 scope: ScopeId::from_raw(0),
1161 },
1162 );
1163 graph.add_flow(
1164 b,
1165 c,
1166 FlowData {
1167 kind: FlowKind::Assign,
1168 line: 3,
1169 scope: ScopeId::from_raw(0),
1170 },
1171 );
1172
1173 let impact = graph.impact(a);
1174 assert_eq!(impact.len(), 3);
1175 assert!(impact.contains(&a));
1176 assert!(impact.contains(&b));
1177 assert!(impact.contains(&c));
1178 }
1179
1180 #[test]
1181 fn test_provenance_analysis() {
1182 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
1183 let fn_sym = symbols.insert("my_fn");
1184 let a_sym = symbols.insert("a");
1185 let b_sym = symbols.insert("b");
1186 let c_sym = symbols.insert("c");
1187
1188 let mut graph = DataFlowGraphV2::new();
1189
1190 let a = graph.add_var(
1191 VarData {
1192 parent: fn_sym,
1193 kind: VarKind::Local,
1194 line: 1,
1195 is_mut: false,
1196 },
1197 "a".to_string(),
1198 Some(a_sym),
1199 );
1200 let b = graph.add_var(
1201 VarData {
1202 parent: fn_sym,
1203 kind: VarKind::Local,
1204 line: 2,
1205 is_mut: false,
1206 },
1207 "b".to_string(),
1208 Some(b_sym),
1209 );
1210 let c = graph.add_var(
1211 VarData {
1212 parent: fn_sym,
1213 kind: VarKind::Local,
1214 line: 3,
1215 is_mut: false,
1216 },
1217 "c".to_string(),
1218 Some(c_sym),
1219 );
1220
1221 graph.add_flow(
1223 a,
1224 b,
1225 FlowData {
1226 kind: FlowKind::Assign,
1227 line: 2,
1228 scope: ScopeId::from_raw(0),
1229 },
1230 );
1231 graph.add_flow(
1232 b,
1233 c,
1234 FlowData {
1235 kind: FlowKind::Assign,
1236 line: 3,
1237 scope: ScopeId::from_raw(0),
1238 },
1239 );
1240
1241 let provenance = graph.provenance(c);
1242 assert_eq!(provenance.len(), 3);
1243 assert!(provenance.contains(&a));
1244 assert!(provenance.contains(&b));
1245 assert!(provenance.contains(&c));
1246 }
1247
1248 #[test]
1249 fn test_add_var_without_symbol_id_produces_distinct_vars() {
1250 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
1251 let fn_sym = symbols.insert("my_fn");
1252
1253 let mut graph = DataFlowGraphV2::new();
1254
1255 let x = graph.add_var(
1257 VarData {
1258 parent: fn_sym,
1259 kind: VarKind::Local,
1260 line: 10,
1261 is_mut: false,
1262 },
1263 "x".to_string(),
1264 None,
1265 );
1266 let y = graph.add_var(
1267 VarData {
1268 parent: fn_sym,
1269 kind: VarKind::Local,
1270 line: 11,
1271 is_mut: false,
1272 },
1273 "y".to_string(),
1274 None,
1275 );
1276
1277 assert_ne!(x, y, "Different variables must get different VarIds");
1279
1280 assert_eq!(graph.var_name(x), Some("x"));
1282 assert_eq!(graph.var_name(y), Some("y"));
1283
1284 assert_eq!(graph.var_count(), 2);
1286
1287 assert_eq!(graph.var_to_symbol(x), None);
1289 assert_eq!(graph.var_to_symbol(y), None);
1290 }
1291
1292 #[test]
1293 fn test_add_var_with_symbol_id_still_works() {
1294 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
1295 let fn_sym = symbols.insert("my_fn");
1296 let x_sym = symbols.insert("x");
1297
1298 let mut graph = DataFlowGraphV2::new();
1299
1300 let var_id = graph.add_var(
1301 VarData {
1302 parent: fn_sym,
1303 kind: VarKind::Local,
1304 line: 10,
1305 is_mut: false,
1306 },
1307 "x".to_string(),
1308 Some(x_sym),
1309 );
1310
1311 assert_eq!(graph.var_count(), 1);
1312 assert_eq!(graph.var_name(var_id), Some("x"));
1313 assert_eq!(graph.var_to_symbol(var_id), Some(x_sym));
1314 }
1315
1316 #[test]
1317 fn test_vars_for_symbol_with_anonymous_vars() {
1318 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
1319 let fn_sym = symbols.insert("my_fn");
1320
1321 let mut graph = DataFlowGraphV2::new();
1322
1323 let x = graph.add_var(
1324 VarData {
1325 parent: fn_sym,
1326 kind: VarKind::Local,
1327 line: 10,
1328 is_mut: false,
1329 },
1330 "x".to_string(),
1331 None,
1332 );
1333 let y = graph.add_var(
1334 VarData {
1335 parent: fn_sym,
1336 kind: VarKind::Local,
1337 line: 11,
1338 is_mut: false,
1339 },
1340 "y".to_string(),
1341 None,
1342 );
1343
1344 let vars = graph.vars_in_symbol(fn_sym);
1346 assert_eq!(vars.len(), 2);
1347 assert!(vars.contains(&x));
1348 assert!(vars.contains(&y));
1349 }
1350}