diff options
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 76 |
1 files changed, 67 insertions, 9 deletions
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 68e20d06b..869f3ca5a 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -5,14 +5,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | |||
5 | use fst::{self, Streamer}; | 5 | use fst::{self, Streamer}; |
6 | use indexmap::{map::Entry, IndexMap}; | 6 | use indexmap::{map::Entry, IndexMap}; |
7 | use ra_db::CrateId; | 7 | use ra_db::CrateId; |
8 | use rustc_hash::FxHasher; | 8 | use ra_syntax::SmolStr; |
9 | use rustc_hash::{FxHashMap, FxHasher}; | ||
10 | use smallvec::SmallVec; | ||
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | db::DefDatabase, | 13 | db::DefDatabase, |
12 | item_scope::ItemInNs, | 14 | item_scope::ItemInNs, |
13 | path::{ModPath, PathKind}, | 15 | path::{ModPath, PathKind}, |
14 | visibility::Visibility, | 16 | visibility::Visibility, |
15 | ModuleDefId, ModuleId, | 17 | AssocItemId, ModuleDefId, ModuleId, TraitId, |
16 | }; | 18 | }; |
17 | 19 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | 20 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; |
@@ -34,6 +36,7 @@ pub struct ImportInfo { | |||
34 | /// | 36 | /// |
35 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | 37 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs |
36 | /// to be prepended to the `ModPath` before the path is valid. | 38 | /// to be prepended to the `ModPath` before the path is valid. |
39 | #[derive(Default)] | ||
37 | pub struct ImportMap { | 40 | pub struct ImportMap { |
38 | map: FxIndexMap<ItemInNs, ImportInfo>, | 41 | map: FxIndexMap<ItemInNs, ImportInfo>, |
39 | 42 | ||
@@ -45,13 +48,17 @@ pub struct ImportMap { | |||
45 | /// the index of the first one. | 48 | /// the index of the first one. |
46 | importables: Vec<ItemInNs>, | 49 | importables: Vec<ItemInNs>, |
47 | fst: fst::Map<Vec<u8>>, | 50 | fst: fst::Map<Vec<u8>>, |
51 | |||
52 | /// Maps names of associated items to the item's ID. Only includes items whose defining trait is | ||
53 | /// exported. | ||
54 | assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>, | ||
48 | } | 55 | } |
49 | 56 | ||
50 | impl ImportMap { | 57 | impl ImportMap { |
51 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 58 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
52 | let _p = ra_prof::profile("import_map_query"); | 59 | let _p = ra_prof::profile("import_map_query"); |
53 | let def_map = db.crate_def_map(krate); | 60 | let def_map = db.crate_def_map(krate); |
54 | let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); | 61 | let mut import_map = Self::default(); |
55 | 62 | ||
56 | // We look only into modules that are public(ly reexported), starting with the crate root. | 63 | // We look only into modules that are public(ly reexported), starting with the crate root. |
57 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 64 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; |
@@ -85,7 +92,7 @@ impl ImportMap { | |||
85 | 92 | ||
86 | for item in per_ns.iter_items() { | 93 | for item in per_ns.iter_items() { |
87 | let path = mk_path(); | 94 | let path = mk_path(); |
88 | match import_map.entry(item) { | 95 | match import_map.map.entry(item) { |
89 | Entry::Vacant(entry) => { | 96 | Entry::Vacant(entry) => { |
90 | entry.insert(ImportInfo { path, container: module }); | 97 | entry.insert(ImportInfo { path, container: module }); |
91 | } | 98 | } |
@@ -105,11 +112,16 @@ impl ImportMap { | |||
105 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | 112 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { |
106 | worklist.push((mod_id, mk_path())); | 113 | worklist.push((mod_id, mk_path())); |
107 | } | 114 | } |
115 | |||
116 | // If we've added a path to a trait, add the trait's methods to the method map. | ||
117 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
118 | import_map.collect_trait_methods(db, tr); | ||
119 | } | ||
108 | } | 120 | } |
109 | } | 121 | } |
110 | } | 122 | } |
111 | 123 | ||
112 | let mut importables = import_map.iter().collect::<Vec<_>>(); | 124 | let mut importables = import_map.map.iter().collect::<Vec<_>>(); |
113 | 125 | ||
114 | importables.sort_by(cmp); | 126 | importables.sort_by(cmp); |
115 | 127 | ||
@@ -133,10 +145,10 @@ impl ImportMap { | |||
133 | builder.insert(key, start as u64).unwrap(); | 145 | builder.insert(key, start as u64).unwrap(); |
134 | } | 146 | } |
135 | 147 | ||
136 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | 148 | import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
137 | let importables = importables.iter().map(|(item, _)| **item).collect(); | 149 | import_map.importables = importables.iter().map(|(item, _)| **item).collect(); |
138 | 150 | ||
139 | Arc::new(Self { map: import_map, fst, importables }) | 151 | Arc::new(import_map) |
140 | } | 152 | } |
141 | 153 | ||
142 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 154 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
@@ -147,6 +159,13 @@ impl ImportMap { | |||
147 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | 159 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { |
148 | self.map.get(&item) | 160 | self.map.get(&item) |
149 | } | 161 | } |
162 | |||
163 | fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { | ||
164 | let data = db.trait_data(tr); | ||
165 | for (name, item) in data.items.iter() { | ||
166 | self.assoc_map.entry(name.to_string().into()).or_default().push(*item); | ||
167 | } | ||
168 | } | ||
150 | } | 169 | } |
151 | 170 | ||
152 | impl PartialEq for ImportMap { | 171 | impl PartialEq for ImportMap { |
@@ -290,13 +309,26 @@ pub fn search_dependencies<'a>( | |||
290 | } | 309 | } |
291 | } | 310 | } |
292 | 311 | ||
312 | // Add all exported associated items whose names match the query (exactly). | ||
313 | for map in &import_maps { | ||
314 | if let Some(v) = map.assoc_map.get(&*query.query) { | ||
315 | res.extend(v.iter().map(|&assoc| { | ||
316 | ItemInNs::Types(match assoc { | ||
317 | AssocItemId::FunctionId(it) => it.into(), | ||
318 | AssocItemId::ConstId(it) => it.into(), | ||
319 | AssocItemId::TypeAliasId(it) => it.into(), | ||
320 | }) | ||
321 | })); | ||
322 | } | ||
323 | } | ||
324 | |||
293 | res | 325 | res |
294 | } | 326 | } |
295 | 327 | ||
296 | #[cfg(test)] | 328 | #[cfg(test)] |
297 | mod tests { | 329 | mod tests { |
298 | use super::*; | 330 | use super::*; |
299 | use crate::test_db::TestDB; | 331 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; |
300 | use insta::assert_snapshot; | 332 | use insta::assert_snapshot; |
301 | use itertools::Itertools; | 333 | use itertools::Itertools; |
302 | use ra_db::fixture::WithFixture; | 334 | use ra_db::fixture::WithFixture; |
@@ -339,6 +371,7 @@ mod tests { | |||
339 | ItemInNs::Values(_) => "v", | 371 | ItemInNs::Values(_) => "v", |
340 | ItemInNs::Macros(_) => "m", | 372 | ItemInNs::Macros(_) => "m", |
341 | }; | 373 | }; |
374 | let item = assoc_to_trait(&db, item); | ||
342 | item.krate(db.upcast()).map(|krate| { | 375 | item.krate(db.upcast()).map(|krate| { |
343 | let map = db.import_map(krate); | 376 | let map = db.import_map(krate); |
344 | let path = map.path_of(item).unwrap(); | 377 | let path = map.path_of(item).unwrap(); |
@@ -353,6 +386,29 @@ mod tests { | |||
353 | .join("\n") | 386 | .join("\n") |
354 | } | 387 | } |
355 | 388 | ||
389 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { | ||
390 | let assoc: AssocItemId = match item { | ||
391 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { | ||
392 | ModuleDefId::TypeAliasId(it) => it.into(), | ||
393 | ModuleDefId::FunctionId(it) => it.into(), | ||
394 | ModuleDefId::ConstId(it) => it.into(), | ||
395 | _ => return item, | ||
396 | }, | ||
397 | _ => return item, | ||
398 | }; | ||
399 | |||
400 | let container = match assoc { | ||
401 | AssocItemId::FunctionId(it) => it.lookup(db).container, | ||
402 | AssocItemId::ConstId(it) => it.lookup(db).container, | ||
403 | AssocItemId::TypeAliasId(it) => it.lookup(db).container, | ||
404 | }; | ||
405 | |||
406 | match container { | ||
407 | AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), | ||
408 | _ => item, | ||
409 | } | ||
410 | } | ||
411 | |||
356 | #[test] | 412 | #[test] |
357 | fn smoke() { | 413 | fn smoke() { |
358 | let map = import_map( | 414 | let map = import_map( |
@@ -610,6 +666,7 @@ mod tests { | |||
610 | dep::Fmt (m) | 666 | dep::Fmt (m) |
611 | dep::fmt::Display (t) | 667 | dep::fmt::Display (t) |
612 | dep::format (v) | 668 | dep::format (v) |
669 | dep::fmt::Display (t) | ||
613 | "###); | 670 | "###); |
614 | 671 | ||
615 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | 672 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); |
@@ -618,6 +675,7 @@ mod tests { | |||
618 | dep::Fmt (t) | 675 | dep::Fmt (t) |
619 | dep::Fmt (v) | 676 | dep::Fmt (v) |
620 | dep::Fmt (m) | 677 | dep::Fmt (m) |
678 | dep::fmt::Display (t) | ||
621 | "###); | 679 | "###); |
622 | } | 680 | } |
623 | 681 | ||