diff options
-rw-r--r-- | crates/ra_hir/src/mock.rs | 47 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 48 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 126 | ||||
-rw-r--r-- | crates/ra_hir/src/path.rs | 2 |
4 files changed, 203 insertions, 20 deletions
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 8d176662c..c9af38009 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -15,6 +15,7 @@ pub(crate) struct MockDatabase { | |||
15 | events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>, | 15 | events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>, |
16 | runtime: salsa::Runtime<MockDatabase>, | 16 | runtime: salsa::Runtime<MockDatabase>, |
17 | id_maps: Arc<IdMaps>, | 17 | id_maps: Arc<IdMaps>, |
18 | file_counter: u32, | ||
18 | } | 19 | } |
19 | 20 | ||
20 | impl MockDatabase { | 21 | impl MockDatabase { |
@@ -27,7 +28,7 @@ impl MockDatabase { | |||
27 | pub(crate) fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) { | 28 | pub(crate) fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) { |
28 | let mut db = MockDatabase::default(); | 29 | let mut db = MockDatabase::default(); |
29 | let mut source_root = SourceRoot::default(); | 30 | let mut source_root = SourceRoot::default(); |
30 | let file_id = db.add_file(&mut source_root, "/main.rs", text); | 31 | let file_id = db.add_file(WORKSPACE, &mut source_root, "/main.rs", text); |
31 | db.query_mut(ra_db::SourceRootQuery) | 32 | db.query_mut(ra_db::SourceRootQuery) |
32 | .set(WORKSPACE, Arc::new(source_root.clone())); | 33 | .set(WORKSPACE, Arc::new(source_root.clone())); |
33 | 34 | ||
@@ -51,6 +52,16 @@ impl MockDatabase { | |||
51 | fn from_fixture(fixture: &str) -> (MockDatabase, SourceRoot, Option<FilePosition>) { | 52 | fn from_fixture(fixture: &str) -> (MockDatabase, SourceRoot, Option<FilePosition>) { |
52 | let mut db = MockDatabase::default(); | 53 | let mut db = MockDatabase::default(); |
53 | 54 | ||
55 | let (source_root, pos) = db.add_fixture(WORKSPACE, fixture); | ||
56 | |||
57 | (db, source_root, pos) | ||
58 | } | ||
59 | |||
60 | pub fn add_fixture( | ||
61 | &mut self, | ||
62 | source_root_id: SourceRootId, | ||
63 | fixture: &str, | ||
64 | ) -> (SourceRoot, Option<FilePosition>) { | ||
54 | let mut position = None; | 65 | let mut position = None; |
55 | let mut source_root = SourceRoot::default(); | 66 | let mut source_root = SourceRoot::default(); |
56 | for entry in parse_fixture(fixture) { | 67 | for entry in parse_fixture(fixture) { |
@@ -59,39 +70,51 @@ impl MockDatabase { | |||
59 | position.is_none(), | 70 | position.is_none(), |
60 | "only one marker (<|>) per fixture is allowed" | 71 | "only one marker (<|>) per fixture is allowed" |
61 | ); | 72 | ); |
62 | position = | 73 | position = Some(self.add_file_with_position( |
63 | Some(db.add_file_with_position(&mut source_root, &entry.meta, &entry.text)); | 74 | source_root_id, |
75 | &mut source_root, | ||
76 | &entry.meta, | ||
77 | &entry.text, | ||
78 | )); | ||
64 | } else { | 79 | } else { |
65 | db.add_file(&mut source_root, &entry.meta, &entry.text); | 80 | self.add_file(source_root_id, &mut source_root, &entry.meta, &entry.text); |
66 | } | 81 | } |
67 | } | 82 | } |
68 | db.query_mut(ra_db::SourceRootQuery) | 83 | self.query_mut(ra_db::SourceRootQuery) |
69 | .set(WORKSPACE, Arc::new(source_root.clone())); | 84 | .set(source_root_id, Arc::new(source_root.clone())); |
70 | (db, source_root, position) | 85 | (source_root, position) |
71 | } | 86 | } |
72 | 87 | ||
73 | fn add_file(&mut self, source_root: &mut SourceRoot, path: &str, text: &str) -> FileId { | 88 | fn add_file( |
89 | &mut self, | ||
90 | source_root_id: SourceRootId, | ||
91 | source_root: &mut SourceRoot, | ||
92 | path: &str, | ||
93 | text: &str, | ||
94 | ) -> FileId { | ||
74 | assert!(path.starts_with('/')); | 95 | assert!(path.starts_with('/')); |
75 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 96 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
76 | let file_id = FileId(source_root.files.len() as u32); | 97 | let file_id = FileId(self.file_counter); |
98 | self.file_counter += 1; | ||
77 | let text = Arc::new(text.to_string()); | 99 | let text = Arc::new(text.to_string()); |
78 | self.query_mut(ra_db::FileTextQuery).set(file_id, text); | 100 | self.query_mut(ra_db::FileTextQuery).set(file_id, text); |
79 | self.query_mut(ra_db::FileRelativePathQuery) | 101 | self.query_mut(ra_db::FileRelativePathQuery) |
80 | .set(file_id, path.clone()); | 102 | .set(file_id, path.clone()); |
81 | self.query_mut(ra_db::FileSourceRootQuery) | 103 | self.query_mut(ra_db::FileSourceRootQuery) |
82 | .set(file_id, WORKSPACE); | 104 | .set(file_id, source_root_id); |
83 | source_root.files.insert(path, file_id); | 105 | source_root.files.insert(path, file_id); |
84 | file_id | 106 | file_id |
85 | } | 107 | } |
86 | 108 | ||
87 | fn add_file_with_position( | 109 | fn add_file_with_position( |
88 | &mut self, | 110 | &mut self, |
111 | source_root_id: SourceRootId, | ||
89 | source_root: &mut SourceRoot, | 112 | source_root: &mut SourceRoot, |
90 | path: &str, | 113 | path: &str, |
91 | text: &str, | 114 | text: &str, |
92 | ) -> FilePosition { | 115 | ) -> FilePosition { |
93 | let (offset, text) = extract_offset(text); | 116 | let (offset, text) = extract_offset(text); |
94 | let file_id = self.add_file(source_root, path, &text); | 117 | let file_id = self.add_file(source_root_id, source_root, path, &text); |
95 | FilePosition { file_id, offset } | 118 | FilePosition { file_id, offset } |
96 | } | 119 | } |
97 | } | 120 | } |
@@ -121,6 +144,7 @@ impl Default for MockDatabase { | |||
121 | events: Default::default(), | 144 | events: Default::default(), |
122 | runtime: salsa::Runtime::default(), | 145 | runtime: salsa::Runtime::default(), |
123 | id_maps: Default::default(), | 146 | id_maps: Default::default(), |
147 | file_counter: 0, | ||
124 | }; | 148 | }; |
125 | db.query_mut(ra_db::CrateGraphQuery) | 149 | db.query_mut(ra_db::CrateGraphQuery) |
126 | .set((), Default::default()); | 150 | .set((), Default::default()); |
@@ -138,6 +162,7 @@ impl salsa::ParallelDatabase for MockDatabase { | |||
138 | events: Default::default(), | 162 | events: Default::default(), |
139 | runtime: self.runtime.snapshot(self), | 163 | runtime: self.runtime.snapshot(self), |
140 | id_maps: self.id_maps.clone(), | 164 | id_maps: self.id_maps.clone(), |
165 | file_counter: self.file_counter, | ||
141 | }) | 166 | }) |
142 | } | 167 | } |
143 | } | 168 | } |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 4181bf4b8..20adc9ec4 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -433,6 +433,7 @@ where | |||
433 | continue; | 433 | continue; |
434 | } | 434 | } |
435 | if self.resolve_import(module_id, import)? { | 435 | if self.resolve_import(module_id, import)? { |
436 | log::debug!("import {:?} resolved (or definite error)", import); | ||
436 | self.processed_imports.insert((module_id, i)); | 437 | self.processed_imports.insert((module_id, i)); |
437 | } | 438 | } |
438 | } | 439 | } |
@@ -440,6 +441,7 @@ where | |||
440 | } | 441 | } |
441 | 442 | ||
442 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable<bool> { | 443 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable<bool> { |
444 | log::debug!("resolving import: {:?}", import); | ||
443 | let ptr = match import.kind { | 445 | let ptr = match import.kind { |
444 | ImportKind::Glob => return Ok(false), | 446 | ImportKind::Glob => return Ok(false), |
445 | ImportKind::Named(ptr) => ptr, | 447 | ImportKind::Named(ptr) => ptr, |
@@ -450,8 +452,11 @@ where | |||
450 | PathKind::Super => { | 452 | PathKind::Super => { |
451 | match module_id.parent(&self.module_tree) { | 453 | match module_id.parent(&self.module_tree) { |
452 | Some(it) => it, | 454 | Some(it) => it, |
453 | // TODO: error | 455 | None => { |
454 | None => return Ok(true), // this can't suddenly resolve if we just resolve some other imports | 456 | // TODO: error |
457 | log::debug!("super path in root module"); | ||
458 | return Ok(true); // this can't suddenly resolve if we just resolve some other imports | ||
459 | } | ||
455 | } | 460 | } |
456 | } | 461 | } |
457 | PathKind::Crate => module_id.crate_root(&self.module_tree), | 462 | PathKind::Crate => module_id.crate_root(&self.module_tree), |
@@ -462,13 +467,20 @@ where | |||
462 | 467 | ||
463 | let def_id = match self.result.per_module[&curr].items.get(name) { | 468 | let def_id = match self.result.per_module[&curr].items.get(name) { |
464 | Some(res) if !res.def_id.is_none() => res.def_id, | 469 | Some(res) if !res.def_id.is_none() => res.def_id, |
465 | _ => return Ok(false), | 470 | _ => { |
471 | log::debug!("path segment {:?} not found", name); | ||
472 | return Ok(false); | ||
473 | } | ||
466 | }; | 474 | }; |
467 | 475 | ||
468 | if !is_last { | 476 | if !is_last { |
469 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { | 477 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { |
470 | d | 478 | d |
471 | } else { | 479 | } else { |
480 | log::debug!( | ||
481 | "path segment {:?} resolved to value only, but is not last", | ||
482 | name | ||
483 | ); | ||
472 | return Ok(false); | 484 | return Ok(false); |
473 | }; | 485 | }; |
474 | curr = match type_def_id.loc(self.db) { | 486 | curr = match type_def_id.loc(self.db) { |
@@ -486,27 +498,49 @@ where | |||
486 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | 498 | segments: import.path.segments[i + 1..].iter().cloned().collect(), |
487 | kind: PathKind::Crate, | 499 | kind: PathKind::Crate, |
488 | }; | 500 | }; |
501 | log::debug!("resolving {:?} in other source root", path); | ||
489 | let def_id = module.resolve_path(self.db, &path)?; | 502 | let def_id = module.resolve_path(self.db, &path)?; |
490 | if !def_id.is_none() { | 503 | if !def_id.is_none() { |
504 | let name = path.segments.last().unwrap(); | ||
491 | self.update(module_id, |items| { | 505 | self.update(module_id, |items| { |
492 | let res = Resolution { | 506 | let res = Resolution { |
493 | def_id: def_id, | 507 | def_id, |
494 | import: Some(ptr), | 508 | import: Some(ptr), |
495 | }; | 509 | }; |
496 | items.items.insert(name.clone(), res); | 510 | items.items.insert(name.clone(), res); |
497 | }); | 511 | }); |
512 | log::debug!( | ||
513 | "resolved import {:?} ({:?}) cross-source root to {:?}", | ||
514 | name, | ||
515 | import, | ||
516 | def_id.map(|did| did.loc(self.db)) | ||
517 | ); | ||
498 | return Ok(true); | 518 | return Ok(true); |
499 | } else { | 519 | } else { |
500 | return Ok(false); | 520 | log::debug!("rest of path did not resolve in other source root"); |
521 | return Ok(true); | ||
501 | } | 522 | } |
502 | } | 523 | } |
503 | } | 524 | } |
504 | _ => return Ok(true), // this resolved to a non-module, so the path won't ever resolve | 525 | _ => { |
526 | log::debug!( | ||
527 | "path segment {:?} resolved to non-module {:?}, but is not last", | ||
528 | name, | ||
529 | type_def_id.loc(self.db) | ||
530 | ); | ||
531 | return Ok(true); // this resolved to a non-module, so the path won't ever resolve | ||
532 | } | ||
505 | } | 533 | } |
506 | } else { | 534 | } else { |
535 | log::debug!( | ||
536 | "resolved import {:?} ({:?}) within source root to {:?}", | ||
537 | name, | ||
538 | import, | ||
539 | def_id.map(|did| did.loc(self.db)) | ||
540 | ); | ||
507 | self.update(module_id, |items| { | 541 | self.update(module_id, |items| { |
508 | let res = Resolution { | 542 | let res = Resolution { |
509 | def_id: def_id, | 543 | def_id, |
510 | import: Some(ptr), | 544 | import: Some(ptr), |
511 | }; | 545 | }; |
512 | items.items.insert(name.clone(), res); | 546 | items.items.insert(name.clone(), res); |
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 82222d1ad..c511c40b2 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use salsa::Database; | 3 | use salsa::Database; |
4 | use ra_db::{FilesDatabase, CrateGraph}; | 4 | use ra_db::{FilesDatabase, CrateGraph, SourceRootId}; |
5 | use relative_path::RelativePath; | 5 | use relative_path::RelativePath; |
6 | use test_utils::assert_eq_text; | 6 | use test_utils::assert_eq_text; |
7 | 7 | ||
@@ -79,6 +79,35 @@ fn item_map_smoke_test() { | |||
79 | } | 79 | } |
80 | 80 | ||
81 | #[test] | 81 | #[test] |
82 | fn use_trees() { | ||
83 | let (item_map, module_id) = item_map( | ||
84 | " | ||
85 | //- /lib.rs | ||
86 | mod foo; | ||
87 | |||
88 | use crate::foo::bar::{Baz, Quux}; | ||
89 | <|> | ||
90 | |||
91 | //- /foo/mod.rs | ||
92 | pub mod bar; | ||
93 | |||
94 | //- /foo/bar.rs | ||
95 | pub struct Baz; | ||
96 | pub enum Quux {}; | ||
97 | ", | ||
98 | ); | ||
99 | check_module_item_map( | ||
100 | &item_map, | ||
101 | module_id, | ||
102 | " | ||
103 | Baz: t v | ||
104 | Quux: t | ||
105 | foo: t | ||
106 | ", | ||
107 | ); | ||
108 | } | ||
109 | |||
110 | #[test] | ||
82 | fn re_exports() { | 111 | fn re_exports() { |
83 | let (item_map, module_id) = item_map( | 112 | let (item_map, module_id) = item_map( |
84 | " | 113 | " |
@@ -199,6 +228,101 @@ fn item_map_across_crates() { | |||
199 | } | 228 | } |
200 | 229 | ||
201 | #[test] | 230 | #[test] |
231 | fn import_across_source_roots() { | ||
232 | let (mut db, sr) = MockDatabase::with_files( | ||
233 | " | ||
234 | //- /lib.rs | ||
235 | pub mod a { | ||
236 | pub mod b { | ||
237 | pub struct C; | ||
238 | } | ||
239 | } | ||
240 | ", | ||
241 | ); | ||
242 | let lib_id = sr.files[RelativePath::new("/lib.rs")]; | ||
243 | |||
244 | let source_root = SourceRootId(1); | ||
245 | |||
246 | let (sr2, pos) = db.add_fixture( | ||
247 | source_root, | ||
248 | " | ||
249 | //- /main.rs | ||
250 | use test_crate::a::b::C; | ||
251 | ", | ||
252 | ); | ||
253 | assert!(pos.is_none()); | ||
254 | |||
255 | let main_id = sr2.files[RelativePath::new("/main.rs")]; | ||
256 | |||
257 | eprintln!("lib = {:?}, main = {:?}", lib_id, main_id); | ||
258 | |||
259 | let mut crate_graph = CrateGraph::default(); | ||
260 | let main_crate = crate_graph.add_crate_root(main_id); | ||
261 | let lib_crate = crate_graph.add_crate_root(lib_id); | ||
262 | crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate); | ||
263 | |||
264 | db.set_crate_graph(crate_graph); | ||
265 | |||
266 | let module = crate::source_binder::module_from_file_id(&db, main_id) | ||
267 | .unwrap() | ||
268 | .unwrap(); | ||
269 | let module_id = module.def_id.loc(&db).module_id; | ||
270 | let item_map = db.item_map(source_root).unwrap(); | ||
271 | |||
272 | check_module_item_map( | ||
273 | &item_map, | ||
274 | module_id, | ||
275 | " | ||
276 | C: t v | ||
277 | test_crate: t | ||
278 | ", | ||
279 | ); | ||
280 | } | ||
281 | |||
282 | #[test] | ||
283 | fn reexport_across_crates() { | ||
284 | let (mut db, sr) = MockDatabase::with_files( | ||
285 | " | ||
286 | //- /main.rs | ||
287 | use test_crate::Baz; | ||
288 | |||
289 | //- /lib.rs | ||
290 | pub use foo::Baz; | ||
291 | |||
292 | mod foo; | ||
293 | |||
294 | //- /foo.rs | ||
295 | pub struct Baz; | ||
296 | ", | ||
297 | ); | ||
298 | let main_id = sr.files[RelativePath::new("/main.rs")]; | ||
299 | let lib_id = sr.files[RelativePath::new("/lib.rs")]; | ||
300 | |||
301 | let mut crate_graph = CrateGraph::default(); | ||
302 | let main_crate = crate_graph.add_crate_root(main_id); | ||
303 | let lib_crate = crate_graph.add_crate_root(lib_id); | ||
304 | crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate); | ||
305 | |||
306 | db.set_crate_graph(crate_graph); | ||
307 | |||
308 | let source_root = db.file_source_root(main_id); | ||
309 | let module = crate::source_binder::module_from_file_id(&db, main_id) | ||
310 | .unwrap() | ||
311 | .unwrap(); | ||
312 | let module_id = module.def_id.loc(&db).module_id; | ||
313 | let item_map = db.item_map(source_root).unwrap(); | ||
314 | |||
315 | check_module_item_map( | ||
316 | &item_map, | ||
317 | module_id, | ||
318 | " | ||
319 | Baz: t v | ||
320 | test_crate: t | ||
321 | ", | ||
322 | ); | ||
323 | } | ||
324 | |||
325 | #[test] | ||
202 | fn typing_inside_a_function_should_not_invalidate_item_map() { | 326 | fn typing_inside_a_function_should_not_invalidate_item_map() { |
203 | let (mut db, pos) = MockDatabase::with_position( | 327 | let (mut db, pos) = MockDatabase::with_position( |
204 | " | 328 | " |
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 6f0b0da97..370e10bb8 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -150,7 +150,7 @@ fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> { | |||
150 | let prefix = if let Some(qual) = path.qualifier() { | 150 | let prefix = if let Some(qual) = path.qualifier() { |
151 | Some(convert_path(prefix, qual)?) | 151 | Some(convert_path(prefix, qual)?) |
152 | } else { | 152 | } else { |
153 | None | 153 | prefix |
154 | }; | 154 | }; |
155 | let segment = path.segment()?; | 155 | let segment = path.segment()?; |
156 | let res = match segment.kind()? { | 156 | let res = match segment.kind()? { |