diff options
-rw-r--r-- | crates/ra_assists/src/doc_tests.rs | 7 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 40 | ||||
-rw-r--r-- | crates/ra_assists/src/test_db.rs | 45 | ||||
-rw-r--r-- | crates/ra_hir/src/expr/scope.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 262 | ||||
-rw-r--r-- | crates/ra_hir/src/test_db.rs | 120 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 32 |
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 | ||
6 | mod generated; | 6 | mod generated; |
7 | 7 | ||
8 | use hir::mock::MockDatabase; | 8 | use ra_db::{fixture::WithFixture, FileRange}; |
9 | use ra_db::FileRange; | ||
10 | use test_utils::{assert_eq_text, extract_range_or_offset}; | 9 | use test_utils::{assert_eq_text, extract_range_or_offset}; |
11 | 10 | ||
11 | use crate::test_db::TestDB; | ||
12 | |||
12 | fn check(assist_id: &str, before: &str, after: &str) { | 13 | fn 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; | |||
9 | mod marks; | 9 | mod marks; |
10 | #[cfg(test)] | 10 | #[cfg(test)] |
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | #[cfg(test)] | ||
13 | mod test_db; | ||
12 | 14 | ||
13 | use hir::db::HirDatabase; | 15 | use hir::db::HirDatabase; |
14 | use ra_db::FileRange; | 16 | use ra_db::FileRange; |
@@ -146,20 +148,19 @@ mod assists { | |||
146 | 148 | ||
147 | #[cfg(test)] | 149 | #[cfg(test)] |
148 | mod helpers { | 150 | mod 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)] |
270 | mod tests { | 271 | mod 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 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use 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)] | ||
17 | pub struct TestDB { | ||
18 | runtime: salsa::Runtime<TestDB>, | ||
19 | } | ||
20 | |||
21 | impl salsa::Database for TestDB { | ||
22 | fn salsa_runtime(&self) -> &salsa::Runtime<Self> { | ||
23 | &self.runtime | ||
24 | } | ||
25 | } | ||
26 | |||
27 | impl std::panic::RefUnwindSafe for TestDB {} | ||
28 | |||
29 | impl 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 | |||
45 | impl 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)] |
176 | mod tests { | 176 | mod 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 { | |||
29 | pub mod debug; | 29 | pub mod debug; |
30 | 30 | ||
31 | pub mod db; | 31 | pub mod db; |
32 | #[macro_use] | ||
33 | pub mod mock; | ||
34 | pub mod source_binder; | 32 | pub mod source_binder; |
35 | 33 | ||
36 | mod ids; | 34 | mod ids; |
@@ -52,6 +50,8 @@ mod code_model; | |||
52 | pub mod from_source; | 50 | pub mod from_source; |
53 | 51 | ||
54 | #[cfg(test)] | 52 | #[cfg(test)] |
53 | mod test_db; | ||
54 | #[cfg(test)] | ||
55 | mod marks; | 55 | mod marks; |
56 | 56 | ||
57 | use hir_expand::AstId; | 57 | use 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 | |||
3 | use std::{panic, sync::Arc}; | ||
4 | |||
5 | use hir_expand::diagnostics::DiagnosticSink; | ||
6 | use parking_lot::Mutex; | ||
7 | use ra_cfg::CfgOptions; | ||
8 | use ra_db::{ | ||
9 | salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition, | ||
10 | RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, | ||
11 | }; | ||
12 | use rustc_hash::FxHashMap; | ||
13 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | ||
14 | |||
15 | use crate::{db, debug::HirDebugHelper}; | ||
16 | |||
17 | pub 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)] | ||
29 | pub 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 | |||
37 | impl panic::RefUnwindSafe for MockDatabase {} | ||
38 | |||
39 | impl 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 | |||
55 | impl 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 | |||
65 | impl 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 | |||
201 | impl 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 | |||
214 | impl 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 | |||
228 | impl 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 | |||
241 | impl 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 | |||
3 | use std::{panic, sync::Arc}; | ||
4 | |||
5 | use hir_def::{db::DefDatabase2, ModuleId}; | ||
6 | use hir_expand::diagnostics::DiagnosticSink; | ||
7 | use parking_lot::Mutex; | ||
8 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase}; | ||
9 | |||
10 | use 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)] | ||
22 | pub struct TestDB { | ||
23 | events: Mutex<Option<Vec<salsa::Event<TestDB>>>>, | ||
24 | runtime: salsa::Runtime<TestDB>, | ||
25 | } | ||
26 | |||
27 | impl 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 | |||
40 | impl 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 | |||
49 | impl panic::RefUnwindSafe for TestDB {} | ||
50 | |||
51 | impl 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 | ||
68 | impl 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 | |||
78 | impl 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 | |||
99 | impl 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::{ | |||
11 | use test_utils::covers; | 11 | use test_utils::covers; |
12 | 12 | ||
13 | use crate::{ | 13 | use 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] |
26 | fn cfg_impl_block() { | 26 | fn 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 |
30 | use foo::S as T; | 30 | use foo::S as T; |
@@ -64,7 +64,7 @@ impl S { | |||
64 | 64 | ||
65 | #[test] | 65 | #[test] |
66 | fn infer_await() { | 66 | fn 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] |
97 | fn infer_box() { | 97 | fn 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] |
124 | fn infer_adt_self() { | 124 | fn 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 |
128 | enum Nat { Succ(Self), Demo(Nat), Zero } | 128 | enum Nat { Succ(Self), Demo(Nat), Zero } |
@@ -141,7 +141,7 @@ fn test() { | |||
141 | 141 | ||
142 | #[test] | 142 | #[test] |
143 | fn infer_try() { | 143 | fn 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] |
183 | fn infer_for_loop() { | 183 | fn 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] |
224 | fn infer_while_let() { | 224 | fn 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 |
229 | enum Option<T> { Some(T), None } | 229 | enum Option<T> { Some(T), None } |
@@ -2484,7 +2484,7 @@ pub fn main_loop() { | |||
2484 | 2484 | ||
2485 | #[test] | 2485 | #[test] |
2486 | fn cross_crate_associated_method_call() { | 2486 | fn 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 |
2490 | fn test() { | 2490 | fn test() { |
@@ -3378,7 +3378,7 @@ fn test() { S.foo()<|>; } | |||
3378 | 3378 | ||
3379 | #[test] | 3379 | #[test] |
3380 | fn infer_macro_with_dollar_crate_is_correct_in_expr() { | 3380 | fn 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 |
3384 | fn test() { | 3384 | fn test() { |
@@ -3482,7 +3482,7 @@ fn test() { (&S).foo()<|>; } | |||
3482 | 3482 | ||
3483 | #[test] | 3483 | #[test] |
3484 | fn method_resolution_trait_from_prelude() { | 3484 | fn 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 |
3488 | struct S; | 3488 | struct 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 | ||
4654 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 4654 | fn 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 | ||
4662 | fn type_at(content: &str) -> String { | 4662 | fn 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 | ||
4667 | fn infer(content: &str) -> String { | 4667 | fn 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] |
4750 | fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | 4750 | fn 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] |
4790 | fn no_such_field_diagnostics() { | 4790 | fn 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: () } |