1use std::collections::HashMap;
47
48use slotmap::{SecondaryMap, SlotMap};
49use uuid::Uuid;
50
51use crate::error::{InvalidSymbolId, RegistrationError, RenameError, UnregisterReexportError};
52use crate::file_path::WorkspaceFilePath;
53use crate::id::SymbolId;
54use crate::kind::SymbolKind;
55use crate::path::SymbolPath;
56use crate::span::{FileSpan, Visibility};
57use crate::var_scope::VarScope;
58
59#[derive(Debug, Clone)]
61pub struct ReExportInfo {
62 pub alias_path: SymbolPath,
64 pub origin_file: WorkspaceFilePath,
66}
67
68#[derive(Clone)]
88pub struct SymbolRegistry {
89 id_to_path: SlotMap<SymbolId, SymbolPath>,
92 path_to_id: HashMap<SymbolPath, SymbolId>,
94
95 kinds: SecondaryMap<SymbolId, SymbolKind>,
98 spans: SecondaryMap<SymbolId, FileSpan>,
100 visibility: SecondaryMap<SymbolId, Visibility>,
102 parents: SecondaryMap<SymbolId, SymbolId>,
104
105 re_exports: SecondaryMap<SymbolId, Vec<ReExportInfo>>,
108 alias_to_canonical: HashMap<SymbolPath, SymbolId>,
110
111 id_to_uuid: SecondaryMap<SymbolId, Uuid>,
114 uuid_to_id: HashMap<Uuid, SymbolId>,
116 preloaded_uuids: HashMap<SymbolPath, Uuid>,
119}
120
121impl SymbolRegistry {
122 pub fn new() -> Self {
124 Self {
125 id_to_path: SlotMap::with_key(),
126 path_to_id: HashMap::new(),
127 kinds: SecondaryMap::new(),
128 spans: SecondaryMap::new(),
129 visibility: SecondaryMap::new(),
130 parents: SecondaryMap::new(),
131 re_exports: SecondaryMap::new(),
132 alias_to_canonical: HashMap::new(),
133 id_to_uuid: SecondaryMap::new(),
134 uuid_to_id: HashMap::new(),
135 preloaded_uuids: HashMap::new(),
136 }
137 }
138
139 pub fn with_capacity(capacity: usize) -> Self {
141 Self {
142 id_to_path: SlotMap::with_capacity_and_key(capacity),
143 path_to_id: HashMap::with_capacity(capacity),
144 kinds: SecondaryMap::new(),
145 spans: SecondaryMap::new(),
146 visibility: SecondaryMap::new(),
147 parents: SecondaryMap::new(),
148 re_exports: SecondaryMap::new(),
149 alias_to_canonical: HashMap::new(),
150 id_to_uuid: SecondaryMap::new(),
151 uuid_to_id: HashMap::with_capacity(capacity),
152 preloaded_uuids: HashMap::new(),
153 }
154 }
155
156 pub fn register(
164 &mut self,
165 path: SymbolPath,
166 kind: SymbolKind,
167 ) -> Result<SymbolId, RegistrationError> {
168 if let Some(&existing_id) = self.path_to_id.get(&path) {
170 if let Some(&existing_kind) = self.kinds.get(existing_id) {
172 if existing_kind != kind {
173 return Err(RegistrationError::ConflictingKind {
174 path: Box::new(path),
175 existing: existing_kind,
176 new: kind,
177 });
178 }
179 }
180 return Ok(existing_id);
181 }
182
183 let id = self.id_to_path.insert(path.clone());
185 self.path_to_id.insert(path.clone(), id);
186 self.kinds.insert(id, kind);
187
188 let uuid = self
190 .preloaded_uuids
191 .remove(&path)
192 .unwrap_or_else(Uuid::new_v4);
193 self.id_to_uuid.insert(id, uuid);
194 self.uuid_to_id.insert(uuid, id);
195
196 Ok(id)
197 }
198
199 pub fn register_with_metadata(
201 &mut self,
202 path: SymbolPath,
203 kind: SymbolKind,
204 span: Option<FileSpan>,
205 vis: Option<Visibility>,
206 ) -> Result<SymbolId, RegistrationError> {
207 let id = self.register(path, kind)?;
208
209 if let Some(span) = span {
210 self.spans.insert(id, span);
211 }
212 if let Some(vis) = vis {
213 self.visibility.insert(id, vis);
214 }
215
216 Ok(id)
217 }
218
219 pub fn register_var(
229 &mut self,
230 containing_symbol: SymbolId,
231 scope: VarScope,
232 name: &str,
233 kind: SymbolKind,
234 ) -> Result<SymbolId, RegistrationError> {
235 let parent_path = self
237 .path(containing_symbol)
238 .ok_or(RegistrationError::InvalidParent)?
239 .clone();
240
241 let var_path = parent_path
243 .with_var_scope(scope, name)
244 .map_err(RegistrationError::InvalidPath)?;
245
246 let id = self.register(var_path, kind)?;
248
249 self.parents.insert(id, containing_symbol);
251
252 Ok(id)
253 }
254
255 #[inline]
261 pub fn lookup(&self, path: &SymbolPath) -> Option<SymbolId> {
262 if let Some(&id) = self.path_to_id.get(path) {
264 return Some(id);
265 }
266 self.alias_to_canonical.get(path).copied()
268 }
269
270 #[inline]
272 pub fn resolve(&self, id: SymbolId) -> Option<&SymbolPath> {
273 self.id_to_path.get(id)
274 }
275
276 #[inline]
278 pub fn path(&self, id: SymbolId) -> Option<&SymbolPath> {
279 self.resolve(id)
280 }
281
282 #[inline]
293 pub fn get_ref(&self, id: SymbolId) -> Option<crate::SymbolRef> {
294 self.resolve(id)
295 .map(|path| crate::SymbolRef::new(id, path.clone()))
296 }
297
298 #[inline]
300 pub fn contains(&self, id: SymbolId) -> bool {
301 self.id_to_path.contains_key(id)
302 }
303
304 #[inline]
308 pub fn kind(&self, id: SymbolId) -> Option<SymbolKind> {
309 self.kinds.get(id).copied()
310 }
311
312 #[inline]
314 pub fn span(&self, id: SymbolId) -> Option<&FileSpan> {
315 self.spans.get(id)
316 }
317
318 #[inline]
320 pub fn visibility(&self, id: SymbolId) -> Option<&Visibility> {
321 self.visibility.get(id)
322 }
323
324 #[inline]
326 pub fn parent(&self, id: SymbolId) -> Option<SymbolId> {
327 self.parents.get(id).copied()
328 }
329
330 pub fn set_span(&mut self, id: SymbolId, span: FileSpan) -> Result<(), InvalidSymbolId> {
334 if !self.contains(id) {
335 return Err(InvalidSymbolId(id));
336 }
337 self.spans.insert(id, span);
338 Ok(())
339 }
340
341 pub fn set_visibility(&mut self, id: SymbolId, vis: Visibility) -> Result<(), InvalidSymbolId> {
343 if !self.contains(id) {
344 return Err(InvalidSymbolId(id));
345 }
346 self.visibility.insert(id, vis);
347 Ok(())
348 }
349
350 pub fn set_kind(&mut self, id: SymbolId, kind: SymbolKind) -> Result<(), InvalidSymbolId> {
352 if !self.contains(id) {
353 return Err(InvalidSymbolId(id));
354 }
355 self.kinds.insert(id, kind);
356 Ok(())
357 }
358
359 pub fn remove(&mut self, id: SymbolId) -> Option<SymbolPath> {
364 let path = self.id_to_path.remove(id)?;
365 self.path_to_id.remove(&path);
366 self.kinds.remove(id);
367 self.spans.remove(id);
368 self.visibility.remove(id);
369 self.parents.remove(id);
370
371 if let Some(aliases) = self.re_exports.remove(id) {
373 for info in aliases {
374 self.alias_to_canonical.remove(&info.alias_path);
375 }
376 }
377
378 if let Some(uuid) = self.id_to_uuid.remove(id) {
380 self.uuid_to_id.remove(&uuid);
381 }
382
383 Some(path)
384 }
385
386 pub fn rename(
390 &mut self,
391 id: SymbolId,
392 new_path: SymbolPath,
393 ) -> Result<SymbolPath, RenameError> {
394 let old_path = self
396 .id_to_path
397 .get(id)
398 .ok_or(RenameError::InvalidId(id))?
399 .clone();
400
401 if self.path_to_id.contains_key(&new_path) {
403 return Err(RenameError::PathExists(Box::new(new_path)));
404 }
405
406 self.path_to_id.remove(&old_path);
408 self.path_to_id.insert(new_path.clone(), id);
409 self.id_to_path[id] = new_path;
410
411 Ok(old_path)
412 }
413
414 pub fn find_by_name(&self, name: &str) -> Vec<SymbolId> {
420 let mut results: Vec<SymbolId> = self
421 .id_to_path
422 .iter()
423 .filter(|(_, path)| path.name() == name)
424 .map(|(id, _)| id)
425 .collect();
426
427 for (alias_path, &canonical_id) in &self.alias_to_canonical {
429 if alias_path.name() == name && !results.contains(&canonical_id) {
430 results.push(canonical_id);
431 }
432 }
433
434 results
435 }
436
437 pub fn lookup_by_name(&self, name: &str) -> Option<SymbolId> {
439 self.id_to_path
440 .iter()
441 .find(|(_, path)| path.name() == name)
442 .map(|(id, _)| id)
443 }
444
445 pub fn register_reexport(
449 &mut self,
450 canonical_id: SymbolId,
451 alias_path: SymbolPath,
452 origin_file: WorkspaceFilePath,
453 ) -> Result<(), InvalidSymbolId> {
454 if !self.contains(canonical_id) {
455 return Err(InvalidSymbolId(canonical_id));
456 }
457
458 self.alias_to_canonical
460 .insert(alias_path.clone(), canonical_id);
461
462 let info = ReExportInfo {
464 alias_path,
465 origin_file,
466 };
467 self.re_exports
468 .entry(canonical_id)
469 .expect("canonical_id was validated by self.contains() above")
470 .or_default()
471 .push(info);
472
473 Ok(())
474 }
475
476 pub fn unregister_reexport(
478 &mut self,
479 alias_path: &SymbolPath,
480 ) -> Result<(), UnregisterReexportError> {
481 let canonical_id = self
482 .alias_to_canonical
483 .remove(alias_path)
484 .ok_or(UnregisterReexportError::NotFound)?;
485
486 if let Some(aliases) = self.re_exports.get_mut(canonical_id) {
487 aliases.retain(|info| &info.alias_path != alias_path);
488 if aliases.is_empty() {
489 self.re_exports.remove(canonical_id);
490 }
491 }
492
493 Ok(())
494 }
495
496 pub fn re_exports(&self, id: SymbolId) -> Option<&[ReExportInfo]> {
498 self.re_exports.get(id).map(|v| v.as_slice())
499 }
500
501 pub fn register_persistent(
526 &mut self,
527 path: SymbolPath,
528 kind: SymbolKind,
529 uuid: Option<Uuid>,
530 ) -> Result<(SymbolId, Uuid), RegistrationError> {
531 let id = self.register(path, kind)?;
533
534 if let Some(&existing_uuid) = self.id_to_uuid.get(id) {
536 if let Some(provided) = uuid {
538 if provided != existing_uuid {
539 return Err(RegistrationError::UuidConflict {
542 id,
543 existing: existing_uuid,
544 provided,
545 });
546 }
547 }
548 return Ok((id, existing_uuid));
549 }
550
551 let final_uuid = uuid.unwrap_or_else(Uuid::new_v4);
553 self.id_to_uuid.insert(id, final_uuid);
554 self.uuid_to_id.insert(final_uuid, id);
555
556 Ok((id, final_uuid))
557 }
558
559 pub fn assign_uuid(
567 &mut self,
568 id: SymbolId,
569 uuid: Option<Uuid>,
570 ) -> Result<Uuid, InvalidSymbolId> {
571 if !self.contains(id) {
572 return Err(InvalidSymbolId(id));
573 }
574
575 if let Some(&existing) = self.id_to_uuid.get(id) {
577 return Ok(existing);
578 }
579
580 let final_uuid = uuid.unwrap_or_else(Uuid::new_v4);
581 self.id_to_uuid.insert(id, final_uuid);
582 self.uuid_to_id.insert(final_uuid, id);
583
584 Ok(final_uuid)
585 }
586
587 #[inline]
591 pub fn uuid(&self, id: SymbolId) -> Option<Uuid> {
592 self.id_to_uuid.get(id).copied()
593 }
594
595 #[inline]
599 pub fn lookup_by_uuid(&self, uuid: Uuid) -> Option<SymbolId> {
600 self.uuid_to_id.get(&uuid).copied()
601 }
602
603 #[inline]
605 pub fn has_uuid(&self, id: SymbolId) -> bool {
606 self.id_to_uuid.contains_key(id)
607 }
608
609 pub fn iter_persistent(&self) -> impl Iterator<Item = (SymbolId, Uuid)> + '_ {
611 self.id_to_uuid.iter().map(|(id, &uuid)| (id, uuid))
612 }
613
614 pub fn persistent_count(&self) -> usize {
616 self.id_to_uuid.len()
617 }
618
619 pub fn preload_uuid_mapping(&mut self, mappings: HashMap<SymbolPath, Uuid>) {
638 self.preloaded_uuids = mappings;
639 }
640
641 pub fn export_uuid_mapping(&self) -> HashMap<SymbolPath, Uuid> {
653 self.id_to_path
654 .iter()
655 .filter_map(|(id, path)| self.id_to_uuid.get(id).map(|&uuid| (path.clone(), uuid)))
656 .collect()
657 }
658
659 pub fn export_uuid_mapping_strings(&self) -> HashMap<String, String> {
663 self.export_uuid_mapping()
664 .into_iter()
665 .map(|(path, uuid)| (path.to_string(), uuid.to_string()))
666 .collect()
667 }
668
669 pub fn preload_uuid_mapping_strings(&mut self, mappings: HashMap<String, String>) {
674 let parsed: HashMap<SymbolPath, Uuid> = mappings
675 .into_iter()
676 .filter_map(|(path_str, uuid_str)| {
677 let path = SymbolPath::parse(&path_str).ok()?;
678 let uuid = Uuid::parse_str(&uuid_str).ok()?;
679 Some((path, uuid))
680 })
681 .collect();
682 self.preloaded_uuids = parsed;
683 }
684
685 pub fn iter(&self) -> impl Iterator<Item = (SymbolId, &SymbolPath)> {
689 self.id_to_path.iter()
690 }
691
692 pub fn iter_by_kind(&self, kind: SymbolKind) -> impl Iterator<Item = SymbolId> + '_ {
694 self.kinds
695 .iter()
696 .filter(move |(_, &k)| k == kind)
697 .map(|(id, _)| id)
698 }
699
700 pub fn iter_in_crate<'a>(&'a self, crate_name: &'a str) -> impl Iterator<Item = SymbolId> + 'a {
702 self.id_to_path
703 .iter()
704 .filter(move |(_, path)| path.crate_name() == crate_name)
705 .map(|(id, _)| id)
706 }
707
708 pub fn len(&self) -> usize {
712 self.id_to_path.len()
713 }
714
715 pub fn is_empty(&self) -> bool {
717 self.id_to_path.is_empty()
718 }
719
720 pub fn memory_stats(&self) -> MemoryStats {
722 MemoryStats {
723 symbol_count: self.id_to_path.len(),
724 estimated_bytes: self.id_to_path.len() * 64 + self.path_to_id.len() * 80
727 + self.kinds.len() * 8
728 + self.spans.len() * 48
729 + self.visibility.len() * 16,
730 }
731 }
732}
733
734impl Default for SymbolRegistry {
735 fn default() -> Self {
736 Self::new()
737 }
738}
739
740#[derive(Debug, Clone)]
742pub struct MemoryStats {
743 pub symbol_count: usize,
745 pub estimated_bytes: usize,
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753
754 fn make_path(s: &str) -> SymbolPath {
755 SymbolPath::parse(s).unwrap()
756 }
757
758 #[test]
759 fn test_register_and_lookup() {
760 let mut registry = SymbolRegistry::new();
761
762 let path = make_path("my_crate::MyStruct");
763 let id = registry.register(path.clone(), SymbolKind::Struct).unwrap();
764
765 assert!(registry.contains(id));
766 assert_eq!(registry.lookup(&path), Some(id));
767 assert_eq!(registry.resolve(id), Some(&path));
768 assert_eq!(registry.kind(id), Some(SymbolKind::Struct));
769 }
770
771 #[test]
772 fn test_register_duplicate() {
773 let mut registry = SymbolRegistry::new();
774
775 let path = make_path("my_crate::MyStruct");
776 let id1 = registry.register(path.clone(), SymbolKind::Struct).unwrap();
777 let id2 = registry.register(path.clone(), SymbolKind::Struct).unwrap();
778
779 assert_eq!(id1, id2);
781 }
782
783 #[test]
784 fn test_register_conflicting_kind() {
785 let mut registry = SymbolRegistry::new();
786
787 let path = make_path("my_crate::MyStruct");
788 registry.register(path.clone(), SymbolKind::Struct).unwrap();
789
790 let result = registry.register(path, SymbolKind::Enum);
792 assert!(matches!(
793 result,
794 Err(RegistrationError::ConflictingKind { .. })
795 ));
796 }
797
798 #[test]
799 fn test_register_var() {
800 let mut registry = SymbolRegistry::new();
801
802 let fn_path = make_path("my_crate::my_fn");
804 let fn_id = registry.register(fn_path, SymbolKind::Function).unwrap();
805
806 let var_id = registry
808 .register_var(fn_id, VarScope::Local, "result", SymbolKind::Variable)
809 .unwrap();
810
811 let var_path = registry.resolve(var_id).unwrap();
813 assert_eq!(var_path.to_string(), "my_crate::my_fn::$var::result");
814 assert_eq!(registry.parent(var_id), Some(fn_id));
815 }
816
817 #[test]
818 fn test_reexport() {
819 let mut registry = SymbolRegistry::new();
820
821 let canonical_path = make_path("std::collections::hash_map::HashMap");
823 let canonical_id = registry
824 .register(canonical_path, SymbolKind::Struct)
825 .unwrap();
826
827 let alias_path = make_path("std::collections::HashMap");
829 let origin = WorkspaceFilePath::new_for_test("src/collections/mod.rs", "/std", "std");
830
831 registry
832 .register_reexport(canonical_id, alias_path.clone(), origin)
833 .unwrap();
834
835 assert_eq!(registry.lookup(&alias_path), Some(canonical_id));
837 }
838
839 #[test]
840 fn test_iter_by_kind() {
841 let mut registry = SymbolRegistry::new();
842
843 registry
844 .register(make_path("my_crate::Struct1"), SymbolKind::Struct)
845 .unwrap();
846 registry
847 .register(make_path("my_crate::Struct2"), SymbolKind::Struct)
848 .unwrap();
849 registry
850 .register(make_path("my_crate::func"), SymbolKind::Function)
851 .unwrap();
852
853 let structs: Vec<_> = registry.iter_by_kind(SymbolKind::Struct).collect();
854 assert_eq!(structs.len(), 2);
855
856 let funcs: Vec<_> = registry.iter_by_kind(SymbolKind::Function).collect();
857 assert_eq!(funcs.len(), 1);
858 }
859
860 #[test]
863 fn test_register_persistent_new() {
864 let mut registry = SymbolRegistry::new();
865
866 let path = make_path("my_crate::MyStruct");
867 let (id, uuid) = registry
868 .register_persistent(path.clone(), SymbolKind::Struct, None)
869 .unwrap();
870
871 assert_eq!(registry.uuid(id), Some(uuid));
873 assert_eq!(registry.lookup_by_uuid(uuid), Some(id));
874 assert!(registry.has_uuid(id));
875 }
876
877 #[test]
878 fn test_register_persistent_returns_auto_uuid() {
879 let mut registry = SymbolRegistry::new();
880
881 let path = make_path("my_crate::MyStruct");
884 let (id, uuid) = registry
885 .register_persistent(path, SymbolKind::Struct, None)
886 .unwrap();
887
888 assert_eq!(registry.uuid(id), Some(uuid));
890 assert_eq!(registry.lookup_by_uuid(uuid), Some(id));
891 }
892
893 #[test]
894 fn test_register_persistent_idempotent() {
895 let mut registry = SymbolRegistry::new();
896
897 let path = make_path("my_crate::MyStruct");
898
899 let (id1, uuid1) = registry
901 .register_persistent(path.clone(), SymbolKind::Struct, None)
902 .unwrap();
903
904 let (id2, uuid2) = registry
906 .register_persistent(path, SymbolKind::Struct, None)
907 .unwrap();
908
909 assert_eq!(id1, id2);
911 assert_eq!(uuid1, uuid2);
912 }
913
914 #[test]
915 fn test_uuid_survives_rename() {
916 let mut registry = SymbolRegistry::new();
917
918 let old_path = make_path("my_crate::OldName");
920 let (id, uuid) = registry
921 .register_persistent(old_path, SymbolKind::Struct, None)
922 .unwrap();
923
924 let new_path = make_path("my_crate::NewName");
926 registry.rename(id, new_path.clone()).unwrap();
927
928 assert_eq!(registry.uuid(id), Some(uuid));
930 assert_eq!(registry.lookup_by_uuid(uuid), Some(id));
931
932 assert_eq!(registry.resolve(id), Some(&new_path));
934 }
935
936 #[test]
937 fn test_uuid_removed_on_delete() {
938 let mut registry = SymbolRegistry::new();
939
940 let path = make_path("my_crate::MyStruct");
941 let (id, uuid) = registry
942 .register_persistent(path, SymbolKind::Struct, None)
943 .unwrap();
944
945 registry.remove(id);
947
948 assert!(registry.uuid(id).is_none());
950 assert!(registry.lookup_by_uuid(uuid).is_none());
951 }
952
953 #[test]
954 fn test_auto_uuid_on_register() {
955 let mut registry = SymbolRegistry::new();
956
957 let path = make_path("my_crate::MyStruct");
959 let id = registry.register(path, SymbolKind::Struct).unwrap();
960
961 assert!(registry.has_uuid(id));
963 let uuid = registry.uuid(id).unwrap();
964 assert_eq!(registry.lookup_by_uuid(uuid), Some(id));
965
966 let uuid2 = registry.assign_uuid(id, None).unwrap();
968 assert_eq!(uuid, uuid2);
969 }
970
971 #[test]
972 fn test_iter_persistent() {
973 let mut registry = SymbolRegistry::new();
974
975 let id0 = registry
977 .register(make_path("my_crate::Symbol0"), SymbolKind::Struct)
978 .unwrap();
979 let id1 = registry
980 .register(make_path("my_crate::Symbol1"), SymbolKind::Struct)
981 .unwrap();
982 let id2 = registry
983 .register(make_path("my_crate::Symbol2"), SymbolKind::Enum)
984 .unwrap();
985
986 let persistent: Vec<_> = registry.iter_persistent().collect();
988 assert_eq!(persistent.len(), 3);
989 assert_eq!(registry.persistent_count(), 3);
990
991 assert!(registry.has_uuid(id0));
993 assert!(registry.has_uuid(id1));
994 assert!(registry.has_uuid(id2));
995 }
996
997 #[test]
998 fn test_uuid_conflict_error() {
999 let mut registry = SymbolRegistry::new();
1000
1001 let path = make_path("my_crate::MyStruct");
1002 let different_uuid = Uuid::new_v4();
1003
1004 let id = registry.register(path.clone(), SymbolKind::Struct).unwrap();
1006 let auto_uuid = registry.uuid(id).unwrap();
1007
1008 let result = registry.register_persistent(path, SymbolKind::Struct, Some(different_uuid));
1010
1011 assert!(matches!(
1012 result,
1013 Err(RegistrationError::UuidConflict { .. })
1014 ));
1015
1016 assert_eq!(registry.uuid(id), Some(auto_uuid));
1018 }
1019
1020 #[test]
1023 fn test_find_by_name_includes_aliases() {
1024 let mut registry = SymbolRegistry::new();
1025
1026 let canonical_path = make_path("parking_lot::Mutex");
1028 let canonical_id = registry
1029 .register(canonical_path, SymbolKind::Struct)
1030 .unwrap();
1031
1032 let alias_path = make_path("tokio::sync::Mutex");
1034 let file_path = WorkspaceFilePath::new_for_test("src/sync/mod.rs", "/tmp/tokio", "tokio");
1035 registry
1036 .register_reexport(canonical_id, alias_path, file_path)
1037 .unwrap();
1038
1039 let results = registry.find_by_name("Mutex");
1041 assert_eq!(results.len(), 1);
1042 assert_eq!(results[0], canonical_id);
1043 }
1044
1045 #[test]
1046 fn test_find_by_name_no_duplicate_with_alias() {
1047 let mut registry = SymbolRegistry::new();
1048
1049 let canonical_path = make_path("parking_lot::Mutex");
1051 let canonical_id = registry
1052 .register(canonical_path, SymbolKind::Struct)
1053 .unwrap();
1054
1055 let alias_path = make_path("tokio::sync::Mutex");
1057 let file_path = WorkspaceFilePath::new_for_test("src/sync/mod.rs", "/tmp/tokio", "tokio");
1058 registry
1059 .register_reexport(canonical_id, alias_path, file_path)
1060 .unwrap();
1061
1062 let results = registry.find_by_name("Mutex");
1064 assert_eq!(results.len(), 1);
1065 }
1066
1067 #[test]
1068 fn test_lookup_resolves_alias() {
1069 let mut registry = SymbolRegistry::new();
1070
1071 let canonical_path = make_path("my_crate::inner::Config");
1072 let canonical_id = registry
1073 .register(canonical_path, SymbolKind::Struct)
1074 .unwrap();
1075
1076 let alias_path = make_path("my_crate::Config");
1077 let file_path = WorkspaceFilePath::new_for_test("src/lib.rs", "/tmp/my_crate", "my_crate");
1078 registry
1079 .register_reexport(canonical_id, alias_path.clone(), file_path)
1080 .unwrap();
1081
1082 assert_eq!(registry.lookup(&alias_path), Some(canonical_id));
1084 }
1085}