1use std::fmt;
19
20use serde::{Deserialize, Serialize};
21
22use super::super::file::id::FileId;
23use super::super::node::id::{GenerationOverflowError, NodeId};
24use super::super::node::kind::NodeKind;
25use super::super::string::id::StringId;
26use crate::graph::body_hash::BodyHash128;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum ArenaError {
34 GenerationOverflow(GenerationOverflowError),
36
37 FreeListCorruption {
43 index: u32,
45 },
46
47 CapacityExceeded,
49}
50
51impl fmt::Display for ArenaError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 ArenaError::GenerationOverflow(e) => write!(f, "{e}"),
55 ArenaError::FreeListCorruption { index } => {
56 write!(
57 f,
58 "free list corruption: occupied slot found at index {index}"
59 )
60 }
61 ArenaError::CapacityExceeded => {
62 write!(
63 f,
64 "arena capacity exceeded: cannot allocate more than {} nodes",
65 u32::MAX
66 )
67 }
68 }
69 }
70}
71
72impl std::error::Error for ArenaError {
73 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74 match self {
75 ArenaError::GenerationOverflow(e) => Some(e),
76 _ => None,
77 }
78 }
79}
80
81impl From<GenerationOverflowError> for ArenaError {
82 fn from(e: GenerationOverflowError) -> Self {
83 ArenaError::GenerationOverflow(e)
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub enum SlotState<T> {
90 Occupied(T),
92 Vacant {
94 next_free: Option<u32>,
96 },
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct Slot<T> {
102 generation: u64,
104 state: SlotState<T>,
106}
107
108impl<T> Slot<T> {
109 #[allow(dead_code)] pub(crate) fn new_vacant(generation: u64, next_free: Option<u32>) -> Self {
112 Self {
113 generation,
114 state: SlotState::Vacant { next_free },
115 }
116 }
117
118 pub(crate) fn new_occupied(generation: u64, data: T) -> Self {
120 Self {
121 generation,
122 state: SlotState::Occupied(data),
123 }
124 }
125
126 #[inline]
128 pub fn is_occupied(&self) -> bool {
129 matches!(self.state, SlotState::Occupied(_))
130 }
131
132 #[inline]
134 pub fn is_vacant(&self) -> bool {
135 matches!(self.state, SlotState::Vacant { .. })
136 }
137
138 #[inline]
140 pub fn generation(&self) -> u64 {
141 self.generation
142 }
143
144 pub fn get(&self) -> Option<&T> {
146 match &self.state {
147 SlotState::Occupied(data) => Some(data),
148 SlotState::Vacant { .. } => None,
149 }
150 }
151
152 #[inline]
154 pub fn state(&self) -> &SlotState<T> {
155 &self.state
156 }
157
158 pub fn get_mut(&mut self) -> Option<&mut T> {
160 match &mut self.state {
161 SlotState::Occupied(data) => Some(data),
162 SlotState::Vacant { .. } => None,
163 }
164 }
165}
166
167#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct NodeEntry {
173 pub kind: NodeKind,
175 pub name: StringId,
177 pub file: FileId,
179 pub start_byte: u32,
181 pub end_byte: u32,
183 pub start_line: u32,
185 pub start_column: u32,
187 pub end_line: u32,
189 pub end_column: u32,
191 pub signature: Option<StringId>,
193 pub doc: Option<StringId>,
195 pub qualified_name: Option<StringId>,
197 pub visibility: Option<StringId>,
199 pub is_async: bool,
201 pub is_static: bool,
203 #[serde(default)]
213 pub body_hash: Option<BodyHash128>,
214 #[serde(default)]
225 pub is_unsafe: bool,
226 #[serde(default)]
240 pub is_definition: bool,
241}
242
243impl NodeEntry {
244 #[must_use]
246 pub fn new(kind: NodeKind, name: StringId, file: FileId) -> Self {
247 Self {
248 kind,
249 name,
250 file,
251 start_byte: 0,
252 end_byte: 0,
253 start_line: 0,
254 start_column: 0,
255 end_line: 0,
256 end_column: 0,
257 signature: None,
258 doc: None,
259 qualified_name: None,
260 visibility: None,
261 is_async: false,
262 is_static: false,
263 is_unsafe: false,
264 body_hash: None,
265 is_definition: false,
266 }
267 }
268
269 #[must_use]
271 pub fn with_byte_range(mut self, start: u32, end: u32) -> Self {
272 self.start_byte = start;
273 self.end_byte = end;
274 self
275 }
276
277 #[must_use]
279 pub fn with_location(
280 mut self,
281 start_line: u32,
282 start_column: u32,
283 end_line: u32,
284 end_column: u32,
285 ) -> Self {
286 self.start_line = start_line;
287 self.start_column = start_column;
288 self.end_line = end_line;
289 self.end_column = end_column;
290 self
291 }
292
293 #[inline]
313 #[must_use]
314 pub fn is_unified_loser(&self) -> bool {
315 self.name == StringId::INVALID
316 }
317
318 #[inline]
324 #[must_use]
325 pub fn is_definition(&self) -> bool {
326 self.is_definition
327 }
328
329 #[inline]
368 #[must_use]
369 pub fn is_synthetic_placeholder_name(display_name: &str) -> bool {
370 is_synthetic_placeholder_name_shape(display_name)
371 }
372
373 #[must_use]
375 pub fn with_signature(mut self, signature: StringId) -> Self {
376 self.signature = Some(signature);
377 self
378 }
379
380 #[must_use]
382 pub fn with_doc(mut self, doc: StringId) -> Self {
383 self.doc = Some(doc);
384 self
385 }
386
387 #[must_use]
389 pub fn with_qualified_name(mut self, qualified_name: StringId) -> Self {
390 self.qualified_name = Some(qualified_name);
391 self
392 }
393
394 #[must_use]
396 pub fn with_visibility(mut self, visibility: StringId) -> Self {
397 self.visibility = Some(visibility);
398 self
399 }
400
401 #[must_use]
403 pub fn with_async(mut self, is_async: bool) -> Self {
404 self.is_async = is_async;
405 self
406 }
407
408 #[must_use]
410 pub fn with_static(mut self, is_static: bool) -> Self {
411 self.is_static = is_static;
412 self
413 }
414
415 #[must_use]
417 pub fn with_unsafe(mut self, is_unsafe: bool) -> Self {
418 self.is_unsafe = is_unsafe;
419 self
420 }
421
422 #[must_use]
428 pub fn with_definition(mut self, is_definition: bool) -> Self {
429 self.is_definition = is_definition;
430 self
431 }
432
433 #[must_use]
438 pub fn with_body_hash(mut self, hash: BodyHash128) -> Self {
439 self.body_hash = Some(hash);
440 self
441 }
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct NodeArena {
468 slots: Vec<Slot<NodeEntry>>,
470 free_head: Option<u32>,
472 len: usize,
474}
475
476impl NodeArena {
477 #[must_use]
479 pub fn new() -> Self {
480 Self {
481 slots: Vec::new(),
482 free_head: None,
483 len: 0,
484 }
485 }
486
487 #[must_use]
489 pub fn with_capacity(capacity: usize) -> Self {
490 Self {
491 slots: Vec::with_capacity(capacity),
492 free_head: None,
493 len: 0,
494 }
495 }
496
497 #[must_use]
505 pub(crate) fn from_raw_parts(
506 slots: Vec<Slot<NodeEntry>>,
507 free_head: Option<u32>,
508 len: usize,
509 ) -> Self {
510 Self {
511 slots,
512 free_head,
513 len,
514 }
515 }
516
517 #[inline]
519 #[must_use]
520 pub fn len(&self) -> usize {
521 self.len
522 }
523
524 #[inline]
526 #[must_use]
527 pub fn is_empty(&self) -> bool {
528 self.len == 0
529 }
530
531 #[inline]
533 #[must_use]
534 pub fn capacity(&self) -> usize {
535 self.slots.capacity()
536 }
537
538 #[inline]
543 #[must_use]
544 pub fn slot_count(&self) -> usize {
545 self.slots.len()
546 }
547
548 pub fn alloc(&mut self, entry: NodeEntry) -> Result<NodeId, ArenaError> {
560 self.len += 1;
561
562 if let Some(free_idx) = self.free_head {
563 let slot_index = free_idx as usize;
565 if slot_index >= self.slots.len() {
566 self.len -= 1; return Err(ArenaError::FreeListCorruption { index: free_idx });
568 }
569
570 let slot = &mut self.slots[slot_index];
572
573 self.free_head = match &slot.state {
575 SlotState::Vacant { next_free } => *next_free,
576 SlotState::Occupied(_) => {
577 self.len -= 1; return Err(ArenaError::FreeListCorruption { index: free_idx });
580 }
581 };
582
583 let temp_id = NodeId::new(free_idx, slot.generation);
586 let new_generation = temp_id.try_increment_generation().map_err(|mut e| {
587 self.len -= 1; e.index = free_idx; ArenaError::GenerationOverflow(e)
590 })?;
591
592 slot.generation = new_generation;
593 slot.state = SlotState::Occupied(entry);
594
595 Ok(NodeId::new(free_idx, new_generation))
596 } else {
597 let index = self.slots.len();
599
600 let Ok(index_u32) = u32::try_from(index) else {
602 self.len -= 1; return Err(ArenaError::CapacityExceeded);
604 };
605
606 let slot = Slot::new_occupied(1, entry);
607 self.slots.push(slot);
608
609 Ok(NodeId::new(index_u32, 1))
610 }
611 }
612
613 #[must_use]
617 pub fn get(&self, id: NodeId) -> Option<&NodeEntry> {
618 if id.is_invalid() {
619 return None;
620 }
621
622 let index = id.index() as usize;
623 let slot = self.slots.get(index)?;
624
625 if slot.generation != id.generation() {
627 return None;
628 }
629
630 slot.get()
631 }
632
633 #[must_use]
637 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut NodeEntry> {
638 if id.is_invalid() {
639 return None;
640 }
641
642 let index = id.index() as usize;
643 let slot = self.slots.get_mut(index)?;
644
645 if slot.generation != id.generation() {
647 return None;
648 }
649
650 slot.get_mut()
651 }
652
653 #[must_use]
655 pub fn contains(&self, id: NodeId) -> bool {
656 self.get(id).is_some()
657 }
658
659 pub fn remove(&mut self, id: NodeId) -> Option<NodeEntry> {
665 if id.is_invalid() {
666 return None;
667 }
668
669 let index = id.index() as usize;
670 let slot = self.slots.get_mut(index)?;
671
672 if slot.generation != id.generation() {
674 return None;
675 }
676
677 if let SlotState::Occupied(_) = &slot.state {
680 let old_state = std::mem::replace(
681 &mut slot.state,
682 SlotState::Vacant {
683 next_free: self.free_head,
684 },
685 );
686
687 self.free_head = Some(id.index());
689 self.len -= 1;
690
691 if let SlotState::Occupied(entry) = old_state {
692 return Some(entry);
693 }
694 }
695
696 None
697 }
698
699 pub fn iter(&self) -> impl Iterator<Item = (NodeId, &NodeEntry)> {
701 self.slots.iter().enumerate().filter_map(|(index, slot)| {
702 if let SlotState::Occupied(entry) = &slot.state {
703 let index_u32 = u32::try_from(index).ok()?;
704 Some((NodeId::new(index_u32, slot.generation), entry))
705 } else {
706 None
707 }
708 })
709 }
710
711 pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut NodeEntry)> {
713 self.slots
714 .iter_mut()
715 .enumerate()
716 .filter_map(|(index, slot)| {
717 let generation = slot.generation;
718 if let SlotState::Occupied(entry) = &mut slot.state {
719 let index_u32 = u32::try_from(index).ok()?;
720 Some((NodeId::new(index_u32, generation), entry))
721 } else {
722 None
723 }
724 })
725 }
726
727 pub fn clear(&mut self) {
731 self.slots.clear();
732 self.free_head = None;
733 self.len = 0;
734 }
735
736 pub fn reserve(&mut self, additional: usize) {
738 self.slots.reserve(additional);
739 }
740
741 #[must_use]
745 pub fn slot(&self, index: u32) -> Option<&Slot<NodeEntry>> {
746 self.slots.get(index as usize)
747 }
748
749 pub fn alloc_range(&mut self, count: u32, placeholder: &NodeEntry) -> Result<u32, ArenaError> {
763 if count == 0 {
764 return Ok(u32::try_from(self.slots.len())
767 .unwrap_or_else(|_| unreachable!("slot_count invariant violated")));
768 }
769
770 let start = self.slots.len();
771 let new_total = start
772 .checked_add(count as usize)
773 .ok_or(ArenaError::CapacityExceeded)?;
774
775 if new_total > u32::MAX as usize + 1 {
777 return Err(ArenaError::CapacityExceeded);
778 }
779
780 let start_u32 = u32::try_from(start).map_err(|_| ArenaError::CapacityExceeded)?;
781
782 self.slots.reserve(count as usize);
784 for _ in 0..count {
785 self.slots.push(Slot::new_occupied(1, placeholder.clone()));
786 }
787
788 self.len += count as usize;
790
791 Ok(start_u32)
792 }
793
794 #[must_use]
803 pub fn bulk_slice_mut(&mut self, start: u32, count: u32) -> &mut [Slot<NodeEntry>] {
804 let begin = start as usize;
805 let end = begin + count as usize;
806 &mut self.slots[begin..end]
807 }
808
809 pub fn truncate_to(&mut self, saved_slot_count: usize) {
820 assert!(
821 saved_slot_count <= self.slots.len(),
822 "truncate_to({saved_slot_count}) exceeds current slot count ({})",
823 self.slots.len(),
824 );
825
826 let dropped_occupied = self.slots[saved_slot_count..]
828 .iter()
829 .filter(|s| s.is_occupied())
830 .count();
831
832 self.slots.truncate(saved_slot_count);
833 self.len -= dropped_occupied;
834
835 self.rebuild_free_list_after_truncate(saved_slot_count);
838 }
839
840 fn rebuild_free_list_after_truncate(&mut self, cutoff: usize) {
846 let mut new_head: Option<u32> = None;
850 for i in (0..cutoff).rev() {
853 if self.slots[i].is_vacant() {
854 let i_u32 = u32::try_from(i)
855 .unwrap_or_else(|_| unreachable!("free-list index exceeds u32 invariant"));
856 self.slots[i].state = SlotState::Vacant {
857 next_free: new_head,
858 };
859 new_head = Some(i_u32);
860 }
861 }
862 self.free_head = new_head;
863 }
864
865 #[must_use]
870 pub fn slots(&self) -> &[Slot<NodeEntry>] {
871 &self.slots
872 }
873}
874
875impl Default for NodeArena {
876 fn default() -> Self {
877 Self::new()
878 }
879}
880
881impl fmt::Display for NodeArena {
882 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
883 write!(
884 f,
885 "NodeArena(len={}, capacity={})",
886 self.len,
887 self.capacity()
888 )
889 }
890}
891
892impl crate::graph::unified::memory::GraphMemorySize for NodeArena {
893 fn heap_bytes(&self) -> usize {
894 self.slots.capacity() * std::mem::size_of::<Slot<NodeEntry>>()
895 }
896}
897
898fn is_synthetic_placeholder_name_shape(name: &str) -> bool {
912 if name.starts_with('<') {
913 return true;
914 }
915 if let Some((_, rest)) = name.rsplit_once('@')
917 && !rest.is_empty()
918 && rest.bytes().all(|b| b.is_ascii_digit())
919 {
920 return true;
921 }
922 false
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928
929 fn test_file() -> FileId {
930 FileId::new(1)
931 }
932
933 fn test_name() -> StringId {
934 StringId::new(1)
935 }
936
937 fn test_entry(kind: NodeKind) -> NodeEntry {
938 NodeEntry::new(kind, test_name(), test_file())
939 }
940
941 #[test]
942 fn test_arena_new() {
943 let arena = NodeArena::new();
944 assert_eq!(arena.len(), 0);
945 assert!(arena.is_empty());
946 assert_eq!(arena.capacity(), 0);
947 }
948
949 #[test]
950 fn synthetic_placeholder_name_shape_recognises_known_synthetics() {
951 assert!(is_synthetic_placeholder_name_shape("<field:s.NeedTags>"));
953 assert!(is_synthetic_placeholder_name_shape(
954 "<field:selector.NeedTags>"
955 ));
956 assert!(is_synthetic_placeholder_name_shape("<type:main.Foo>"));
958 assert!(is_synthetic_placeholder_name_shape("NeedTags@469"));
960 assert!(is_synthetic_placeholder_name_shape("NeedTags@508"));
961 assert!(is_synthetic_placeholder_name_shape("foo@0"));
962 assert!(NodeEntry::is_synthetic_placeholder_name("<field:x.y>"));
964 assert!(NodeEntry::is_synthetic_placeholder_name("x@123"));
965 }
966
967 #[test]
968 fn synthetic_placeholder_name_shape_passes_real_identifiers() {
969 assert!(!is_synthetic_placeholder_name_shape("NeedTags"));
971 assert!(!is_synthetic_placeholder_name_shape(
972 "main.SelectorSource.NeedTags"
973 ));
974 assert!(!is_synthetic_placeholder_name_shape("main.useSelector"));
975 assert!(!is_synthetic_placeholder_name_shape("foo"));
976 assert!(!is_synthetic_placeholder_name_shape(""));
977 assert!(!is_synthetic_placeholder_name_shape("foo@bar"));
979 assert!(!is_synthetic_placeholder_name_shape("foo@"));
981 }
982
983 #[test]
984 fn test_arena_with_capacity() {
985 let arena = NodeArena::with_capacity(100);
986 assert_eq!(arena.len(), 0);
987 assert!(arena.capacity() >= 100);
988 }
989
990 #[test]
991 fn test_alloc_and_get() {
992 let mut arena = NodeArena::new();
993 let entry = test_entry(NodeKind::Function);
994
995 let id = arena.alloc(entry).unwrap();
996 assert!(!id.is_invalid());
997 assert_eq!(arena.len(), 1);
998
999 let node = arena.get(id).unwrap();
1000 assert_eq!(node.kind, NodeKind::Function);
1001 }
1002
1003 #[test]
1004 fn test_alloc_multiple() {
1005 let mut arena = NodeArena::new();
1006
1007 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1008 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1009 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
1010
1011 assert_eq!(arena.len(), 3);
1012 assert_ne!(id1, id2);
1013 assert_ne!(id2, id3);
1014
1015 assert_eq!(arena.get(id1).unwrap().kind, NodeKind::Function);
1016 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Class);
1017 assert_eq!(arena.get(id3).unwrap().kind, NodeKind::Method);
1018 }
1019
1020 #[test]
1021 fn test_get_mut() {
1022 let mut arena = NodeArena::new();
1023 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1024
1025 {
1026 let node = arena.get_mut(id).unwrap();
1027 node.start_line = 42;
1028 }
1029
1030 assert_eq!(arena.get(id).unwrap().start_line, 42);
1031 }
1032
1033 #[test]
1034 fn test_contains() {
1035 let mut arena = NodeArena::new();
1036 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1037
1038 assert!(arena.contains(id));
1039 assert!(!arena.contains(NodeId::INVALID));
1040 }
1041
1042 #[test]
1043 fn test_remove() {
1044 let mut arena = NodeArena::new();
1045 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1046
1047 assert_eq!(arena.len(), 1);
1048 assert!(arena.contains(id));
1049
1050 let removed = arena.remove(id).unwrap();
1051 assert_eq!(removed.kind, NodeKind::Function);
1052 assert_eq!(arena.len(), 0);
1053 assert!(!arena.contains(id));
1054 }
1055
1056 #[test]
1057 fn test_stale_id_after_remove() {
1058 let mut arena = NodeArena::new();
1059 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1060
1061 arena.remove(id1);
1063 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1064
1065 assert!(arena.get(id1).is_none());
1067 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Class);
1069 assert_eq!(id1.index(), id2.index());
1071 assert_ne!(id1.generation(), id2.generation());
1072 }
1073
1074 #[test]
1075 fn test_remove_idempotent() {
1076 let mut arena = NodeArena::new();
1077 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1078 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1079
1080 assert!(arena.remove(id1).is_some());
1082 assert_eq!(arena.len(), 1);
1083
1084 assert!(arena.remove(id1).is_none());
1086 assert_eq!(arena.len(), 1); assert!(arena.remove(id1).is_none());
1090 assert_eq!(arena.len(), 1); assert!(arena.contains(id2));
1094
1095 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
1097 assert_eq!(id3.index(), id1.index()); assert_eq!(arena.len(), 2);
1099 }
1100
1101 #[test]
1102 fn test_free_list_reuse() {
1103 let mut arena = NodeArena::new();
1104
1105 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1107 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1108 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
1109
1110 arena.remove(id2);
1112 assert_eq!(arena.len(), 2);
1113 assert_eq!(arena.slot_count(), 3);
1114
1115 let id4 = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
1117 assert_eq!(id4.index(), id2.index());
1118 assert_eq!(arena.len(), 3);
1119 assert_eq!(arena.slot_count(), 3); assert!(arena.get(id2).is_none());
1123 assert_eq!(arena.get(id4).unwrap().kind, NodeKind::Variable);
1125 assert!(arena.contains(id1));
1127 assert!(arena.contains(id3));
1128 }
1129
1130 #[test]
1131 fn test_invalid_id() {
1132 let arena = NodeArena::new();
1133 assert!(arena.get(NodeId::INVALID).is_none());
1134 }
1135
1136 #[test]
1137 fn test_out_of_bounds_id() {
1138 let arena = NodeArena::new();
1139 let fake_id = NodeId::new(999, 1);
1140 assert!(arena.get(fake_id).is_none());
1141 }
1142
1143 #[test]
1144 fn test_wrong_generation() {
1145 let mut arena = NodeArena::new();
1146 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1147
1148 let wrong_gen_id = NodeId::new(id.index(), id.generation() + 1);
1150 assert!(arena.get(wrong_gen_id).is_none());
1151 }
1152
1153 #[test]
1154 fn test_iter() {
1155 let mut arena = NodeArena::new();
1156 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1157 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1158 arena.alloc(test_entry(NodeKind::Method)).unwrap();
1159
1160 let entries: Vec<_> = arena.iter().collect();
1161 assert_eq!(entries.len(), 3);
1162
1163 let kinds: Vec<_> = entries.iter().map(|(_, e)| e.kind).collect();
1164 assert!(kinds.contains(&NodeKind::Function));
1165 assert!(kinds.contains(&NodeKind::Class));
1166 assert!(kinds.contains(&NodeKind::Method));
1167 }
1168
1169 #[test]
1170 fn test_iter_with_removed() {
1171 let mut arena = NodeArena::new();
1172 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1173 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1174 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
1175
1176 arena.remove(id2);
1177
1178 let entries: Vec<_> = arena.iter().collect();
1179 assert_eq!(entries.len(), 2);
1180
1181 let collected_ids: Vec<_> = entries.iter().map(|(id, _)| *id).collect();
1182 assert!(collected_ids.contains(&id1));
1183 assert!(!collected_ids.contains(&id2));
1184 assert!(collected_ids.contains(&id3));
1185 }
1186
1187 #[test]
1188 fn test_iter_mut() {
1189 let mut arena = NodeArena::new();
1190 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1191 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1192
1193 for (_, entry) in arena.iter_mut() {
1194 entry.start_line = 100;
1195 }
1196
1197 for (_, entry) in arena.iter() {
1198 assert_eq!(entry.start_line, 100);
1199 }
1200 }
1201
1202 #[test]
1203 fn test_clear() {
1204 let mut arena = NodeArena::new();
1205 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1206 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1207
1208 assert_eq!(arena.len(), 2);
1209 arena.clear();
1210 assert_eq!(arena.len(), 0);
1211 assert!(arena.is_empty());
1212 }
1213
1214 #[test]
1215 fn test_reserve() {
1216 let mut arena = NodeArena::new();
1217 arena.reserve(1000);
1218 assert!(arena.capacity() >= 1000);
1219 }
1220
1221 #[test]
1222 fn test_display() {
1223 let mut arena = NodeArena::new();
1224 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1225
1226 let display = format!("{arena}");
1227 assert!(display.contains("NodeArena"));
1228 assert!(display.contains("len=1"));
1229 }
1230
1231 #[test]
1232 fn test_node_entry_builder() {
1233 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file())
1234 .with_byte_range(10, 100)
1235 .with_location(1, 0, 5, 10)
1236 .with_signature(StringId::new(2))
1237 .with_doc(StringId::new(3))
1238 .with_qualified_name(StringId::new(4));
1239
1240 assert_eq!(entry.kind, NodeKind::Function);
1241 assert_eq!(entry.start_byte, 10);
1242 assert_eq!(entry.end_byte, 100);
1243 assert_eq!(entry.start_line, 1);
1244 assert_eq!(entry.start_column, 0);
1245 assert_eq!(entry.end_line, 5);
1246 assert_eq!(entry.end_column, 10);
1247 assert_eq!(entry.signature, Some(StringId::new(2)));
1248 assert_eq!(entry.doc, Some(StringId::new(3)));
1249 assert_eq!(entry.qualified_name, Some(StringId::new(4)));
1250 }
1251
1252 #[test]
1253 fn test_slot_state() {
1254 let occupied: Slot<i32> = Slot::new_occupied(1, 42);
1255 assert!(occupied.is_occupied());
1256 assert!(!occupied.is_vacant());
1257 assert_eq!(occupied.get(), Some(&42));
1258
1259 let vacant: Slot<i32> = Slot::new_vacant(2, Some(5));
1260 assert!(!vacant.is_occupied());
1261 assert!(vacant.is_vacant());
1262 assert_eq!(vacant.get(), None);
1263 }
1264
1265 #[test]
1266 fn test_slot_generation() {
1267 let slot: Slot<i32> = Slot::new_occupied(42, 100);
1268 assert_eq!(slot.generation(), 42);
1269 }
1270
1271 #[test]
1272 fn test_slot_state_accessor() {
1273 let occupied: Slot<i32> = Slot::new_occupied(1, 42);
1274 assert!(matches!(occupied.state(), SlotState::Occupied(42)));
1275
1276 let vacant: Slot<i32> = Slot::new_vacant(1, Some(3));
1277 assert!(matches!(
1278 vacant.state(),
1279 SlotState::Vacant { next_free: Some(3) }
1280 ));
1281 }
1282
1283 #[test]
1286 fn test_alloc_range_basic() {
1287 let mut arena = NodeArena::new();
1288 let placeholder = test_entry(NodeKind::Function);
1289
1290 let start = arena.alloc_range(5, &placeholder).unwrap();
1291 assert_eq!(start, 0);
1292 assert_eq!(arena.len(), 5);
1293 assert_eq!(arena.slot_count(), 5);
1294
1295 for i in 0..5u32 {
1297 let id = NodeId::new(i, 1);
1298 let entry = arena.get(id).expect("slot should be occupied");
1299 assert_eq!(entry.kind, NodeKind::Function);
1300 }
1301 }
1302
1303 #[test]
1304 fn test_alloc_range_after_existing() {
1305 let mut arena = NodeArena::new();
1306
1307 let id0 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1309 assert_eq!(id0.index(), 0);
1310 assert_eq!(arena.len(), 1);
1311
1312 let placeholder = test_entry(NodeKind::Method);
1314 let start = arena.alloc_range(3, &placeholder).unwrap();
1315 assert_eq!(start, 1);
1316 assert_eq!(arena.len(), 4);
1317 assert_eq!(arena.slot_count(), 4);
1318
1319 assert_eq!(arena.get(id0).unwrap().kind, NodeKind::Class);
1321
1322 for i in 1..4u32 {
1324 let id = NodeId::new(i, 1);
1325 assert_eq!(arena.get(id).unwrap().kind, NodeKind::Method);
1326 }
1327 }
1328
1329 #[test]
1330 fn test_alloc_range_zero_is_noop() {
1331 let mut arena = NodeArena::new();
1332
1333 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1335 let len_before = arena.len();
1336 let slot_count_before = arena.slot_count();
1337
1338 let start = arena.alloc_range(0, &test_entry(NodeKind::Class)).unwrap();
1339 #[allow(clippy::cast_possible_truncation)]
1340 let expected_start = slot_count_before as u32;
1342 assert_eq!(start, expected_start);
1343 assert_eq!(arena.len(), len_before);
1344 assert_eq!(arena.slot_count(), slot_count_before);
1345 }
1346
1347 #[test]
1348 fn test_bulk_slice_mut() {
1349 let mut arena = NodeArena::new();
1350 let placeholder = test_entry(NodeKind::Function);
1351 let start = arena.alloc_range(3, &placeholder).unwrap();
1352
1353 {
1355 let slice = arena.bulk_slice_mut(start, 3);
1356 assert_eq!(slice.len(), 3);
1357
1358 slice[1] = Slot::new_occupied(1, test_entry(NodeKind::Class));
1360 }
1361
1362 let id1 = NodeId::new(1, 1);
1364 assert_eq!(arena.get(id1).unwrap().kind, NodeKind::Class);
1365
1366 let id0 = NodeId::new(0, 1);
1368 assert_eq!(arena.get(id0).unwrap().kind, NodeKind::Function);
1369 let id2 = NodeId::new(2, 1);
1370 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Function);
1371 }
1372
1373 #[test]
1374 fn test_truncate_to() {
1375 let mut arena = NodeArena::new();
1376 let placeholder = test_entry(NodeKind::Function);
1377
1378 arena.alloc_range(3, &placeholder).unwrap();
1380 assert_eq!(arena.len(), 3);
1381 assert_eq!(arena.slot_count(), 3);
1382
1383 let saved = arena.slot_count();
1385
1386 arena.alloc_range(4, &placeholder).unwrap();
1388 assert_eq!(arena.len(), 7);
1389 assert_eq!(arena.slot_count(), 7);
1390
1391 arena.truncate_to(saved);
1393 assert_eq!(arena.len(), 3);
1394 assert_eq!(arena.slot_count(), 3);
1395
1396 for i in 0..3u32 {
1398 let id = NodeId::new(i, 1);
1399 assert!(arena.get(id).is_some());
1400 }
1401 }
1402
1403 #[test]
1404 fn test_alloc_range_with_free_list() {
1405 let mut arena = NodeArena::new();
1406
1407 let id0 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1409 let _id1 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1410 arena.remove(id0);
1411
1412 assert_eq!(arena.len(), 1);
1414 assert_eq!(arena.slot_count(), 2);
1415
1416 let start = arena.alloc_range(3, &test_entry(NodeKind::Method)).unwrap();
1418 assert_eq!(start, 2); assert_eq!(arena.len(), 4); assert_eq!(arena.slot_count(), 5); let slot0 = arena.slot(0).unwrap();
1424 assert!(slot0.is_vacant());
1425
1426 for i in 2..5u32 {
1428 let id = NodeId::new(i, 1);
1429 assert_eq!(arena.get(id).unwrap().kind, NodeKind::Method);
1430 }
1431 }
1432
1433 #[test]
1434 fn test_truncate_to_clamps_free_head() {
1435 let mut arena = NodeArena::new();
1436
1437 let mut ids = Vec::new();
1439 for _ in 0..5 {
1440 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1441 }
1442
1443 let saved = arena.slot_count();
1445
1446 let extra_ids: Vec<_> = (0..3)
1448 .map(|_| arena.alloc(test_entry(NodeKind::Class)).unwrap())
1449 .collect();
1450 arena.remove(extra_ids[1]); arena.truncate_to(saved);
1454 assert_eq!(arena.slot_count(), 5);
1455 assert_eq!(arena.len(), 5);
1456
1457 let new_id = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
1459 assert_eq!(new_id.index(), 5); assert_eq!(arena.get(new_id).unwrap().kind, NodeKind::Variable);
1461 }
1462
1463 #[test]
1464 fn test_truncate_to_preserves_retained_free_list() {
1465 let mut arena = NodeArena::new();
1466
1467 let mut ids = Vec::new();
1469 for _ in 0..8 {
1470 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1471 }
1472 assert_eq!(arena.len(), 8);
1473
1474 arena.remove(ids[2]);
1477 arena.remove(ids[6]);
1478 assert_eq!(arena.len(), 6);
1479
1480 arena.truncate_to(5);
1484
1485 assert_eq!(arena.slot_count(), 5);
1489 assert_eq!(arena.len(), 4);
1490
1491 let reused = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
1494 assert_eq!(reused.index(), 2);
1495 assert_eq!(arena.get(reused).unwrap().kind, NodeKind::Variable);
1496 assert_eq!(arena.len(), 5);
1497
1498 let appended = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1500 assert_eq!(appended.index(), 5);
1501 assert_eq!(arena.len(), 6);
1502 }
1503
1504 #[test]
1505 fn test_slots_read_access() {
1506 let mut arena = NodeArena::new();
1507 let placeholder = test_entry(NodeKind::Variable);
1508 arena.alloc_range(4, &placeholder).unwrap();
1509
1510 let slots = arena.slots();
1511 assert_eq!(slots.len(), 4);
1512
1513 for slot in slots {
1514 assert!(slot.is_occupied());
1515 assert_eq!(slot.generation(), 1);
1516 assert_eq!(slot.get().unwrap().kind, NodeKind::Variable);
1517 }
1518 }
1519
1520 #[test]
1521 fn test_multiple_remove_reuse_cycle() {
1522 let mut arena = NodeArena::new();
1523
1524 let mut ids = Vec::new();
1526 for _ in 0..5 {
1527 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1528 }
1529
1530 for &id in ids.iter().rev() {
1532 arena.remove(id);
1533 }
1534
1535 let new_ids: Vec<_> = (0..5)
1537 .map(|_| arena.alloc(test_entry(NodeKind::Class)).unwrap())
1538 .collect();
1539
1540 for &old_id in &ids {
1542 assert!(arena.get(old_id).is_none());
1543 }
1544
1545 for &new_id in &new_ids {
1547 assert!(arena.get(new_id).is_some());
1548 }
1549 }
1550
1551 #[test]
1552 fn test_generation_overflow_at_max_generation() {
1553 let mut arena = NodeArena::new();
1555
1556 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1558 assert_eq!(id.index(), 0);
1559
1560 arena.remove(id);
1562
1563 arena.slots[0].generation = NodeId::MAX_GENERATION;
1566
1567 let result = arena.alloc(test_entry(NodeKind::Class));
1569 assert!(result.is_err());
1570
1571 let err = result.unwrap_err();
1572 match err {
1573 ArenaError::GenerationOverflow(inner) => {
1574 assert_eq!(inner.index, 0);
1575 assert_eq!(inner.generation, NodeId::MAX_GENERATION);
1576 }
1577 _ => panic!("expected GenerationOverflow, got {err:?}"),
1578 }
1579 }
1580
1581 #[test]
1582 fn test_free_list_corruption_returns_error() {
1583 let mut arena = NodeArena::new();
1585
1586 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1588 arena.remove(id);
1589
1590 arena.slots[0].state = SlotState::Occupied(test_entry(NodeKind::Class));
1592
1593 let result = arena.alloc(test_entry(NodeKind::Method));
1595 assert!(result.is_err());
1596
1597 let err = result.unwrap_err();
1598 match err {
1599 ArenaError::FreeListCorruption { index } => {
1600 assert_eq!(index, 0);
1601 }
1602 _ => panic!("expected FreeListCorruption, got {err:?}"),
1603 }
1604
1605 assert_eq!(arena.len(), 0);
1607 }
1608
1609 #[test]
1610 fn test_arena_error_display() {
1611 let gen_err = ArenaError::GenerationOverflow(GenerationOverflowError {
1612 index: 42,
1613 generation: 100,
1614 });
1615 let display = format!("{gen_err}");
1616 assert!(display.contains("42"));
1617 assert!(display.contains("100"));
1618
1619 let corruption_err = ArenaError::FreeListCorruption { index: 5 };
1620 let display = format!("{corruption_err}");
1621 assert!(display.contains("free list corruption"));
1622 assert!(display.contains('5'));
1623
1624 let capacity_err = ArenaError::CapacityExceeded;
1625 let display = format!("{capacity_err}");
1626 assert!(display.contains("capacity exceeded"));
1627 }
1628
1629 #[test]
1630 fn test_arena_error_source_generation_overflow() {
1631 use std::error::Error;
1632
1633 let inner = GenerationOverflowError {
1634 index: 0,
1635 generation: NodeId::MAX_GENERATION,
1636 };
1637 let err = ArenaError::GenerationOverflow(inner);
1638 assert!(err.source().is_some());
1640 }
1641
1642 #[test]
1643 fn test_arena_error_source_none_for_other_variants() {
1644 use std::error::Error;
1645
1646 let corruption = ArenaError::FreeListCorruption { index: 0 };
1647 assert!(corruption.source().is_none());
1648
1649 let capacity = ArenaError::CapacityExceeded;
1650 assert!(capacity.source().is_none());
1651 }
1652
1653 #[test]
1654 fn test_arena_from_generation_overflow_error() {
1655 let inner = GenerationOverflowError {
1656 index: 10,
1657 generation: 99,
1658 };
1659 let err: ArenaError = inner.into();
1660 assert!(matches!(err, ArenaError::GenerationOverflow(_)));
1661 }
1662
1663 #[test]
1664 fn test_arena_error_clone_and_eq() {
1665 let err = ArenaError::CapacityExceeded;
1666 let cloned = err.clone();
1667 assert_eq!(err, cloned);
1668
1669 let err2 = ArenaError::FreeListCorruption { index: 3 };
1670 let cloned2 = err2.clone();
1671 assert_eq!(err2, cloned2);
1672 }
1673
1674 #[test]
1675 fn test_node_entry_with_visibility() {
1676 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file())
1677 .with_visibility(StringId::new(5));
1678
1679 assert_eq!(entry.visibility, Some(StringId::new(5)));
1680 }
1681
1682 #[test]
1683 fn test_node_entry_with_async_and_static() {
1684 let entry = NodeEntry::new(NodeKind::Method, test_name(), test_file())
1685 .with_async(true)
1686 .with_static(true);
1687
1688 assert!(entry.is_async);
1689 assert!(entry.is_static);
1690 }
1691
1692 #[test]
1693 fn test_node_entry_with_unsafe_flag() {
1694 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file()).with_unsafe(true);
1695
1696 assert!(entry.is_unsafe);
1697 }
1698
1699 #[test]
1700 fn test_node_entry_with_body_hash() {
1701 use crate::graph::body_hash::BodyHash128;
1702 let hash = BodyHash128::from_u128(0u128);
1703 let entry =
1704 NodeEntry::new(NodeKind::Function, test_name(), test_file()).with_body_hash(hash);
1705
1706 assert!(entry.body_hash.is_some());
1707 }
1708
1709 #[test]
1710 fn test_node_entry_defaults() {
1711 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file());
1712 assert_eq!(entry.start_byte, 0);
1713 assert_eq!(entry.end_byte, 0);
1714 assert_eq!(entry.start_line, 0);
1715 assert_eq!(entry.start_column, 0);
1716 assert_eq!(entry.end_line, 0);
1717 assert_eq!(entry.end_column, 0);
1718 assert!(entry.signature.is_none());
1719 assert!(entry.doc.is_none());
1720 assert!(entry.qualified_name.is_none());
1721 assert!(entry.visibility.is_none());
1722 assert!(!entry.is_async);
1723 assert!(!entry.is_static);
1724 assert!(!entry.is_unsafe);
1725 assert!(entry.body_hash.is_none());
1726 }
1727
1728 #[test]
1729 fn test_arena_default_impl() {
1730 let arena = NodeArena::default();
1731 assert_eq!(arena.len(), 0);
1732 assert!(arena.is_empty());
1733 }
1734
1735 #[test]
1736 fn test_get_invalid_id() {
1737 let mut arena = NodeArena::new();
1738 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1739 assert!(arena.get(NodeId::INVALID).is_none());
1741 }
1742
1743 #[test]
1744 fn test_get_mut_invalid_id() {
1745 let mut arena = NodeArena::new();
1746 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1747 assert!(arena.get_mut(NodeId::INVALID).is_none());
1748 }
1749
1750 #[test]
1751 fn test_get_mut_wrong_generation() {
1752 let mut arena = NodeArena::new();
1753 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1754 let wrong_gen_id = NodeId::new(id.index(), id.generation() + 1);
1755 assert!(arena.get_mut(wrong_gen_id).is_none());
1756 }
1757
1758 #[test]
1759 fn test_remove_invalid_id() {
1760 let mut arena = NodeArena::new();
1761 assert!(arena.remove(NodeId::INVALID).is_none());
1762 }
1763
1764 #[test]
1765 fn test_slot_get_mut_occupied() {
1766 let mut slot: Slot<i32> = Slot::new_occupied(1, 42);
1767 let val = slot.get_mut().unwrap();
1768 *val = 99;
1769 assert_eq!(slot.get(), Some(&99));
1770 }
1771
1772 #[test]
1773 fn test_slot_get_mut_vacant() {
1774 let mut slot: Slot<i32> = Slot::new_vacant(1, None);
1775 assert!(slot.get_mut().is_none());
1776 }
1777
1778 #[test]
1779 fn test_truncate_to_zero_occupied_dropped() {
1780 let mut arena = NodeArena::new();
1781 let placeholder = test_entry(NodeKind::Function);
1782 arena.alloc_range(5, &placeholder).unwrap();
1783 arena.truncate_to(0);
1785 assert_eq!(arena.len(), 0);
1786 assert_eq!(arena.slot_count(), 0);
1787 }
1788
1789 #[test]
1790 fn test_slot_count_vs_len() {
1791 let mut arena = NodeArena::new();
1792 let id0 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1793 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1794 arena.remove(id0);
1795 assert_eq!(arena.slot_count(), 2);
1797 assert_eq!(arena.len(), 1);
1798 }
1799}