From 2c50f996b68e9a24a564de728ffcc13d896afc1c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 30 Dec 2019 17:31:15 +0100 Subject: more WIP --- crates/ra_hir_def/src/find_path.rs | 102 +++++++++++++++++++++++++++++++----- crates/ra_hir_def/src/item_scope.rs | 32 +++++++++++ 2 files changed, 122 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir_def/src') diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 1ddf5fca6..cc686ea6a 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -1,18 +1,35 @@ //! An algorithm to find a path to refer to a certain item. -use crate::{ModuleDefId, path::ModPath, ModuleId}; +use crate::{ + db::DefDatabase, + item_scope::ItemInNs, + path::{ModPath, PathKind}, + ModuleId, +}; -pub fn find_path(item: ModuleDefId, from: ModuleId) -> ModPath { +pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> ModPath { + // 1. Find all locations that the item could be imported from (i.e. that are visible) + // - this needs to consider other crates, for reexports from transitive dependencies + // - filter by visibility + // 2. For each of these, go up the module tree until we find an + // item/module/crate that is already in scope (including because it is in + // the prelude, and including aliases!) + // 3. Then select the one that gives the shortest path + let def_map = db.crate_def_map(from.krate); + let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; + if let Some((name, _)) = from_scope.reverse_get(item) { + return ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]); + } todo!() } #[cfg(test)] mod tests { use super::*; - use ra_db::{fixture::WithFixture, SourceDatabase}; - use crate::{db::DefDatabase, test_db::TestDB}; - use ra_syntax::ast::AstNode; + use crate::test_db::TestDB; use hir_expand::hygiene::Hygiene; + use ra_db::fixture::WithFixture; + use ra_syntax::ast::AstNode; /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the @@ -21,13 +38,26 @@ mod tests { let (db, pos) = TestDB::with_position(code); let module = db.module_for_file(pos.file_id); let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); - let ast_path = parsed_path_file.syntax_node().descendants().find_map(ra_syntax::ast::Path::cast).unwrap(); + let ast_path = parsed_path_file + .syntax_node() + .descendants() + .find_map(ra_syntax::ast::Path::cast) + .unwrap(); let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); let crate_def_map = db.crate_def_map(module.krate); - let resolved = crate_def_map.resolve_path(&db, module.local_id, &mod_path, crate::item_scope::BuiltinShadowMode::Module).0.take_types().unwrap(); + let resolved = crate_def_map + .resolve_path( + &db, + module.local_id, + &mod_path, + crate::item_scope::BuiltinShadowMode::Module, + ) + .0 + .take_types() + .unwrap(); - let found_path = find_path(resolved, module); + let found_path = find_path(&db, ItemInNs::Types(resolved), module); assert_eq!(mod_path, found_path); } @@ -35,10 +65,58 @@ mod tests { #[test] fn same_module() { let code = r#" -//- /main.rs -struct S; -<|> -"#; + //- /main.rs + struct S; + <|> + "#; check_found_path(code, "S"); } + + #[test] + fn sub_module() { + let code = r#" + //- /main.rs + mod foo { + pub struct S; + } + <|> + "#; + check_found_path(code, "foo::S"); + } + + #[test] + fn same_crate() { + let code = r#" + //- /main.rs + mod foo; + struct S; + //- /foo.rs + <|> + "#; + check_found_path(code, "crate::S"); + } + + #[test] + fn different_crate() { + let code = r#" + //- /main.rs crate:main deps:std + <|> + //- /std.rs crate:std + pub struct S; + "#; + check_found_path(code, "std::S"); + } + + #[test] + fn same_crate_reexport() { + let code = r#" + //- /main.rs + mod bar { + mod foo { pub(super) struct S; } + pub(crate) use foo::*; + } + <|> + "#; + check_found_path(code, "bar::S"); + } } diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fe7bb9779..f88502d78 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -104,6 +104,15 @@ impl ItemScope { } } + pub(crate) fn reverse_get(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { + for (name, per_ns) in &self.visible { + if let Some(vis) = item.match_with(*per_ns) { + return Some((name, vis)); + } + } + None + } + pub(crate) fn traits<'a>(&'a self) -> impl Iterator + 'a { self.visible.values().filter_map(|def| match def.take_types() { Some(ModuleDefId::TraitId(t)) => Some(t), @@ -173,3 +182,26 @@ impl PerNs { } } } + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum ItemInNs { + Types(ModuleDefId), + Values(ModuleDefId), + Macros(MacroDefId), +} + +impl ItemInNs { + fn match_with(self, per_ns: PerNs) -> Option { + match self { + ItemInNs::Types(def) => { + per_ns.types.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) + }, + ItemInNs::Values(def) => { + per_ns.values.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) + }, + ItemInNs::Macros(def) => { + per_ns.macros.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) + }, + } + } +} -- cgit v1.2.3