diff options
-rw-r--r-- | crates/ra_analysis/src/db.rs | 43 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 109 | ||||
-rw-r--r-- | crates/ra_analysis/src/mock_analysis.rs | 40 | ||||
-rw-r--r-- | crates/ra_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_db/src/mock.rs | 51 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 172 | ||||
-rw-r--r-- | crates/ra_hir/src/module/mod.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 108 |
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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | #[cfg(test)] | ||
3 | use parking_lot::Mutex; | ||
4 | use salsa::{self, Database}; | 2 | use salsa::{self, Database}; |
5 | use ra_db::{LocationIntener, BaseDatabase}; | 3 | use ra_db::{LocationIntener, BaseDatabase}; |
6 | use hir::{self, DefId, DefLoc, FnId, SourceItemId}; | 4 | use hir::{self, DefId, DefLoc, FnId, SourceItemId}; |
@@ -11,11 +9,6 @@ use crate::{ | |||
11 | 9 | ||
12 | #[derive(Debug)] | 10 | #[derive(Debug)] |
13 | pub(crate) struct RootDatabase { | 11 | pub(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 | ||
46 | impl Default for RootDatabase { | 28 | impl 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 { | |||
63 | impl salsa::ParallelDatabase for RootDatabase { | 44 | impl 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)] | ||
88 | impl 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 | |||
110 | salsa::database_storage! { | 67 | salsa::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)] | ||
323 | mod 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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::{RelativePath, RelativePathBuf}; | 3 | use relative_path::{RelativePathBuf}; |
4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; |
5 | use ra_db::mock::FileMap; | ||
5 | 6 | ||
6 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition}; | 7 | use 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)] | ||
118 | struct FileMap(Vec<(FileId, RelativePathBuf)>); | ||
119 | |||
120 | impl 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 | |||
132 | impl 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; | |||
10 | mod file_resolver; | 10 | mod file_resolver; |
11 | mod input; | 11 | mod input; |
12 | mod loc2id; | 12 | mod loc2id; |
13 | pub mod mock; | ||
13 | 14 | ||
14 | use std::sync::Arc; | 15 | use std::sync::Arc; |
15 | use ra_editor::LineIndex; | 16 | use 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashSet; | ||
4 | use relative_path::{RelativePath, RelativePathBuf}; | ||
5 | |||
6 | use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; | ||
7 | |||
8 | #[derive(Default, Debug)] | ||
9 | pub struct FileMap(Vec<(FileId, RelativePathBuf)>); | ||
10 | |||
11 | impl 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 | |||
42 | impl 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 | ||
17 | pub mod db; | 17 | pub mod db; |
18 | #[cfg(test)] | ||
19 | mod mock; | ||
18 | mod query_definitions; | 20 | mod query_definitions; |
19 | mod function; | 21 | mod function; |
20 | mod module; | 22 | mod 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use parking_lot::Mutex; | ||
4 | use salsa::{self, Database}; | ||
5 | use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE}; | ||
6 | use relative_path::RelativePathBuf; | ||
7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | ||
8 | |||
9 | use crate::{db, DefId, DefLoc, FnId, SourceItemId}; | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub(crate) struct MockDatabase { | ||
13 | events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>, | ||
14 | runtime: salsa::Runtime<MockDatabase>, | ||
15 | id_maps: Arc<IdMaps>, | ||
16 | } | ||
17 | |||
18 | impl 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)] | ||
67 | struct IdMaps { | ||
68 | fns: LocationIntener<SourceItemId, FnId>, | ||
69 | defs: LocationIntener<DefLoc, DefId>, | ||
70 | } | ||
71 | |||
72 | impl 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 | |||
85 | impl 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 | |||
102 | impl 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 | |||
112 | impl BaseDatabase for MockDatabase {} | ||
113 | |||
114 | impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase { | ||
115 | fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { | ||
116 | &self.id_maps.defs | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl AsRef<LocationIntener<SourceItemId, FnId>> for MockDatabase { | ||
121 | fn as_ref(&self) -> &LocationIntener<SourceItemId, FnId> { | ||
122 | &self.id_maps.fns | ||
123 | } | ||
124 | } | ||
125 | |||
126 | impl 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 | |||
148 | salsa::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; | |||
26 | pub struct Module { | 26 | pub 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 | ||
33 | impl Module { | 32 | impl 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. |
221 | pub enum ModuleSourceNode { | 219 | pub(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)] |
46 | pub struct ModuleScope { | 46 | pub struct ModuleScope { |
47 | pub items: FxHashMap<SmolStr, Resolution>, | 47 | items: FxHashMap<SmolStr, Resolution>, |
48 | } | 48 | } |
49 | 49 | ||
50 | impl ModuleScope { | 50 | impl ModuleScope { |
@@ -200,11 +200,11 @@ impl ModuleItem { | |||
200 | } | 200 | } |
201 | 201 | ||
202 | pub(crate) struct Resolver<'a, DB> { | 202 | pub(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 | ||
210 | impl<'a, DB> Resolver<'a, DB> | 210 | impl<'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)] | ||
341 | mod 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 | } | ||