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/mod.rs8
-rw-r--r--crates/ra_hir/src/module/nameres.rs108
4 files changed, 279 insertions, 11 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/mod.rs b/crates/ra_hir/src/module/mod.rs
index a011fd53e..3ae83d8cb 100644
--- a/crates/ra_hir/src/module/mod.rs
+++ b/crates/ra_hir/src/module/mod.rs
@@ -26,8 +26,7 @@ pub use self::nameres::ModuleScope;
26pub struct Module { 26pub struct Module {
27 tree: Arc<ModuleTree>, 27 tree: Arc<ModuleTree>,
28 source_root_id: SourceRootId, 28 source_root_id: SourceRootId,
29 //TODO: make private 29 module_id: ModuleId,
30 pub module_id: ModuleId,
31} 30}
32 31
33impl Module { 32impl Module {
@@ -122,7 +121,6 @@ impl Module {
122 } 121 }
123 122
124 /// `name` is `None` for the crate's root module 123 /// `name` is `None` for the crate's root module
125 #[allow(unused)]
126 pub fn name(&self) -> Option<SmolStr> { 124 pub fn name(&self) -> Option<SmolStr> {
127 let link = self.module_id.parent_link(&self.tree)?; 125 let link = self.module_id.parent_link(&self.tree)?;
128 Some(link.name(&self.tree)) 126 Some(link.name(&self.tree))
@@ -218,7 +216,7 @@ pub enum ModuleSource {
218 216
219/// An owned syntax node for a module. Unlike `ModuleSource`, 217/// An owned syntax node for a module. Unlike `ModuleSource`,
220/// this holds onto the AST for the whole file. 218/// this holds onto the AST for the whole file.
221pub enum ModuleSourceNode { 219pub(crate) enum ModuleSourceNode {
222 SourceFile(ast::SourceFileNode), 220 SourceFile(ast::SourceFileNode),
223 Module(ast::ModuleNode), 221 Module(ast::ModuleNode),
224} 222}
@@ -338,7 +336,7 @@ impl ModuleSource {
338 } 336 }
339 } 337 }
340 338
341 pub fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode { 339 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
342 match self { 340 match self {
343 ModuleSource::SourceFile(file_id) => { 341 ModuleSource::SourceFile(file_id) => {
344 let syntax = db.source_file(file_id); 342 let syntax = db.source_file(file_id);
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 837a8d5ae..8529e16b3 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -44,7 +44,7 @@ pub struct ItemMap {
44 44
45#[derive(Debug, Default, PartialEq, Eq, Clone)] 45#[derive(Debug, Default, PartialEq, Eq, Clone)]
46pub struct ModuleScope { 46pub struct ModuleScope {
47 pub items: FxHashMap<SmolStr, Resolution>, 47 items: FxHashMap<SmolStr, Resolution>,
48} 48}
49 49
50impl ModuleScope { 50impl ModuleScope {
@@ -200,11 +200,11 @@ impl ModuleItem {
200} 200}
201 201
202pub(crate) struct Resolver<'a, DB> { 202pub(crate) struct Resolver<'a, DB> {
203 pub db: &'a DB, 203 pub(crate) db: &'a DB,
204 pub input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, 204 pub(crate) input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
205 pub source_root: SourceRootId, 205 pub(crate) source_root: SourceRootId,
206 pub module_tree: Arc<ModuleTree>, 206 pub(crate) module_tree: Arc<ModuleTree>,
207 pub result: ItemMap, 207 pub(crate) result: ItemMap,
208} 208}
209 209
210impl<'a, DB> Resolver<'a, DB> 210impl<'a, DB> Resolver<'a, DB>
@@ -336,3 +336,99 @@ where
336 f(module_items) 336 f(module_items)
337 } 337 }
338} 338}
339
340#[cfg(test)]
341mod tests {
342 use std::sync::Arc;
343
344 use salsa::Database;
345 use ra_db::FilesDatabase;
346 use ra_syntax::SmolStr;
347
348 use crate::{
349 self as hir,
350 db::HirDatabase,
351 mock::MockDatabase,
352};
353
354 fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
355 let (db, pos) = MockDatabase::with_position(fixture);
356 let source_root = db.file_source_root(pos.file_id);
357 let module = hir::Module::guess_from_position(&db, pos).unwrap().unwrap();
358 let module_id = module.module_id;
359 (db.item_map(source_root).unwrap(), module_id)
360 }
361
362 #[test]
363 fn test_item_map() {
364 let (item_map, module_id) = item_map(
365 "
366 //- /lib.rs
367 mod foo;
368
369 use crate::foo::bar::Baz;
370 <|>
371
372 //- /foo/mod.rs
373 pub mod bar;
374
375 //- /foo/bar.rs
376 pub struct Baz;
377 ",
378 );
379 let name = SmolStr::from("Baz");
380 let resolution = &item_map.per_module[&module_id].items[&name];
381 assert!(resolution.def_id.is_some());
382 }
383
384 #[test]
385 fn typing_inside_a_function_should_not_invalidate_item_map() {
386 let (mut db, pos) = MockDatabase::with_position(
387 "
388 //- /lib.rs
389 mod foo;<|>
390
391 use crate::foo::bar::Baz;
392
393 fn foo() -> i32 {
394 1 + 1
395 }
396 //- /foo/mod.rs
397 pub mod bar;
398
399 //- /foo/bar.rs
400 pub struct Baz;
401 ",
402 );
403 let source_root = db.file_source_root(pos.file_id);
404 {
405 let events = db.log_executed(|| {
406 db.item_map(source_root).unwrap();
407 });
408 assert!(format!("{:?}", events).contains("item_map"))
409 }
410
411 let new_text = "
412 mod foo;
413
414 use crate::foo::bar::Baz;
415
416 fn foo() -> i32 { 92 }
417 "
418 .to_string();
419
420 db.query_mut(ra_db::FileTextQuery)
421 .set(pos.file_id, Arc::new(new_text));
422
423 {
424 let events = db.log_executed(|| {
425 db.item_map(source_root).unwrap();
426 });
427 assert!(
428 !format!("{:?}", events).contains("_item_map"),
429 "{:#?}",
430 events
431 )
432 }
433 }
434}