diff options
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 70 | ||||
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 8 |
2 files changed, 67 insertions, 11 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 6772330e0..afcf04280 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -8,22 +8,12 @@ use crate::{ | |||
8 | }; | 8 | }; |
9 | use hir_expand::name::Name; | 9 | use hir_expand::name::Name; |
10 | 10 | ||
11 | // TODO handle prelude | ||
12 | // TODO handle enum variants | ||
13 | // TODO don't import from super imports? or at least deprioritize | 11 | // TODO don't import from super imports? or at least deprioritize |
14 | // TODO use super? | 12 | // TODO use super? |
15 | // TODO use shortest path | 13 | // TODO use shortest path |
16 | // TODO performance / memoize | 14 | // TODO performance / memoize |
17 | 15 | ||
18 | pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 16 | pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
19 | // 1. Find all locations that the item could be imported from (i.e. that are visible) | ||
20 | // - this needs to consider other crates, for reexports from transitive dependencies | ||
21 | // - filter by visibility | ||
22 | // 2. For each of these, go up the module tree until we find an | ||
23 | // item/module/crate that is already in scope (including because it is in | ||
24 | // the prelude, and including aliases!) | ||
25 | // 3. Then select the one that gives the shortest path | ||
26 | |||
27 | // Base cases: | 17 | // Base cases: |
28 | 18 | ||
29 | // - if the item is already in scope, return the name under which it is | 19 | // - if the item is already in scope, return the name under which it is |
@@ -46,10 +36,28 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio | |||
46 | } | 36 | } |
47 | 37 | ||
48 | // - if the item is in the prelude, return the name from there | 38 | // - if the item is in the prelude, return the name from there |
49 | // TODO check prelude | 39 | if let Some(prelude_module) = def_map.prelude { |
40 | let prelude_def_map = db.crate_def_map(prelude_module.krate); | ||
41 | let prelude_scope: &crate::item_scope::ItemScope = &prelude_def_map.modules[prelude_module.local_id].scope; | ||
42 | if let Some((name, vis)) = prelude_scope.reverse_get(item) { | ||
43 | if vis.is_visible_from(db, from) { | ||
44 | return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); | ||
45 | } | ||
46 | } | ||
47 | } | ||
50 | 48 | ||
51 | // Recursive case: | 49 | // Recursive case: |
52 | // - if the item is an enum variant, refer to it via the enum | 50 | // - if the item is an enum variant, refer to it via the enum |
51 | if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { | ||
52 | if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) { | ||
53 | let data = db.enum_data(variant.parent); | ||
54 | path.segments.push(data.variants[variant.local_id].name.clone()); | ||
55 | return Some(path); | ||
56 | } | ||
57 | // If this doesn't work, it seems we have no way of referring to the | ||
58 | // enum; that's very weird, but there might still be a reexport of the | ||
59 | // variant somewhere | ||
60 | } | ||
53 | 61 | ||
54 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | 62 | // - otherwise, look for modules containing (reexporting) it and import it from one of those |
55 | let importable_locations = find_importable_locations(db, item, from); | 63 | let importable_locations = find_importable_locations(db, item, from); |
@@ -132,6 +140,16 @@ mod tests { | |||
132 | } | 140 | } |
133 | 141 | ||
134 | #[test] | 142 | #[test] |
143 | fn enum_variant() { | ||
144 | let code = r#" | ||
145 | //- /main.rs | ||
146 | enum E { A } | ||
147 | <|> | ||
148 | "#; | ||
149 | check_found_path(code, "E::A"); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
135 | fn sub_module() { | 153 | fn sub_module() { |
136 | let code = r#" | 154 | let code = r#" |
137 | //- /main.rs | 155 | //- /main.rs |
@@ -216,6 +234,19 @@ mod tests { | |||
216 | } | 234 | } |
217 | 235 | ||
218 | #[test] | 236 | #[test] |
237 | fn different_crate_reexport() { | ||
238 | let code = r#" | ||
239 | //- /main.rs crate:main deps:std | ||
240 | <|> | ||
241 | //- /std.rs crate:std deps:core | ||
242 | pub use core::S; | ||
243 | //- /core.rs crate:core | ||
244 | pub struct S; | ||
245 | "#; | ||
246 | check_found_path(code, "std::S"); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
219 | fn prelude() { | 250 | fn prelude() { |
220 | let code = r#" | 251 | let code = r#" |
221 | //- /main.rs crate:main deps:std | 252 | //- /main.rs crate:main deps:std |
@@ -227,4 +258,21 @@ mod tests { | |||
227 | "#; | 258 | "#; |
228 | check_found_path(code, "S"); | 259 | check_found_path(code, "S"); |
229 | } | 260 | } |
261 | |||
262 | #[test] | ||
263 | fn enum_variant_from_prelude() { | ||
264 | let code = r#" | ||
265 | //- /main.rs crate:main deps:std | ||
266 | <|> | ||
267 | //- /std.rs crate:std | ||
268 | pub mod prelude { | ||
269 | pub enum Option<T> { Some(T), None } | ||
270 | pub use Option::*; | ||
271 | } | ||
272 | #[prelude_import] | ||
273 | pub use prelude::*; | ||
274 | "#; | ||
275 | check_found_path(code, "None"); | ||
276 | check_found_path(code, "Some"); | ||
277 | } | ||
230 | } | 278 | } |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index f88502d78..71afdb235 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -204,4 +204,12 @@ impl ItemInNs { | |||
204 | }, | 204 | }, |
205 | } | 205 | } |
206 | } | 206 | } |
207 | |||
208 | pub fn as_module_def_id(self) -> Option<ModuleDefId> { | ||
209 | match self { | ||
210 | ItemInNs::Types(t) => Some(t), | ||
211 | ItemInNs::Values(v) => Some(v), | ||
212 | ItemInNs::Macros(_) => None, | ||
213 | } | ||
214 | } | ||
207 | } | 215 | } |