aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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/nameres.rs97
7 files changed, 330 insertions, 142 deletions
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/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}