aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/doc_tests.rs7
-rw-r--r--crates/ra_assists/src/lib.rs40
-rw-r--r--crates/ra_assists/src/test_db.rs45
-rw-r--r--crates/ra_hir/src/expr/scope.rs8
-rw-r--r--crates/ra_hir/src/lib.rs4
-rw-r--r--crates/ra_hir/src/mock.rs262
-rw-r--r--crates/ra_hir/src/test_db.rs120
-rw-r--r--crates/ra_hir/src/ty/tests.rs32
8 files changed, 212 insertions, 306 deletions
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index 6e1e3de84..a8f8446cb 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -5,13 +5,14 @@
5 5
6mod generated; 6mod generated;
7 7
8use hir::mock::MockDatabase; 8use ra_db::{fixture::WithFixture, FileRange};
9use ra_db::FileRange;
10use test_utils::{assert_eq_text, extract_range_or_offset}; 9use test_utils::{assert_eq_text, extract_range_or_offset};
11 10
11use crate::test_db::TestDB;
12
12fn check(assist_id: &str, before: &str, after: &str) { 13fn check(assist_id: &str, before: &str, after: &str) {
13 let (selection, before) = extract_range_or_offset(before); 14 let (selection, before) = extract_range_or_offset(before);
14 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 15 let (db, file_id) = TestDB::with_single_file(&before);
15 let frange = FileRange { file_id, range: selection.into() }; 16 let frange = FileRange { file_id, range: selection.into() };
16 17
17 let (_assist_id, action) = crate::assists(&db, frange) 18 let (_assist_id, action) = crate::assists(&db, frange)
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 38599d4f1..39c1c283f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,6 +9,8 @@ mod assist_ctx;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12#[cfg(test)]
13mod test_db;
12 14
13use hir::db::HirDatabase; 15use hir::db::HirDatabase;
14use ra_db::FileRange; 16use ra_db::FileRange;
@@ -146,20 +148,19 @@ mod assists {
146 148
147#[cfg(test)] 149#[cfg(test)]
148mod helpers { 150mod helpers {
149 use hir::mock::MockDatabase; 151 use ra_db::{fixture::WithFixture, FileRange};
150 use ra_db::FileRange;
151 use ra_syntax::TextRange; 152 use ra_syntax::TextRange;
152 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 153 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
153 154
154 use crate::{Assist, AssistCtx}; 155 use crate::{test_db::TestDB, Assist, AssistCtx};
155 156
156 pub(crate) fn check_assist( 157 pub(crate) fn check_assist(
157 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 158 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
158 before: &str, 159 before: &str,
159 after: &str, 160 after: &str,
160 ) { 161 ) {
161 let (before_cursor_pos, before) = extract_offset(before); 162 let (before_cursor_pos, before) = extract_offset(before);
162 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 163 let (db, file_id) = TestDB::with_single_file(&before);
163 let frange = 164 let frange =
164 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 165 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
165 let assist = 166 let assist =
@@ -182,12 +183,12 @@ mod helpers {
182 } 183 }
183 184
184 pub(crate) fn check_assist_range( 185 pub(crate) fn check_assist_range(
185 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 186 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
186 before: &str, 187 before: &str,
187 after: &str, 188 after: &str,
188 ) { 189 ) {
189 let (range, before) = extract_range(before); 190 let (range, before) = extract_range(before);
190 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 191 let (db, file_id) = TestDB::with_single_file(&before);
191 let frange = FileRange { file_id, range }; 192 let frange = FileRange { file_id, range };
192 let assist = 193 let assist =
193 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 194 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -204,12 +205,12 @@ mod helpers {
204 } 205 }
205 206
206 pub(crate) fn check_assist_target( 207 pub(crate) fn check_assist_target(
207 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 208 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
208 before: &str, 209 before: &str,
209 target: &str, 210 target: &str,
210 ) { 211 ) {
211 let (before_cursor_pos, before) = extract_offset(before); 212 let (before_cursor_pos, before) = extract_offset(before);
212 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 213 let (db, file_id) = TestDB::with_single_file(&before);
213 let frange = 214 let frange =
214 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 215 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
215 let assist = 216 let assist =
@@ -224,12 +225,12 @@ mod helpers {
224 } 225 }
225 226
226 pub(crate) fn check_assist_range_target( 227 pub(crate) fn check_assist_range_target(
227 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 228 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
228 before: &str, 229 before: &str,
229 target: &str, 230 target: &str,
230 ) { 231 ) {
231 let (range, before) = extract_range(before); 232 let (range, before) = extract_range(before);
232 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 233 let (db, file_id) = TestDB::with_single_file(&before);
233 let frange = FileRange { file_id, range }; 234 let frange = FileRange { file_id, range };
234 let assist = 235 let assist =
235 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 236 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -243,11 +244,11 @@ mod helpers {
243 } 244 }
244 245
245 pub(crate) fn check_assist_not_applicable( 246 pub(crate) fn check_assist_not_applicable(
246 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 247 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
247 before: &str, 248 before: &str,
248 ) { 249 ) {
249 let (before_cursor_pos, before) = extract_offset(before); 250 let (before_cursor_pos, before) = extract_offset(before);
250 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 251 let (db, file_id) = TestDB::with_single_file(&before);
251 let frange = 252 let frange =
252 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 253 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
253 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 254 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
@@ -255,11 +256,11 @@ mod helpers {
255 } 256 }
256 257
257 pub(crate) fn check_assist_range_not_applicable( 258 pub(crate) fn check_assist_range_not_applicable(
258 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 259 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
259 before: &str, 260 before: &str,
260 ) { 261 ) {
261 let (range, before) = extract_range(before); 262 let (range, before) = extract_range(before);
262 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 263 let (db, file_id) = TestDB::with_single_file(&before);
263 let frange = FileRange { file_id, range }; 264 let frange = FileRange { file_id, range };
264 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 265 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
265 assert!(assist.is_none()); 266 assert!(assist.is_none());
@@ -268,16 +269,17 @@ mod helpers {
268 269
269#[cfg(test)] 270#[cfg(test)]
270mod tests { 271mod tests {
271 use hir::mock::MockDatabase; 272 use ra_db::{fixture::WithFixture, FileRange};
272 use ra_db::FileRange;
273 use ra_syntax::TextRange; 273 use ra_syntax::TextRange;
274 use test_utils::{extract_offset, extract_range}; 274 use test_utils::{extract_offset, extract_range};
275 275
276 use crate::test_db::TestDB;
277
276 #[test] 278 #[test]
277 fn assist_order_field_struct() { 279 fn assist_order_field_struct() {
278 let before = "struct Foo { <|>bar: u32 }"; 280 let before = "struct Foo { <|>bar: u32 }";
279 let (before_cursor_pos, before) = extract_offset(before); 281 let (before_cursor_pos, before) = extract_offset(before);
280 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 282 let (db, file_id) = TestDB::with_single_file(&before);
281 let frange = 283 let frange =
282 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 284 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
283 let assists = super::assists(&db, frange); 285 let assists = super::assists(&db, frange);
@@ -298,7 +300,7 @@ mod tests {
298 } 300 }
299 }"; 301 }";
300 let (range, before) = extract_range(before); 302 let (range, before) = extract_range(before);
301 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 303 let (db, file_id) = TestDB::with_single_file(&before);
302 let frange = FileRange { file_id, range }; 304 let frange = FileRange { file_id, range };
303 let assists = super::assists(&db, frange); 305 let assists = super::assists(&db, frange);
304 let mut assists = assists.iter(); 306 let mut assists = assists.iter();
diff --git a/crates/ra_assists/src/test_db.rs b/crates/ra_assists/src/test_db.rs
new file mode 100644
index 000000000..5be7383ed
--- /dev/null
+++ b/crates/ra_assists/src/test_db.rs
@@ -0,0 +1,45 @@
1//! Database used for testing `ra_assists`.
2
3use std::sync::Arc;
4
5use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
6
7#[salsa::database(
8 ra_db::SourceDatabaseExtStorage,
9 ra_db::SourceDatabaseStorage,
10 hir::db::InternDatabaseStorage,
11 hir::db::AstDatabaseStorage,
12 hir::db::DefDatabaseStorage,
13 hir::db::DefDatabase2Storage,
14 hir::db::HirDatabaseStorage
15)]
16#[derive(Debug, Default)]
17pub struct TestDB {
18 runtime: salsa::Runtime<TestDB>,
19}
20
21impl salsa::Database for TestDB {
22 fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
23 &self.runtime
24 }
25}
26
27impl std::panic::RefUnwindSafe for TestDB {}
28
29impl FileLoader for TestDB {
30 fn file_text(&self, file_id: FileId) -> Arc<String> {
31 FileLoaderDelegate(self).file_text(file_id)
32 }
33 fn resolve_relative_path(
34 &self,
35 anchor: FileId,
36 relative_path: &RelativePath,
37 ) -> Option<FileId> {
38 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
39 }
40 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
41 FileLoaderDelegate(self).relevant_crates(file_id)
42 }
43}
44
45impl hir::debug::HirDebugHelper for TestDB {}
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 5496822e7..c14c2ab66 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -174,11 +174,11 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
174 174
175#[cfg(test)] 175#[cfg(test)]
176mod tests { 176mod tests {
177 use ra_db::SourceDatabase; 177 use ra_db::{fixture::WithFixture, SourceDatabase};
178 use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 178 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
179 use test_utils::{assert_eq_text, extract_offset}; 179 use test_utils::{assert_eq_text, extract_offset};
180 180
181 use crate::{mock::MockDatabase, source_binder::SourceAnalyzer}; 181 use crate::{source_binder::SourceAnalyzer, test_db::TestDB};
182 182
183 fn do_check(code: &str, expected: &[&str]) { 183 fn do_check(code: &str, expected: &[&str]) {
184 let (off, code) = extract_offset(code); 184 let (off, code) = extract_offset(code);
@@ -191,7 +191,7 @@ mod tests {
191 buf 191 buf
192 }; 192 };
193 193
194 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); 194 let (db, file_id) = TestDB::with_single_file(&code);
195 let file = db.parse(file_id).ok().unwrap(); 195 let file = db.parse(file_id).ok().unwrap();
196 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 196 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
197 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None); 197 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
@@ -288,7 +288,7 @@ mod tests {
288 fn do_check_local_name(code: &str, expected_offset: u32) { 288 fn do_check_local_name(code: &str, expected_offset: u32) {
289 let (off, code) = extract_offset(code); 289 let (off, code) = extract_offset(code);
290 290
291 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); 291 let (db, file_id) = TestDB::with_single_file(&code);
292 let file = db.parse(file_id).ok().unwrap(); 292 let file = db.parse(file_id).ok().unwrap();
293 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 293 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
294 .expect("failed to find a name at the target offset"); 294 .expect("failed to find a name at the target offset");
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 3ba99d92d..da6aa2ed8 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -29,8 +29,6 @@ macro_rules! impl_froms {
29pub mod debug; 29pub mod debug;
30 30
31pub mod db; 31pub mod db;
32#[macro_use]
33pub mod mock;
34pub mod source_binder; 32pub mod source_binder;
35 33
36mod ids; 34mod ids;
@@ -52,6 +50,8 @@ mod code_model;
52pub mod from_source; 50pub mod from_source;
53 51
54#[cfg(test)] 52#[cfg(test)]
53mod test_db;
54#[cfg(test)]
55mod marks; 55mod marks;
56 56
57use hir_expand::AstId; 57use hir_expand::AstId;
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
deleted file mode 100644
index ab97a09b9..000000000
--- a/crates/ra_hir/src/mock.rs
+++ /dev/null
@@ -1,262 +0,0 @@
1//! FIXME: write short doc here
2
3use std::{panic, sync::Arc};
4
5use hir_expand::diagnostics::DiagnosticSink;
6use parking_lot::Mutex;
7use ra_cfg::CfgOptions;
8use ra_db::{
9 salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition,
10 RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
11};
12use rustc_hash::FxHashMap;
13use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
14
15use crate::{db, debug::HirDebugHelper};
16
17pub const WORKSPACE: SourceRootId = SourceRootId(0);
18
19#[salsa::database(
20 ra_db::SourceDatabaseExtStorage,
21 ra_db::SourceDatabaseStorage,
22 db::InternDatabaseStorage,
23 db::AstDatabaseStorage,
24 db::DefDatabaseStorage,
25 db::DefDatabase2Storage,
26 db::HirDatabaseStorage
27)]
28#[derive(Debug)]
29pub struct MockDatabase {
30 events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
31 runtime: salsa::Runtime<MockDatabase>,
32 files: FxHashMap<String, FileId>,
33 crate_names: Arc<FxHashMap<CrateId, String>>,
34 file_paths: Arc<FxHashMap<FileId, String>>,
35}
36
37impl panic::RefUnwindSafe for MockDatabase {}
38
39impl FileLoader for MockDatabase {
40 fn file_text(&self, file_id: FileId) -> Arc<String> {
41 FileLoaderDelegate(self).file_text(file_id)
42 }
43 fn resolve_relative_path(
44 &self,
45 anchor: FileId,
46 relative_path: &RelativePath,
47 ) -> Option<FileId> {
48 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
49 }
50 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
51 FileLoaderDelegate(self).relevant_crates(file_id)
52 }
53}
54
55impl HirDebugHelper for MockDatabase {
56 fn crate_name(&self, krate: CrateId) -> Option<String> {
57 self.crate_names.get(&krate).cloned()
58 }
59
60 fn file_path(&self, file_id: FileId) -> Option<String> {
61 self.file_paths.get(&file_id).cloned()
62 }
63}
64
65impl MockDatabase {
66 pub fn with_files(fixture: &str) -> MockDatabase {
67 let (db, position) = MockDatabase::from_fixture(fixture);
68 assert!(position.is_none());
69 db
70 }
71
72 pub fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) {
73 let mut db = MockDatabase::default();
74 let mut source_root = SourceRoot::default();
75 let file_id = db.add_file(WORKSPACE, "/", &mut source_root, "/main.rs", text);
76 db.set_source_root(WORKSPACE, Arc::new(source_root.clone()));
77 (db, source_root, file_id)
78 }
79
80 pub fn file_id_of(&self, path: &str) -> FileId {
81 match self.files.get(path) {
82 Some(it) => *it,
83 None => panic!("unknown file: {:?}\nexisting files:\n{:#?}", path, self.files),
84 }
85 }
86
87 pub fn diagnostics(&self) -> String {
88 let mut buf = String::new();
89 let mut files: Vec<FileId> = self.files.values().copied().collect();
90 files.sort();
91 for file in files {
92 let src = crate::Source {
93 file_id: file.into(),
94 ast: crate::ModuleSource::new(self, Some(file), None),
95 };
96 let module = crate::Module::from_definition(self, src).unwrap();
97 module.diagnostics(
98 self,
99 &mut DiagnosticSink::new(|d| {
100 buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
101 }),
102 )
103 }
104 buf
105 }
106
107 fn from_fixture(fixture: &str) -> (MockDatabase, Option<FilePosition>) {
108 let mut db = MockDatabase::default();
109
110 let pos = db.add_fixture(fixture);
111
112 (db, pos)
113 }
114
115 fn add_fixture(&mut self, fixture: &str) -> Option<FilePosition> {
116 let mut position = None;
117 let mut source_root = SourceRoot::default();
118 let mut source_root_id = WORKSPACE;
119 let mut source_root_prefix = "/".to_string();
120 for entry in parse_fixture(fixture) {
121 if entry.meta.starts_with("root") {
122 self.set_source_root(source_root_id, Arc::new(source_root));
123 source_root = SourceRoot::default();
124
125 source_root_id = SourceRootId(source_root_id.0 + 1);
126 source_root_prefix = entry.meta["root".len()..].trim().to_string();
127 continue;
128 }
129 if entry.text.contains(CURSOR_MARKER) {
130 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
131 position = Some(self.add_file_with_position(
132 source_root_id,
133 &source_root_prefix,
134 &mut source_root,
135 &entry.meta,
136 &entry.text,
137 ));
138 } else {
139 self.add_file(
140 source_root_id,
141 &source_root_prefix,
142 &mut source_root,
143 &entry.meta,
144 &entry.text,
145 );
146 }
147 }
148 self.set_source_root(source_root_id, Arc::new(source_root));
149 position
150 }
151
152 fn add_file(
153 &mut self,
154 source_root_id: SourceRootId,
155 source_root_prefix: &str,
156 source_root: &mut SourceRoot,
157 path: &str,
158 text: &str,
159 ) -> FileId {
160 assert!(source_root_prefix.starts_with('/'));
161 assert!(source_root_prefix.ends_with('/'));
162 assert!(path.starts_with(source_root_prefix));
163 let rel_path = RelativePathBuf::from_path(&path[source_root_prefix.len()..]).unwrap();
164
165 let is_crate_root = rel_path == "lib.rs" || rel_path == "/main.rs";
166
167 let file_id = FileId(self.files.len() as u32);
168
169 let prev = self.files.insert(path.to_string(), file_id);
170 assert!(prev.is_none(), "duplicate files in the text fixture");
171 Arc::make_mut(&mut self.file_paths).insert(file_id, path.to_string());
172
173 let text = Arc::new(text.to_string());
174 self.set_file_text(file_id, text);
175 self.set_file_relative_path(file_id, rel_path.clone());
176 self.set_file_source_root(file_id, source_root_id);
177 source_root.insert_file(rel_path, file_id);
178
179 if is_crate_root {
180 let mut crate_graph = CrateGraph::default();
181 crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default());
182 self.set_crate_graph(Arc::new(crate_graph));
183 }
184 file_id
185 }
186
187 fn add_file_with_position(
188 &mut self,
189 source_root_id: SourceRootId,
190 source_root_prefix: &str,
191 source_root: &mut SourceRoot,
192 path: &str,
193 text: &str,
194 ) -> FilePosition {
195 let (offset, text) = extract_offset(text);
196 let file_id = self.add_file(source_root_id, source_root_prefix, source_root, path, &text);
197 FilePosition { file_id, offset }
198 }
199}
200
201impl salsa::Database for MockDatabase {
202 fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> {
203 &self.runtime
204 }
205
206 fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) {
207 let mut events = self.events.lock();
208 if let Some(events) = &mut *events {
209 events.push(event());
210 }
211 }
212}
213
214impl Default for MockDatabase {
215 fn default() -> MockDatabase {
216 let mut db = MockDatabase {
217 events: Default::default(),
218 runtime: salsa::Runtime::default(),
219 files: FxHashMap::default(),
220 crate_names: Default::default(),
221 file_paths: Default::default(),
222 };
223 db.set_crate_graph(Default::default());
224 db
225 }
226}
227
228impl salsa::ParallelDatabase for MockDatabase {
229 fn snapshot(&self) -> salsa::Snapshot<MockDatabase> {
230 salsa::Snapshot::new(MockDatabase {
231 events: Default::default(),
232 runtime: self.runtime.snapshot(self),
233 // only the root database can be used to get file_id by path.
234 files: FxHashMap::default(),
235 file_paths: Arc::clone(&self.file_paths),
236 crate_names: Arc::clone(&self.crate_names),
237 })
238 }
239}
240
241impl MockDatabase {
242 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
243 *self.events.lock() = Some(Vec::new());
244 f();
245 self.events.lock().take().unwrap()
246 }
247
248 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
249 let events = self.log(f);
250 events
251 .into_iter()
252 .filter_map(|e| match e.kind {
253 // This pretty horrible, but `Debug` is the only way to inspect
254 // QueryDescriptor at the moment.
255 salsa::EventKind::WillExecute { database_key } => {
256 Some(format!("{:?}", database_key))
257 }
258 _ => None,
259 })
260 .collect()
261 }
262}
diff --git a/crates/ra_hir/src/test_db.rs b/crates/ra_hir/src/test_db.rs
new file mode 100644
index 000000000..5237b303a
--- /dev/null
+++ b/crates/ra_hir/src/test_db.rs
@@ -0,0 +1,120 @@
1//! Database used for testing `hir`.
2
3use std::{panic, sync::Arc};
4
5use hir_def::{db::DefDatabase2, ModuleId};
6use hir_expand::diagnostics::DiagnosticSink;
7use parking_lot::Mutex;
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase};
9
10use crate::{db, debug::HirDebugHelper};
11
12#[salsa::database(
13 ra_db::SourceDatabaseExtStorage,
14 ra_db::SourceDatabaseStorage,
15 db::InternDatabaseStorage,
16 db::AstDatabaseStorage,
17 db::DefDatabaseStorage,
18 db::DefDatabase2Storage,
19 db::HirDatabaseStorage
20)]
21#[derive(Debug, Default)]
22pub struct TestDB {
23 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
24 runtime: salsa::Runtime<TestDB>,
25}
26
27impl salsa::Database for TestDB {
28 fn salsa_runtime(&self) -> &salsa::Runtime<TestDB> {
29 &self.runtime
30 }
31
32 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
33 let mut events = self.events.lock();
34 if let Some(events) = &mut *events {
35 events.push(event());
36 }
37 }
38}
39
40impl salsa::ParallelDatabase for TestDB {
41 fn snapshot(&self) -> salsa::Snapshot<TestDB> {
42 salsa::Snapshot::new(TestDB {
43 events: Default::default(),
44 runtime: self.runtime.snapshot(self),
45 })
46 }
47}
48
49impl panic::RefUnwindSafe for TestDB {}
50
51impl FileLoader for TestDB {
52 fn file_text(&self, file_id: FileId) -> Arc<String> {
53 FileLoaderDelegate(self).file_text(file_id)
54 }
55 fn resolve_relative_path(
56 &self,
57 anchor: FileId,
58 relative_path: &RelativePath,
59 ) -> Option<FileId> {
60 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
61 }
62 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
63 FileLoaderDelegate(self).relevant_crates(file_id)
64 }
65}
66
67// FIXME: improve `WithFixture` to bring useful hir debugging back
68impl HirDebugHelper for TestDB {
69 fn crate_name(&self, _krate: CrateId) -> Option<String> {
70 None
71 }
72
73 fn file_path(&self, _file_id: FileId) -> Option<String> {
74 None
75 }
76}
77
78impl TestDB {
79 pub fn diagnostics(&self) -> String {
80 let mut buf = String::new();
81 let crate_graph = self.crate_graph();
82 for krate in crate_graph.iter().next() {
83 let crate_def_map = self.crate_def_map(krate);
84 for (module_id, _) in crate_def_map.modules.iter() {
85 let module_id = ModuleId { krate, module_id };
86 let module = crate::Module::from(module_id);
87 module.diagnostics(
88 self,
89 &mut DiagnosticSink::new(|d| {
90 buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
91 }),
92 )
93 }
94 }
95 buf
96 }
97}
98
99impl TestDB {
100 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> {
101 *self.events.lock() = Some(Vec::new());
102 f();
103 self.events.lock().take().unwrap()
104 }
105
106 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
107 let events = self.log(f);
108 events
109 .into_iter()
110 .filter_map(|e| match e.kind {
111 // This pretty horrible, but `Debug` is the only way to inspect
112 // QueryDescriptor at the moment.
113 salsa::EventKind::WillExecute { database_key } => {
114 Some(format!("{:?}", database_key))
115 }
116 _ => None,
117 })
118 .collect()
119 }
120}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 4b7e34878..e56b9356e 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use test_utils::covers; 11use test_utils::covers;
12 12
13use crate::{ 13use crate::{
14 expr::BodySourceMap, mock::MockDatabase, ty::display::HirDisplay, ty::InferenceResult, 14 expr::BodySourceMap, test_db::TestDB, ty::display::HirDisplay, ty::InferenceResult,
15 SourceAnalyzer, 15 SourceAnalyzer,
16}; 16};
17 17
@@ -24,7 +24,7 @@ mod coercion;
24 24
25#[test] 25#[test]
26fn cfg_impl_block() { 26fn cfg_impl_block() {
27 let (db, pos) = MockDatabase::with_position( 27 let (db, pos) = TestDB::with_position(
28 r#" 28 r#"
29//- /main.rs crate:main deps:foo cfg:test 29//- /main.rs crate:main deps:foo cfg:test
30use foo::S as T; 30use foo::S as T;
@@ -64,7 +64,7 @@ impl S {
64 64
65#[test] 65#[test]
66fn infer_await() { 66fn infer_await() {
67 let (db, pos) = MockDatabase::with_position( 67 let (db, pos) = TestDB::with_position(
68 r#" 68 r#"
69//- /main.rs crate:main deps:std 69//- /main.rs crate:main deps:std
70 70
@@ -95,7 +95,7 @@ mod future {
95 95
96#[test] 96#[test]
97fn infer_box() { 97fn infer_box() {
98 let (db, pos) = MockDatabase::with_position( 98 let (db, pos) = TestDB::with_position(
99 r#" 99 r#"
100//- /main.rs crate:main deps:std 100//- /main.rs crate:main deps:std
101 101
@@ -122,7 +122,7 @@ mod boxed {
122 122
123#[test] 123#[test]
124fn infer_adt_self() { 124fn infer_adt_self() {
125 let (db, pos) = MockDatabase::with_position( 125 let (db, pos) = TestDB::with_position(
126 r#" 126 r#"
127//- /main.rs 127//- /main.rs
128enum Nat { Succ(Self), Demo(Nat), Zero } 128enum Nat { Succ(Self), Demo(Nat), Zero }
@@ -141,7 +141,7 @@ fn test() {
141 141
142#[test] 142#[test]
143fn infer_try() { 143fn infer_try() {
144 let (db, pos) = MockDatabase::with_position( 144 let (db, pos) = TestDB::with_position(
145 r#" 145 r#"
146//- /main.rs crate:main deps:std 146//- /main.rs crate:main deps:std
147 147
@@ -181,7 +181,7 @@ mod result {
181 181
182#[test] 182#[test]
183fn infer_for_loop() { 183fn infer_for_loop() {
184 let (db, pos) = MockDatabase::with_position( 184 let (db, pos) = TestDB::with_position(
185 r#" 185 r#"
186//- /main.rs crate:main deps:std 186//- /main.rs crate:main deps:std
187 187
@@ -223,7 +223,7 @@ mod collections {
223#[test] 223#[test]
224fn infer_while_let() { 224fn infer_while_let() {
225 covers!(infer_while_let); 225 covers!(infer_while_let);
226 let (db, pos) = MockDatabase::with_position( 226 let (db, pos) = TestDB::with_position(
227 r#" 227 r#"
228//- /main.rs 228//- /main.rs
229enum Option<T> { Some(T), None } 229enum Option<T> { Some(T), None }
@@ -2484,7 +2484,7 @@ pub fn main_loop() {
2484 2484
2485#[test] 2485#[test]
2486fn cross_crate_associated_method_call() { 2486fn cross_crate_associated_method_call() {
2487 let (db, pos) = MockDatabase::with_position( 2487 let (db, pos) = TestDB::with_position(
2488 r#" 2488 r#"
2489//- /main.rs crate:main deps:other_crate 2489//- /main.rs crate:main deps:other_crate
2490fn test() { 2490fn test() {
@@ -3378,7 +3378,7 @@ fn test() { S.foo()<|>; }
3378 3378
3379#[test] 3379#[test]
3380fn infer_macro_with_dollar_crate_is_correct_in_expr() { 3380fn infer_macro_with_dollar_crate_is_correct_in_expr() {
3381 let (db, pos) = MockDatabase::with_position( 3381 let (db, pos) = TestDB::with_position(
3382 r#" 3382 r#"
3383//- /main.rs crate:main deps:foo 3383//- /main.rs crate:main deps:foo
3384fn test() { 3384fn test() {
@@ -3482,7 +3482,7 @@ fn test() { (&S).foo()<|>; }
3482 3482
3483#[test] 3483#[test]
3484fn method_resolution_trait_from_prelude() { 3484fn method_resolution_trait_from_prelude() {
3485 let (db, pos) = MockDatabase::with_position( 3485 let (db, pos) = TestDB::with_position(
3486 r#" 3486 r#"
3487//- /main.rs crate:main deps:other_crate 3487//- /main.rs crate:main deps:other_crate
3488struct S; 3488struct S;
@@ -4651,7 +4651,7 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
4651 assert_eq!(t, "{unknown}"); 4651 assert_eq!(t, "{unknown}");
4652} 4652}
4653 4653
4654fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 4654fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
4655 let file = db.parse(pos.file_id).ok().unwrap(); 4655 let file = db.parse(pos.file_id).ok().unwrap();
4656 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 4656 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
4657 let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset)); 4657 let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
@@ -4660,12 +4660,12 @@ fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
4660} 4660}
4661 4661
4662fn type_at(content: &str) -> String { 4662fn type_at(content: &str) -> String {
4663 let (db, file_pos) = MockDatabase::with_position(content); 4663 let (db, file_pos) = TestDB::with_position(content);
4664 type_at_pos(&db, file_pos) 4664 type_at_pos(&db, file_pos)
4665} 4665}
4666 4666
4667fn infer(content: &str) -> String { 4667fn infer(content: &str) -> String {
4668 let (db, _, file_id) = MockDatabase::with_single_file(content); 4668 let (db, file_id) = TestDB::with_single_file(content);
4669 let source_file = db.parse(file_id).ok().unwrap(); 4669 let source_file = db.parse(file_id).ok().unwrap();
4670 4670
4671 let mut acc = String::new(); 4671 let mut acc = String::new();
@@ -4748,7 +4748,7 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
4748 4748
4749#[test] 4749#[test]
4750fn typing_whitespace_inside_a_function_should_not_invalidate_types() { 4750fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
4751 let (mut db, pos) = MockDatabase::with_position( 4751 let (mut db, pos) = TestDB::with_position(
4752 " 4752 "
4753 //- /lib.rs 4753 //- /lib.rs
4754 fn foo() -> i32 { 4754 fn foo() -> i32 {
@@ -4788,7 +4788,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
4788 4788
4789#[test] 4789#[test]
4790fn no_such_field_diagnostics() { 4790fn no_such_field_diagnostics() {
4791 let diagnostics = MockDatabase::with_files( 4791 let diagnostics = TestDB::with_files(
4792 r" 4792 r"
4793 //- /lib.rs 4793 //- /lib.rs
4794 struct S { foo: i32, bar: () } 4794 struct S { foo: i32, bar: () }