aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r--crates/ra_hir/src/lib.rs2
-rw-r--r--crates/ra_hir/src/mock.rs172
-rw-r--r--crates/ra_hir/src/module/nameres.rs97
3 files changed, 271 insertions, 0 deletions
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index f13f0107e..e7b6a81f4 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -15,6 +15,8 @@ macro_rules! ctry {
15} 15}
16 16
17pub mod db; 17pub mod db;
18#[cfg(test)]
19mod mock;
18mod query_definitions; 20mod query_definitions;
19mod function; 21mod function;
20mod module; 22mod module;
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
new file mode 100644
index 000000000..8e256b89f
--- /dev/null
+++ b/crates/ra_hir/src/mock.rs
@@ -0,0 +1,172 @@
1use std::sync::Arc;
2
3use parking_lot::Mutex;
4use salsa::{self, Database};
5use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE};
6use relative_path::RelativePathBuf;
7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8
9use crate::{db, DefId, DefLoc, FnId, SourceItemId};
10
11#[derive(Debug)]
12pub(crate) struct MockDatabase {
13 events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
14 runtime: salsa::Runtime<MockDatabase>,
15 id_maps: Arc<IdMaps>,
16}
17
18impl MockDatabase {
19 pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
20 let mut db = MockDatabase::default();
21
22 let mut position = None;
23 let mut file_map = FileMap::default();
24 for entry in parse_fixture(fixture) {
25 if entry.text.contains(CURSOR_MARKER) {
26 assert!(
27 position.is_none(),
28 "only one marker (<|>) per fixture is allowed"
29 );
30 position = Some(db.add_file_with_position(&mut file_map, &entry.meta, &entry.text));
31 } else {
32 db.add_file(&mut file_map, &entry.meta, &entry.text);
33 }
34 }
35 let position = position.expect("expected a marker (<|>)");
36 let source_root = file_map.into_source_root();
37 db.query_mut(ra_db::SourceRootQuery)
38 .set(WORKSPACE, Arc::new(source_root));
39 (db, position)
40 }
41
42 fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId {
43 assert!(path.starts_with('/'));
44 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
45
46 let file_id = file_map.add(path);
47 let text = Arc::new(text.to_string());
48 self.query_mut(ra_db::FileTextQuery).set(file_id, text);
49 self.query_mut(ra_db::FileSourceRootQuery)
50 .set(file_id, WORKSPACE);
51 file_id
52 }
53
54 fn add_file_with_position(
55 &mut self,
56 file_map: &mut FileMap,
57 path: &str,
58 text: &str,
59 ) -> FilePosition {
60 let (offset, text) = extract_offset(text);
61 let file_id = self.add_file(file_map, path, &text);
62 FilePosition { file_id, offset }
63 }
64}
65
66#[derive(Debug, Default)]
67struct IdMaps {
68 fns: LocationIntener<SourceItemId, FnId>,
69 defs: LocationIntener<DefLoc, DefId>,
70}
71
72impl salsa::Database for MockDatabase {
73 fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> {
74 &self.runtime
75 }
76
77 fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) {
78 let mut events = self.events.lock();
79 if let Some(events) = &mut *events {
80 events.push(event());
81 }
82 }
83}
84
85impl Default for MockDatabase {
86 fn default() -> MockDatabase {
87 let mut db = MockDatabase {
88 events: Default::default(),
89 runtime: salsa::Runtime::default(),
90 id_maps: Default::default(),
91 };
92 db.query_mut(ra_db::SourceRootQuery)
93 .set(ra_db::WORKSPACE, Default::default());
94 db.query_mut(ra_db::CrateGraphQuery)
95 .set((), Default::default());
96 db.query_mut(ra_db::LibrariesQuery)
97 .set((), Default::default());
98 db
99 }
100}
101
102impl salsa::ParallelDatabase for MockDatabase {
103 fn snapshot(&self) -> salsa::Snapshot<MockDatabase> {
104 salsa::Snapshot::new(MockDatabase {
105 events: Default::default(),
106 runtime: self.runtime.snapshot(self),
107 id_maps: self.id_maps.clone(),
108 })
109 }
110}
111
112impl BaseDatabase for MockDatabase {}
113
114impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase {
115 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
116 &self.id_maps.defs
117 }
118}
119
120impl AsRef<LocationIntener<SourceItemId, FnId>> for MockDatabase {
121 fn as_ref(&self) -> &LocationIntener<SourceItemId, FnId> {
122 &self.id_maps.fns
123 }
124}
125
126impl MockDatabase {
127 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
128 *self.events.lock() = Some(Vec::new());
129 f();
130 let events = self.events.lock().take().unwrap();
131 events
132 }
133
134 pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
135 let events = self.log(f);
136 events
137 .into_iter()
138 .filter_map(|e| match e.kind {
139 // This pretty horrible, but `Debug` is the only way to inspect
140 // QueryDescriptor at the moment.
141 salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)),
142 _ => None,
143 })
144 .collect()
145 }
146}
147
148salsa::database_storage! {
149 pub(crate) struct MockDatabaseStorage for MockDatabase {
150 impl ra_db::FilesDatabase {
151 fn file_text() for ra_db::FileTextQuery;
152 fn file_source_root() for ra_db::FileSourceRootQuery;
153 fn source_root() for ra_db::SourceRootQuery;
154 fn libraries() for ra_db::LibrariesQuery;
155 fn crate_graph() for ra_db::CrateGraphQuery;
156 }
157 impl ra_db::SyntaxDatabase {
158 fn source_file() for ra_db::SourceFileQuery;
159 fn file_lines() for ra_db::FileLinesQuery;
160 }
161 impl db::HirDatabase {
162 fn module_tree() for db::ModuleTreeQuery;
163 fn fn_scopes() for db::FnScopesQuery;
164 fn file_items() for db::SourceFileItemsQuery;
165 fn file_item() for db::FileItemQuery;
166 fn input_module_items() for db::InputModuleItemsQuery;
167 fn item_map() for db::ItemMapQuery;
168 fn fn_syntax() for db::FnSyntaxQuery;
169 fn submodules() for db::SubmodulesQuery;
170 }
171 }
172}
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 837a8d5ae..de13835b2 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -336,3 +336,100 @@ where
336 f(module_items) 336 f(module_items)
337 } 337 }
338} 338}
339
340//TODO: move to hir
341#[cfg(test)]
342mod tests {
343 use std::sync::Arc;
344
345 use salsa::Database;
346 use ra_db::FilesDatabase;
347 use ra_syntax::SmolStr;
348
349 use crate::{
350 self as hir,
351 db::HirDatabase,
352 mock::MockDatabase,
353};
354
355 fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
356 let (db, pos) = MockDatabase::with_position(fixture);
357 let source_root = db.file_source_root(pos.file_id);
358 let module = hir::Module::guess_from_position(&db, pos).unwrap().unwrap();
359 let module_id = module.module_id;
360 (db.item_map(source_root).unwrap(), module_id)
361 }
362
363 #[test]
364 fn test_item_map() {
365 let (item_map, module_id) = item_map(
366 "
367 //- /lib.rs
368 mod foo;
369
370 use crate::foo::bar::Baz;
371 <|>
372
373 //- /foo/mod.rs
374 pub mod bar;
375
376 //- /foo/bar.rs
377 pub struct Baz;
378 ",
379 );
380 let name = SmolStr::from("Baz");
381 let resolution = &item_map.per_module[&module_id].items[&name];
382 assert!(resolution.def_id.is_some());
383 }
384
385 #[test]
386 fn typing_inside_a_function_should_not_invalidate_item_map() {
387 let (mut db, pos) = MockDatabase::with_position(
388 "
389 //- /lib.rs
390 mod foo;<|>
391
392 use crate::foo::bar::Baz;
393
394 fn foo() -> i32 {
395 1 + 1
396 }
397 //- /foo/mod.rs
398 pub mod bar;
399
400 //- /foo/bar.rs
401 pub struct Baz;
402 ",
403 );
404 let source_root = db.file_source_root(pos.file_id);
405 {
406 let events = db.log_executed(|| {
407 db.item_map(source_root).unwrap();
408 });
409 assert!(format!("{:?}", events).contains("item_map"))
410 }
411
412 let new_text = "
413 mod foo;
414
415 use crate::foo::bar::Baz;
416
417 fn foo() -> i32 { 92 }
418 "
419 .to_string();
420
421 db.query_mut(ra_db::FileTextQuery)
422 .set(pos.file_id, Arc::new(new_text));
423
424 {
425 let events = db.log_executed(|| {
426 db.item_map(source_root).unwrap();
427 });
428 assert!(
429 !format!("{:?}", events).contains("_item_map"),
430 "{:#?}",
431 events
432 )
433 }
434 }
435}