aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/mock.rs47
-rw-r--r--crates/ra_hir/src/nameres.rs48
-rw-r--r--crates/ra_hir/src/nameres/tests.rs126
-rw-r--r--crates/ra_hir/src/path.rs2
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
20impl MockDatabase { 21impl 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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use salsa::Database; 3use salsa::Database;
4use ra_db::{FilesDatabase, CrateGraph}; 4use ra_db::{FilesDatabase, CrateGraph, SourceRootId};
5use relative_path::RelativePath; 5use relative_path::RelativePath;
6use test_utils::assert_eq_text; 6use test_utils::assert_eq_text;
7 7
@@ -79,6 +79,35 @@ fn item_map_smoke_test() {
79} 79}
80 80
81#[test] 81#[test]
82fn 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]
82fn re_exports() { 111fn 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]
231fn 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]
283fn 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]
202fn typing_inside_a_function_should_not_invalidate_item_map() { 326fn 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()? {