1 //! Database used for testing `hir`. 2 3 use std::{fmt, panic, sync::Mutex}; 4 5 use base_db::{ 6 salsa::{self, Durability}, 7 AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, 8 }; 9 use hir_def::{db::DefDatabase, ModuleId}; 10 use hir_expand::db::ExpandDatabase; 11 use nohash_hasher::IntMap; 12 use rustc_hash::FxHashSet; 13 use syntax::TextRange; 14 use test_utils::extract_annotations; 15 use triomphe::Arc; 16 17 #[salsa::database( 18 base_db::SourceDatabaseExtStorage, 19 base_db::SourceDatabaseStorage, 20 hir_expand::db::ExpandDatabaseStorage, 21 hir_def::db::InternDatabaseStorage, 22 hir_def::db::DefDatabaseStorage, 23 crate::db::HirDatabaseStorage 24 )] 25 pub(crate) struct TestDB { 26 storage: salsa::Storage<TestDB>, 27 events: Mutex<Option<Vec<salsa::Event>>>, 28 } 29 30 impl Default for TestDB { default() -> Self31 fn default() -> Self { 32 let mut this = Self { storage: Default::default(), events: Default::default() }; 33 this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); 34 this 35 } 36 } 37 38 impl fmt::Debug for TestDB { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 f.debug_struct("TestDB").finish() 41 } 42 } 43 44 impl Upcast<dyn ExpandDatabase> for TestDB { upcast(&self) -> &(dyn ExpandDatabase + 'static)45 fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { 46 &*self 47 } 48 } 49 50 impl Upcast<dyn DefDatabase> for TestDB { upcast(&self) -> &(dyn DefDatabase + 'static)51 fn upcast(&self) -> &(dyn DefDatabase + 'static) { 52 &*self 53 } 54 } 55 56 impl salsa::Database for TestDB { salsa_event(&self, event: salsa::Event)57 fn salsa_event(&self, event: salsa::Event) { 58 let mut events = self.events.lock().unwrap(); 59 if let Some(events) = &mut *events { 60 events.push(event); 61 } 62 } 63 } 64 65 impl salsa::ParallelDatabase for TestDB { snapshot(&self) -> salsa::Snapshot<TestDB>66 fn snapshot(&self) -> salsa::Snapshot<TestDB> { 67 salsa::Snapshot::new(TestDB { 68 storage: self.storage.snapshot(), 69 events: Default::default(), 70 }) 71 } 72 } 73 74 impl panic::RefUnwindSafe for TestDB {} 75 76 impl FileLoader for TestDB { file_text(&self, file_id: FileId) -> Arc<str>77 fn file_text(&self, file_id: FileId) -> Arc<str> { 78 FileLoaderDelegate(self).file_text(file_id) 79 } resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>80 fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { 81 FileLoaderDelegate(self).resolve_path(path) 82 } relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>83 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { 84 FileLoaderDelegate(self).relevant_crates(file_id) 85 } 86 } 87 88 impl TestDB { module_for_file_opt(&self, file_id: FileId) -> Option<ModuleId>89 pub(crate) fn module_for_file_opt(&self, file_id: FileId) -> Option<ModuleId> { 90 for &krate in self.relevant_crates(file_id).iter() { 91 let crate_def_map = self.crate_def_map(krate); 92 for (local_id, data) in crate_def_map.modules() { 93 if data.origin.file_id() == Some(file_id) { 94 return Some(crate_def_map.module_id(local_id)); 95 } 96 } 97 } 98 None 99 } 100 module_for_file(&self, file_id: FileId) -> ModuleId101 pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { 102 self.module_for_file_opt(file_id).unwrap() 103 } 104 extract_annotations(&self) -> IntMap<FileId, Vec<(TextRange, String)>>105 pub(crate) fn extract_annotations(&self) -> IntMap<FileId, Vec<(TextRange, String)>> { 106 let mut files = Vec::new(); 107 let crate_graph = self.crate_graph(); 108 for krate in crate_graph.iter() { 109 let crate_def_map = self.crate_def_map(krate); 110 for (module_id, _) in crate_def_map.modules() { 111 let file_id = crate_def_map[module_id].origin.file_id(); 112 files.extend(file_id) 113 } 114 } 115 files 116 .into_iter() 117 .filter_map(|file_id| { 118 let text = self.file_text(file_id); 119 let annotations = extract_annotations(&text); 120 if annotations.is_empty() { 121 return None; 122 } 123 Some((file_id, annotations)) 124 }) 125 .collect() 126 } 127 } 128 129 impl TestDB { log(&self, f: impl FnOnce()) -> Vec<salsa::Event>130 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> { 131 *self.events.lock().unwrap() = Some(Vec::new()); 132 f(); 133 self.events.lock().unwrap().take().unwrap() 134 } 135 log_executed(&self, f: impl FnOnce()) -> Vec<String>136 pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> { 137 let events = self.log(f); 138 events 139 .into_iter() 140 .filter_map(|e| match e.kind { 141 // This is pretty horrible, but `Debug` is the only way to inspect 142 // QueryDescriptor at the moment. 143 salsa::EventKind::WillExecute { database_key } => { 144 Some(format!("{:?}", database_key.debug(self))) 145 } 146 _ => None, 147 }) 148 .collect() 149 } 150 } 151