diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 102 | ||||
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 32 |
2 files changed, 122 insertions, 12 deletions
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 @@ | |||
1 | //! An algorithm to find a path to refer to a certain item. | 1 | //! An algorithm to find a path to refer to a certain item. |
2 | 2 | ||
3 | use crate::{ModuleDefId, path::ModPath, ModuleId}; | 3 | use crate::{ |
4 | db::DefDatabase, | ||
5 | item_scope::ItemInNs, | ||
6 | path::{ModPath, PathKind}, | ||
7 | ModuleId, | ||
8 | }; | ||
4 | 9 | ||
5 | pub fn find_path(item: ModuleDefId, from: ModuleId) -> ModPath { | 10 | pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> ModPath { |
11 | // 1. Find all locations that the item could be imported from (i.e. that are visible) | ||
12 | // - this needs to consider other crates, for reexports from transitive dependencies | ||
13 | // - filter by visibility | ||
14 | // 2. For each of these, go up the module tree until we find an | ||
15 | // item/module/crate that is already in scope (including because it is in | ||
16 | // the prelude, and including aliases!) | ||
17 | // 3. Then select the one that gives the shortest path | ||
18 | let def_map = db.crate_def_map(from.krate); | ||
19 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; | ||
20 | if let Some((name, _)) = from_scope.reverse_get(item) { | ||
21 | return ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]); | ||
22 | } | ||
6 | todo!() | 23 | todo!() |
7 | } | 24 | } |
8 | 25 | ||
9 | #[cfg(test)] | 26 | #[cfg(test)] |
10 | mod tests { | 27 | mod tests { |
11 | use super::*; | 28 | use super::*; |
12 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 29 | use crate::test_db::TestDB; |
13 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
14 | use ra_syntax::ast::AstNode; | ||
15 | use hir_expand::hygiene::Hygiene; | 30 | use hir_expand::hygiene::Hygiene; |
31 | use ra_db::fixture::WithFixture; | ||
32 | use ra_syntax::ast::AstNode; | ||
16 | 33 | ||
17 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 34 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
18 | /// item the `path` refers to returns that same path when called from the | 35 | /// item the `path` refers to returns that same path when called from the |
@@ -21,13 +38,26 @@ mod tests { | |||
21 | let (db, pos) = TestDB::with_position(code); | 38 | let (db, pos) = TestDB::with_position(code); |
22 | let module = db.module_for_file(pos.file_id); | 39 | let module = db.module_for_file(pos.file_id); |
23 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); | 40 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); |
24 | let ast_path = parsed_path_file.syntax_node().descendants().find_map(ra_syntax::ast::Path::cast).unwrap(); | 41 | let ast_path = parsed_path_file |
42 | .syntax_node() | ||
43 | .descendants() | ||
44 | .find_map(ra_syntax::ast::Path::cast) | ||
45 | .unwrap(); | ||
25 | let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); | 46 | let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); |
26 | 47 | ||
27 | let crate_def_map = db.crate_def_map(module.krate); | 48 | let crate_def_map = db.crate_def_map(module.krate); |
28 | let resolved = crate_def_map.resolve_path(&db, module.local_id, &mod_path, crate::item_scope::BuiltinShadowMode::Module).0.take_types().unwrap(); | 49 | let resolved = crate_def_map |
50 | .resolve_path( | ||
51 | &db, | ||
52 | module.local_id, | ||
53 | &mod_path, | ||
54 | crate::item_scope::BuiltinShadowMode::Module, | ||
55 | ) | ||
56 | .0 | ||
57 | .take_types() | ||
58 | .unwrap(); | ||
29 | 59 | ||
30 | let found_path = find_path(resolved, module); | 60 | let found_path = find_path(&db, ItemInNs::Types(resolved), module); |
31 | 61 | ||
32 | assert_eq!(mod_path, found_path); | 62 | assert_eq!(mod_path, found_path); |
33 | } | 63 | } |
@@ -35,10 +65,58 @@ mod tests { | |||
35 | #[test] | 65 | #[test] |
36 | fn same_module() { | 66 | fn same_module() { |
37 | let code = r#" | 67 | let code = r#" |
38 | //- /main.rs | 68 | //- /main.rs |
39 | struct S; | 69 | struct S; |
40 | <|> | 70 | <|> |
41 | "#; | 71 | "#; |
42 | check_found_path(code, "S"); | 72 | check_found_path(code, "S"); |
43 | } | 73 | } |
74 | |||
75 | #[test] | ||
76 | fn sub_module() { | ||
77 | let code = r#" | ||
78 | //- /main.rs | ||
79 | mod foo { | ||
80 | pub struct S; | ||
81 | } | ||
82 | <|> | ||
83 | "#; | ||
84 | check_found_path(code, "foo::S"); | ||
85 | } | ||
86 | |||
87 | #[test] | ||
88 | fn same_crate() { | ||
89 | let code = r#" | ||
90 | //- /main.rs | ||
91 | mod foo; | ||
92 | struct S; | ||
93 | //- /foo.rs | ||
94 | <|> | ||
95 | "#; | ||
96 | check_found_path(code, "crate::S"); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn different_crate() { | ||
101 | let code = r#" | ||
102 | //- /main.rs crate:main deps:std | ||
103 | <|> | ||
104 | //- /std.rs crate:std | ||
105 | pub struct S; | ||
106 | "#; | ||
107 | check_found_path(code, "std::S"); | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn same_crate_reexport() { | ||
112 | let code = r#" | ||
113 | //- /main.rs | ||
114 | mod bar { | ||
115 | mod foo { pub(super) struct S; } | ||
116 | pub(crate) use foo::*; | ||
117 | } | ||
118 | <|> | ||
119 | "#; | ||
120 | check_found_path(code, "bar::S"); | ||
121 | } | ||
44 | } | 122 | } |
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 { | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | 106 | ||
107 | pub(crate) fn reverse_get(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | ||
108 | for (name, per_ns) in &self.visible { | ||
109 | if let Some(vis) = item.match_with(*per_ns) { | ||
110 | return Some((name, vis)); | ||
111 | } | ||
112 | } | ||
113 | None | ||
114 | } | ||
115 | |||
107 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | 116 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { |
108 | self.visible.values().filter_map(|def| match def.take_types() { | 117 | self.visible.values().filter_map(|def| match def.take_types() { |
109 | Some(ModuleDefId::TraitId(t)) => Some(t), | 118 | Some(ModuleDefId::TraitId(t)) => Some(t), |
@@ -173,3 +182,26 @@ impl PerNs { | |||
173 | } | 182 | } |
174 | } | 183 | } |
175 | } | 184 | } |
185 | |||
186 | #[derive(Clone, Copy, PartialEq, Eq)] | ||
187 | pub enum ItemInNs { | ||
188 | Types(ModuleDefId), | ||
189 | Values(ModuleDefId), | ||
190 | Macros(MacroDefId), | ||
191 | } | ||
192 | |||
193 | impl ItemInNs { | ||
194 | fn match_with(self, per_ns: PerNs) -> Option<Visibility> { | ||
195 | match self { | ||
196 | ItemInNs::Types(def) => { | ||
197 | per_ns.types.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
198 | }, | ||
199 | ItemInNs::Values(def) => { | ||
200 | per_ns.values.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
201 | }, | ||
202 | ItemInNs::Macros(def) => { | ||
203 | per_ns.macros.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
204 | }, | ||
205 | } | ||
206 | } | ||
207 | } | ||