diff options
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 118 |
1 files changed, 113 insertions, 5 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index cc686ea6a..6772330e0 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -4,10 +4,18 @@ use crate::{ | |||
4 | db::DefDatabase, | 4 | db::DefDatabase, |
5 | item_scope::ItemInNs, | 5 | item_scope::ItemInNs, |
6 | path::{ModPath, PathKind}, | 6 | path::{ModPath, PathKind}, |
7 | ModuleId, | 7 | ModuleId, ModuleDefId, |
8 | }; | 8 | }; |
9 | use hir_expand::name::Name; | ||
9 | 10 | ||
10 | pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> ModPath { | 11 | // TODO handle prelude |
12 | // TODO handle enum variants | ||
13 | // TODO don't import from super imports? or at least deprioritize | ||
14 | // TODO use super? | ||
15 | // TODO use shortest path | ||
16 | // TODO performance / memoize | ||
17 | |||
18 | pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | ||
11 | // 1. Find all locations that the item could be imported from (i.e. that are visible) | 19 | // 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 | 20 | // - this needs to consider other crates, for reexports from transitive dependencies |
13 | // - filter by visibility | 21 | // - filter by visibility |
@@ -15,12 +23,63 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> ModPa | |||
15 | // item/module/crate that is already in scope (including because it is in | 23 | // item/module/crate that is already in scope (including because it is in |
16 | // the prelude, and including aliases!) | 24 | // the prelude, and including aliases!) |
17 | // 3. Then select the one that gives the shortest path | 25 | // 3. Then select the one that gives the shortest path |
26 | |||
27 | // Base cases: | ||
28 | |||
29 | // - if the item is already in scope, return the name under which it is | ||
18 | let def_map = db.crate_def_map(from.krate); | 30 | let def_map = db.crate_def_map(from.krate); |
19 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; | 31 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; |
20 | if let Some((name, _)) = from_scope.reverse_get(item) { | 32 | if let Some((name, _)) = from_scope.reverse_get(item) { |
21 | return ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]); | 33 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); |
34 | } | ||
35 | |||
36 | // - if the item is the crate root, return `crate` | ||
37 | if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: def_map.root })) { | ||
38 | return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new())); | ||
39 | } | ||
40 | |||
41 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude | ||
42 | for (name, def_id) in &def_map.extern_prelude { | ||
43 | if item == ItemInNs::Types(*def_id) { | ||
44 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); | ||
45 | } | ||
46 | } | ||
47 | |||
48 | // - if the item is in the prelude, return the name from there | ||
49 | // TODO check prelude | ||
50 | |||
51 | // Recursive case: | ||
52 | // - if the item is an enum variant, refer to it via the enum | ||
53 | |||
54 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | ||
55 | let importable_locations = find_importable_locations(db, item, from); | ||
56 | // XXX going in order for now | ||
57 | for (module_id, name) in importable_locations { | ||
58 | // TODO prevent infinite loops | ||
59 | let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from) { | ||
60 | None => continue, | ||
61 | Some(path) => path, | ||
62 | }; | ||
63 | path.segments.push(name); | ||
64 | return Some(path); | ||
65 | } | ||
66 | None | ||
67 | } | ||
68 | |||
69 | fn find_importable_locations(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Vec<(ModuleId, Name)> { | ||
70 | let crate_graph = db.crate_graph(); | ||
71 | let mut result = Vec::new(); | ||
72 | for krate in Some(from.krate).into_iter().chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) { | ||
73 | let def_map = db.crate_def_map(krate); | ||
74 | for (local_id, data) in def_map.modules.iter() { | ||
75 | if let Some((name, vis)) = data.scope.reverse_get(item) { | ||
76 | if vis.is_visible_from(db, from) { | ||
77 | result.push((ModuleId { krate, local_id }, name.clone())); | ||
78 | } | ||
79 | } | ||
80 | } | ||
22 | } | 81 | } |
23 | todo!() | 82 | result |
24 | } | 83 | } |
25 | 84 | ||
26 | #[cfg(test)] | 85 | #[cfg(test)] |
@@ -59,7 +118,7 @@ mod tests { | |||
59 | 118 | ||
60 | let found_path = find_path(&db, ItemInNs::Types(resolved), module); | 119 | let found_path = find_path(&db, ItemInNs::Types(resolved), module); |
61 | 120 | ||
62 | assert_eq!(mod_path, found_path); | 121 | assert_eq!(found_path, Some(mod_path)); |
63 | } | 122 | } |
64 | 123 | ||
65 | #[test] | 124 | #[test] |
@@ -85,6 +144,17 @@ mod tests { | |||
85 | } | 144 | } |
86 | 145 | ||
87 | #[test] | 146 | #[test] |
147 | fn crate_root() { | ||
148 | let code = r#" | ||
149 | //- /main.rs | ||
150 | mod foo; | ||
151 | //- /foo.rs | ||
152 | <|> | ||
153 | "#; | ||
154 | check_found_path(code, "crate"); | ||
155 | } | ||
156 | |||
157 | #[test] | ||
88 | fn same_crate() { | 158 | fn same_crate() { |
89 | let code = r#" | 159 | let code = r#" |
90 | //- /main.rs | 160 | //- /main.rs |
@@ -108,6 +178,18 @@ mod tests { | |||
108 | } | 178 | } |
109 | 179 | ||
110 | #[test] | 180 | #[test] |
181 | fn different_crate_renamed() { | ||
182 | let code = r#" | ||
183 | //- /main.rs crate:main deps:std | ||
184 | extern crate std as std_renamed; | ||
185 | <|> | ||
186 | //- /std.rs crate:std | ||
187 | pub struct S; | ||
188 | "#; | ||
189 | check_found_path(code, "std_renamed::S"); | ||
190 | } | ||
191 | |||
192 | #[test] | ||
111 | fn same_crate_reexport() { | 193 | fn same_crate_reexport() { |
112 | let code = r#" | 194 | let code = r#" |
113 | //- /main.rs | 195 | //- /main.rs |
@@ -119,4 +201,30 @@ mod tests { | |||
119 | "#; | 201 | "#; |
120 | check_found_path(code, "bar::S"); | 202 | check_found_path(code, "bar::S"); |
121 | } | 203 | } |
204 | |||
205 | #[test] | ||
206 | fn same_crate_reexport_rename() { | ||
207 | let code = r#" | ||
208 | //- /main.rs | ||
209 | mod bar { | ||
210 | mod foo { pub(super) struct S; } | ||
211 | pub(crate) use foo::S as U; | ||
212 | } | ||
213 | <|> | ||
214 | "#; | ||
215 | check_found_path(code, "bar::U"); | ||
216 | } | ||
217 | |||
218 | #[test] | ||
219 | fn prelude() { | ||
220 | let code = r#" | ||
221 | //- /main.rs crate:main deps:std | ||
222 | <|> | ||
223 | //- /std.rs crate:std | ||
224 | pub mod prelude { pub struct S; } | ||
225 | #[prelude_import] | ||
226 | pub use prelude::*; | ||
227 | "#; | ||
228 | check_found_path(code, "S"); | ||
229 | } | ||
122 | } | 230 | } |