aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-11-28 13:28:44 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-11-28 13:28:44 +0000
commit0cda188621b792265c957bdf4ac54af31cbc9947 (patch)
tree6f93cb6c8d04e2eb44154464b7e74ef549573a6f
parent95c0c8f3986c8b3bcf0052d34d3ace09ebb9fa1b (diff)
parent555483d397db530d1d102d7828c7847a948baf6b (diff)
Merge #248
248: Hir tests r=matklad a=matklad bors r+ Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_analysis/src/db.rs43
-rw-r--r--crates/ra_analysis/src/lib.rs109
-rw-r--r--crates/ra_analysis/src/mock_analysis.rs40
-rw-r--r--crates/ra_db/src/lib.rs1
-rw-r--r--crates/ra_db/src/mock.rs51
-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
9 files changed, 338 insertions, 196 deletions
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 7fc3fe31b..df2ef293d 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,6 +1,4 @@
1use std::sync::Arc; 1use std::sync::Arc;
2#[cfg(test)]
3use parking_lot::Mutex;
4use salsa::{self, Database}; 2use salsa::{self, Database};
5use ra_db::{LocationIntener, BaseDatabase}; 3use ra_db::{LocationIntener, BaseDatabase};
6use hir::{self, DefId, DefLoc, FnId, SourceItemId}; 4use hir::{self, DefId, DefLoc, FnId, SourceItemId};
@@ -11,11 +9,6 @@ use crate::{
11 9
12#[derive(Debug)] 10#[derive(Debug)]
13pub(crate) struct RootDatabase { 11pub(crate) struct RootDatabase {
14 #[cfg(test)]
15 events: Mutex<Option<Vec<salsa::Event<RootDatabase>>>>,
16 #[cfg(not(test))]
17 events: (),
18
19 runtime: salsa::Runtime<RootDatabase>, 12 runtime: salsa::Runtime<RootDatabase>,
20 id_maps: Arc<IdMaps>, 13 id_maps: Arc<IdMaps>,
21} 14}
@@ -30,23 +23,11 @@ impl salsa::Database for RootDatabase {
30 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { 23 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
31 &self.runtime 24 &self.runtime
32 } 25 }
33
34 #[allow(unused)]
35 fn salsa_event(&self, event: impl Fn() -> salsa::Event<RootDatabase>) {
36 #[cfg(test)]
37 {
38 let mut events = self.events.lock();
39 if let Some(events) = &mut *events {
40 events.push(event());
41 }
42 }
43 }
44} 26}
45 27
46impl Default for RootDatabase { 28impl Default for RootDatabase {
47 fn default() -> RootDatabase { 29 fn default() -> RootDatabase {
48 let mut db = RootDatabase { 30 let mut db = RootDatabase {
49 events: Default::default(),
50 runtime: salsa::Runtime::default(), 31 runtime: salsa::Runtime::default(),
51 id_maps: Default::default(), 32 id_maps: Default::default(),
52 }; 33 };
@@ -63,7 +44,6 @@ impl Default for RootDatabase {
63impl salsa::ParallelDatabase for RootDatabase { 44impl salsa::ParallelDatabase for RootDatabase {
64 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { 45 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
65 salsa::Snapshot::new(RootDatabase { 46 salsa::Snapshot::new(RootDatabase {
66 events: Default::default(),
67 runtime: self.runtime.snapshot(self), 47 runtime: self.runtime.snapshot(self),
68 id_maps: self.id_maps.clone(), 48 id_maps: self.id_maps.clone(),
69 }) 49 })
@@ -84,29 +64,6 @@ impl AsRef<LocationIntener<hir::SourceItemId, FnId>> for RootDatabase {
84 } 64 }
85} 65}
86 66
87#[cfg(test)]
88impl RootDatabase {
89 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<RootDatabase>> {
90 *self.events.lock() = Some(Vec::new());
91 f();
92 let events = self.events.lock().take().unwrap();
93 events
94 }
95
96 pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
97 let events = self.log(f);
98 events
99 .into_iter()
100 .filter_map(|e| match e.kind {
101 // This pretty horrible, but `Debug` is the only way to inspect
102 // QueryDescriptor at the moment.
103 salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)),
104 _ => None,
105 })
106 .collect()
107 }
108}
109
110salsa::database_storage! { 67salsa::database_storage! {
111 pub(crate) struct RootDatabaseStorage for RootDatabase { 68 pub(crate) struct RootDatabaseStorage for RootDatabase {
112 impl ra_db::FilesDatabase { 69 impl ra_db::FilesDatabase {
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 350a6d627..12df580ba 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -317,112 +317,3 @@ fn analysis_is_send() {
317 fn is_send<T: Send>() {} 317 fn is_send<T: Send>() {}
318 is_send::<Analysis>(); 318 is_send::<Analysis>();
319} 319}
320
321//TODO: move to hir
322#[cfg(test)]
323mod hir_namres_tests {
324 use std::sync::Arc;
325 use ra_db::FilesDatabase;
326 use ra_syntax::SmolStr;
327 use hir::{self, db::HirDatabase};
328
329 use crate::{
330 AnalysisChange,
331 mock_analysis::{MockAnalysis, analysis_and_position},
332};
333
334 fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
335 let (analysis, pos) = analysis_and_position(fixture);
336 let db = analysis.imp.db;
337 let source_root = db.file_source_root(pos.file_id);
338 let descr = hir::Module::guess_from_position(&*db, pos)
339 .unwrap()
340 .unwrap();
341 let module_id = descr.module_id;
342 (db.item_map(source_root).unwrap(), module_id)
343 }
344
345 #[test]
346 fn test_item_map() {
347 let (item_map, module_id) = item_map(
348 "
349 //- /lib.rs
350 mod foo;
351
352 use crate::foo::bar::Baz;
353 <|>
354
355 //- /foo/mod.rs
356 pub mod bar;
357
358 //- /foo/bar.rs
359 pub struct Baz;
360 ",
361 );
362 let name = SmolStr::from("Baz");
363 let resolution = &item_map.per_module[&module_id].items[&name];
364 assert!(resolution.def_id.is_some());
365 }
366
367 #[test]
368 fn typing_inside_a_function_should_not_invalidate_item_map() {
369 let mock_analysis = MockAnalysis::with_files(
370 "
371 //- /lib.rs
372 mod foo;
373
374 use crate::foo::bar::Baz;
375
376 fn foo() -> i32 {
377 1 + 1
378 }
379 //- /foo/mod.rs
380 pub mod bar;
381
382 //- /foo/bar.rs
383 pub struct Baz;
384 ",
385 );
386
387 let file_id = mock_analysis.id_of("/lib.rs");
388 let mut host = mock_analysis.analysis_host();
389
390 let source_root = host.analysis().imp.db.file_source_root(file_id);
391
392 {
393 let db = host.analysis().imp.db;
394 let events = db.log_executed(|| {
395 db.item_map(source_root).unwrap();
396 });
397 assert!(format!("{:?}", events).contains("item_map"))
398 }
399
400 let mut change = AnalysisChange::new();
401
402 change.change_file(
403 file_id,
404 "
405 mod foo;
406
407 use crate::foo::bar::Baz;
408
409 fn foo() -> i32 { 92 }
410 "
411 .to_string(),
412 );
413
414 host.apply_change(change);
415
416 {
417 let db = host.analysis().imp.db;
418 let events = db.log_executed(|| {
419 db.item_map(source_root).unwrap();
420 });
421 assert!(
422 !format!("{:?}", events).contains("_item_map"),
423 "{:#?}",
424 events
425 )
426 }
427 }
428}
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index 8e8f969f4..0d9a7a147 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,9 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use relative_path::{RelativePath, RelativePathBuf}; 3use relative_path::{RelativePathBuf};
4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
5use ra_db::mock::FileMap;
5 6
6use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition}; 7use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition};
7 8
8/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 9/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
9/// from a set of in-memory files. 10/// from a set of in-memory files.
@@ -76,16 +77,15 @@ impl MockAnalysis {
76 } 77 }
77 pub fn analysis_host(self) -> AnalysisHost { 78 pub fn analysis_host(self) -> AnalysisHost {
78 let mut host = AnalysisHost::default(); 79 let mut host = AnalysisHost::default();
79 let mut file_map = Vec::new(); 80 let mut file_map = FileMap::default();
80 let mut change = AnalysisChange::new(); 81 let mut change = AnalysisChange::new();
81 for (id, (path, contents)) in self.files.into_iter().enumerate() { 82 for (path, contents) in self.files.into_iter() {
82 let file_id = FileId((id + 1) as u32);
83 assert!(path.starts_with('/')); 83 assert!(path.starts_with('/'));
84 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 84 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
85 let file_id = file_map.add(path);
85 change.add_file(file_id, contents); 86 change.add_file(file_id, contents);
86 file_map.push((file_id, path));
87 } 87 }
88 change.set_file_resolver(Arc::new(FileMap(file_map))); 88 change.set_file_resolver(Arc::new(file_map));
89 host.apply_change(change); 89 host.apply_change(change);
90 host 90 host
91 } 91 }
@@ -113,29 +113,3 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) {
113 let pos = mock.add_file_with_position("/main.rs", code); 113 let pos = mock.add_file_with_position("/main.rs", code);
114 (mock.analysis(), pos) 114 (mock.analysis(), pos)
115} 115}
116
117#[derive(Debug)]
118struct FileMap(Vec<(FileId, RelativePathBuf)>);
119
120impl FileMap {
121 fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
122 self.0
123 .iter()
124 .map(|(id, path)| (*id, path.as_relative_path()))
125 }
126
127 fn path(&self, id: FileId) -> &RelativePath {
128 self.iter().find(|&(it, _)| it == id).unwrap().1
129 }
130}
131
132impl FileResolver for FileMap {
133 fn file_stem(&self, id: FileId) -> String {
134 self.path(id).file_stem().unwrap().to_string()
135 }
136 fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
137 let path = self.path(id).join(rel).normalize();
138 let id = self.iter().find(|&(_, p)| path == p)?.0;
139 Some(id)
140 }
141}
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 33cb0e2ec..6a996c7f4 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -10,6 +10,7 @@ mod syntax_ptr;
10mod file_resolver; 10mod file_resolver;
11mod input; 11mod input;
12mod loc2id; 12mod loc2id;
13pub mod mock;
13 14
14use std::sync::Arc; 15use std::sync::Arc;
15use ra_editor::LineIndex; 16use ra_editor::LineIndex;
diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs
new file mode 100644
index 000000000..2840f9655
--- /dev/null
+++ b/crates/ra_db/src/mock.rs
@@ -0,0 +1,51 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashSet;
4use relative_path::{RelativePath, RelativePathBuf};
5
6use crate::{FileId, FileResolver, SourceRoot, FileResolverImp};
7
8#[derive(Default, Debug)]
9pub struct FileMap(Vec<(FileId, RelativePathBuf)>);
10
11impl FileMap {
12 pub fn add(&mut self, path: RelativePathBuf) -> FileId {
13 let file_id = FileId((self.0.len() + 1) as u32);
14 self.0.push((file_id, path));
15 file_id
16 }
17
18 pub fn into_source_root(self) -> SourceRoot {
19 let files = self.files();
20 let file_resolver = FileResolverImp::new(Arc::new(self));
21 SourceRoot {
22 file_resolver,
23 files,
24 }
25 }
26
27 pub fn files(&self) -> FxHashSet<FileId> {
28 self.iter().map(|(id, _)| id).collect()
29 }
30
31 fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
32 self.0
33 .iter()
34 .map(|(id, path)| (*id, path.as_relative_path()))
35 }
36
37 fn path(&self, id: FileId) -> &RelativePath {
38 self.iter().find(|&(it, _)| it == id).unwrap().1
39 }
40}
41
42impl FileResolver for FileMap {
43 fn file_stem(&self, id: FileId) -> String {
44 self.path(id).file_stem().unwrap().to_string()
45 }
46 fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
47 let path = self.path(id).join(rel).normalize();
48 let id = self.iter().find(|&(_, p)| path == p)?.0;
49 Some(id)
50 }
51}
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}