diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs index 2840f96558..2f7551597f 100644 --- a/crates/ra_db/src/mock.rs +++ b/crates/ra_db/src/mock.rs @@ -5,7 +5,7 @@ use relative_path::{RelativePath, RelativePathBuf}; use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct FileMap(Vec<(FileId, RelativePathBuf)>); impl FileMap { @@ -28,6 +28,11 @@ impl FileMap { self.iter().map(|(id, _)| id).collect() } + pub fn file_id(&self, path: &str) -> FileId { + assert!(path.starts_with('/')); + self.iter().find(|(_, p)| p == &path[1..]).unwrap().0 + } + fn iter<'a>(&'a self) -> impl Iterator + 'a { self.0 .iter() diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index e855df11d1..b7193c4f3f 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use parking_lot::Mutex; use salsa::{self, Database}; -use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE}; +use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE, CrateGraph}; use relative_path::RelativePathBuf; use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; @@ -16,7 +16,24 @@ pub(crate) struct MockDatabase { } impl MockDatabase { + pub(crate) fn with_files(fixture: &str) -> (MockDatabase, FileMap) { + let (db, file_map, position) = MockDatabase::from_fixture(fixture); + assert!(position.is_none()); + (db, file_map) + } + pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { + let (db, _, position) = MockDatabase::from_fixture(fixture); + let position = position.expect("expected a marker ( <|> )"); + (db, position) + } + + pub(crate) fn set_crate_graph(&mut self, crate_graph: CrateGraph) { + self.query_mut(ra_db::CrateGraphQuery) + .set((), Arc::new(crate_graph)); + } + + fn from_fixture(fixture: &str) -> (MockDatabase, FileMap, Option) { let mut db = MockDatabase::default(); let mut position = None; @@ -32,11 +49,10 @@ impl MockDatabase { db.add_file(&mut file_map, &entry.meta, &entry.text); } } - let position = position.expect("expected a marker (<|>)"); - let source_root = file_map.into_source_root(); + let source_root = file_map.clone().into_source_root(); db.query_mut(ra_db::SourceRootQuery) .set(WORKSPACE, Arc::new(source_root)); - (db, position) + (db, file_map, position) } fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId { diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 5e4bb76690..9afeade9e5 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs @@ -228,7 +228,7 @@ where pub(crate) fn resolve(mut self) -> Cancelable { for (&module_id, items) in self.input.iter() { - self.populate_module(module_id, items) + self.populate_module(module_id, items)?; } for &module_id in self.input.keys() { @@ -238,11 +238,25 @@ where Ok(self.result) } - fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { + fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> { let file_id = module_id.source(&self.module_tree).file_id(); let mut module_items = ModuleScope::default(); + // Populate extern crates prelude + { + let root_id = module_id.crate_root(&self.module_tree); + let file_id = root_id.source(&self.module_tree).file_id(); + let crate_graph = self.db.crate_graph(); + if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) { + let krate = Crate::new(crate_id); + for dep in krate.dependencies(self.db) { + if let Some(module) = dep.krate.root_module(self.db)? { + self.add_module_item(&mut module_items, dep.name, module.module_id); + } + } + }; + } for import in input.imports.iter() { if let Some(name) = import.path.segments.iter().last() { if let ImportKind::Named(import) = import.kind { @@ -256,10 +270,9 @@ where } } } - + // Populate explicitelly declared items, except modules for item in input.items.iter() { if item.kind == MODULE { - // handle submodules separatelly continue; } let def_loc = DefLoc { @@ -279,22 +292,28 @@ where module_items.items.insert(item.name.clone(), resolution); } + // Populate modules for (name, module_id) in module_id.children(&self.module_tree) { - let def_loc = DefLoc { - kind: DefKind::Module, - source_root_id: self.source_root, - module_id, - source_item_id: module_id.source(&self.module_tree).0, - }; - let def_id = def_loc.id(self.db); - let resolution = Resolution { - def_id: Some(def_id), - import: None, - }; - module_items.items.insert(name, resolution); + self.add_module_item(&mut module_items, name, module_id); } self.result.per_module.insert(module_id, module_items); + Ok(()) + } + + fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, module_id: ModuleId) { + let def_loc = DefLoc { + kind: DefKind::Module, + source_root_id: self.source_root, + module_id, + source_item_id: module_id.source(&self.module_tree).0, + }; + let def_id = def_loc.id(self.db); + let resolution = Resolution { + def_id: Some(def_id), + import: None, + }; + module_items.items.insert(name, resolution); } fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> { @@ -309,35 +328,9 @@ where ImportKind::Glob => return Ok(()), ImportKind::Named(ptr) => ptr, }; - let mut segments = import.path.segments.iter().enumerate(); let mut curr = match import.path.kind { - PathKind::Plain => { - let root_id = module_id.crate_root(&self.module_tree); - let file_id = root_id.source(&self.module_tree).file_id(); - let crate_graph = self.db.crate_graph(); - let crate_id = match crate_graph.crate_id_for_crate_root(file_id) { - None => return Ok(()), - Some(it) => it, - }; - let krate = Crate::new(crate_id); - let crate_name = match segments.next() { - None => return Ok(()), - Some((_, it)) => it, - }; - match krate - .dependencies(self.db) - .into_iter() - .find(|it| &it.name == crate_name) - { - None => return Ok(()), - Some(dep) => match dep.krate.root_module(self.db)? { - None => return Ok(()), - Some(it) => it.module_id, - }, - } - } - PathKind::Self_ => module_id, + PathKind::Plain | PathKind::Self_ => module_id, PathKind::Super => { match module_id.parent(&self.module_tree) { Some(it) => it, @@ -348,7 +341,7 @@ where PathKind::Crate => module_id.crate_root(&self.module_tree), }; - for (i, name) in segments { + for (i, name) in import.path.segments.iter().enumerate() { let is_last = i == import.path.segments.len() - 1; let def_id = match self.result.per_module[&curr].items.get(name) { diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index 060683e276..9ddc32dcd9 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use salsa::Database; -use ra_db::FilesDatabase; +use ra_db::{FilesDatabase, CrateGraph}; use ra_syntax::SmolStr; use crate::{ @@ -21,7 +21,7 @@ fn item_map(fixture: &str) -> (Arc, hir::ModuleId) { } #[test] -fn test_item_map() { +fn item_map_smoke_test() { let (item_map, module_id) = item_map( " //- /lib.rs @@ -42,6 +42,39 @@ fn test_item_map() { assert!(resolution.def_id.is_some()); } +#[test] +fn item_map_across_crates() { + let (mut db, files) = MockDatabase::with_files( + " + //- /main.rs + use test_crate::Baz; + + //- /lib.rs + pub struct Baz; + ", + ); + let main_id = files.file_id("/main.rs"); + let lib_id = files.file_id("/lib.rs"); + + let mut crate_graph = CrateGraph::default(); + let main_crate = crate_graph.add_crate_root(main_id); + let lib_crate = crate_graph.add_crate_root(lib_id); + crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate); + + db.set_crate_graph(crate_graph); + + let source_root = db.file_source_root(main_id); + let module = hir::source_binder::module_from_file_id(&db, main_id) + .unwrap() + .unwrap(); + let module_id = module.module_id; + let item_map = db.item_map(source_root).unwrap(); + + let name = SmolStr::from("Baz"); + let resolution = &item_map.per_module[&module_id].items[&name]; + assert!(resolution.def_id.is_some()); +} + #[test] fn typing_inside_a_function_should_not_invalidate_item_map() { let (mut db, pos) = MockDatabase::with_position(