Skip to content

Commit 31f5b48

Browse files
author
Jonas Schievink
committed
Record and suggest trait items via ImportMap
1 parent ec1d1a1 commit 31f5b48

File tree

2 files changed

+179
-8
lines changed

2 files changed

+179
-8
lines changed

crates/ra_assists/src/handlers/auto_import.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,146 @@ fn main() {
810810
);
811811
}
812812

813+
#[test]
814+
fn trait_method_cross_crate() {
815+
check_assist(
816+
auto_import,
817+
r"
818+
//- /main.rs crate:main deps:dep
819+
fn main() {
820+
let test_struct = dep::test_mod::TestStruct {};
821+
test_struct.test_meth<|>od()
822+
}
823+
//- /dep.rs crate:dep
824+
pub mod test_mod {
825+
pub trait TestTrait {
826+
fn test_method(&self);
827+
}
828+
pub struct TestStruct {}
829+
impl TestTrait for TestStruct {
830+
fn test_method(&self) {}
831+
}
832+
}
833+
",
834+
r"
835+
use dep::test_mod::TestTrait;
836+
837+
fn main() {
838+
let test_struct = dep::test_mod::TestStruct {};
839+
test_struct.test_method()
840+
}
841+
",
842+
);
843+
}
844+
845+
#[test]
846+
fn assoc_fn_cross_crate() {
847+
check_assist(
848+
auto_import,
849+
r"
850+
//- /main.rs crate:main deps:dep
851+
fn main() {
852+
dep::test_mod::TestStruct::test_func<|>tion
853+
}
854+
//- /dep.rs crate:dep
855+
pub mod test_mod {
856+
pub trait TestTrait {
857+
fn test_function();
858+
}
859+
pub struct TestStruct {}
860+
impl TestTrait for TestStruct {
861+
fn test_function() {}
862+
}
863+
}
864+
",
865+
r"
866+
use dep::test_mod::TestTrait;
867+
868+
fn main() {
869+
dep::test_mod::TestStruct::test_function
870+
}
871+
",
872+
);
873+
}
874+
875+
#[test]
876+
fn assoc_const_cross_crate() {
877+
check_assist(
878+
auto_import,
879+
r"
880+
//- /main.rs crate:main deps:dep
881+
fn main() {
882+
dep::test_mod::TestStruct::CONST<|>
883+
}
884+
//- /dep.rs crate:dep
885+
pub mod test_mod {
886+
pub trait TestTrait {
887+
const CONST: bool;
888+
}
889+
pub struct TestStruct {}
890+
impl TestTrait for TestStruct {
891+
const CONST: bool = true;
892+
}
893+
}
894+
",
895+
r"
896+
use dep::test_mod::TestTrait;
897+
898+
fn main() {
899+
dep::test_mod::TestStruct::CONST
900+
}
901+
",
902+
);
903+
}
904+
905+
#[test]
906+
fn assoc_fn_as_method_cross_crate() {
907+
check_assist_not_applicable(
908+
auto_import,
909+
r"
910+
//- /main.rs crate:main deps:dep
911+
fn main() {
912+
let test_struct = dep::test_mod::TestStruct {};
913+
test_struct.test_func<|>tion()
914+
}
915+
//- /dep.rs crate:dep
916+
pub mod test_mod {
917+
pub trait TestTrait {
918+
fn test_function();
919+
}
920+
pub struct TestStruct {}
921+
impl TestTrait for TestStruct {
922+
fn test_function() {}
923+
}
924+
}
925+
",
926+
);
927+
}
928+
929+
#[test]
930+
fn private_trait_cross_crate() {
931+
check_assist_not_applicable(
932+
auto_import,
933+
r"
934+
//- /main.rs crate:main deps:dep
935+
fn main() {
936+
let test_struct = dep::test_mod::TestStruct {};
937+
test_struct.test_meth<|>od()
938+
}
939+
//- /dep.rs crate:dep
940+
pub mod test_mod {
941+
trait TestTrait {
942+
fn test_method(&self);
943+
}
944+
pub struct TestStruct {}
945+
impl TestTrait for TestStruct {
946+
fn test_method(&self) {}
947+
}
948+
}
949+
",
950+
);
951+
}
952+
813953
#[test]
814954
fn not_applicable_for_imported_trait_for_method() {
815955
check_assist_not_applicable(

crates/ra_hir_def/src/import_map.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
55
use fst::{self, Streamer};
66
use indexmap::{map::Entry, IndexMap};
77
use ra_db::CrateId;
8-
use rustc_hash::FxHasher;
8+
use rustc_hash::{FxHashMap, FxHasher};
9+
use smallvec::SmallVec;
910

1011
use crate::{
1112
db::DefDatabase,
1213
item_scope::ItemInNs,
1314
path::{ModPath, PathKind},
1415
visibility::Visibility,
15-
ModuleDefId, ModuleId,
16+
AssocItemId, ModuleDefId, ModuleId, TraitId,
1617
};
1718

1819
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -34,6 +35,7 @@ pub struct ImportInfo {
3435
///
3536
/// Note that all paths are relative to the containing crate's root, so the crate name still needs
3637
/// to be prepended to the `ModPath` before the path is valid.
38+
#[derive(Default)]
3739
pub struct ImportMap {
3840
map: FxIndexMap<ItemInNs, ImportInfo>,
3941

@@ -45,13 +47,17 @@ pub struct ImportMap {
4547
/// the index of the first one.
4648
importables: Vec<ItemInNs>,
4749
fst: fst::Map<Vec<u8>>,
50+
51+
/// Maps names of associated items to the item's ID. Only includes items whose defining trait is
52+
/// exported.
53+
assoc_map: FxHashMap<String, SmallVec<[AssocItemId; 1]>>,
4854
}
4955

5056
impl ImportMap {
5157
pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
5258
let _p = ra_prof::profile("import_map_query");
5359
let def_map = db.crate_def_map(krate);
54-
let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
60+
let mut import_map = Self::default();
5561

5662
// We look only into modules that are public(ly reexported), starting with the crate root.
5763
let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
@@ -85,7 +91,7 @@ impl ImportMap {
8591

8692
for item in per_ns.iter_items() {
8793
let path = mk_path();
88-
match import_map.entry(item) {
94+
match import_map.map.entry(item) {
8995
Entry::Vacant(entry) => {
9096
entry.insert(ImportInfo { path, container: module });
9197
}
@@ -105,11 +111,16 @@ impl ImportMap {
105111
if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
106112
worklist.push((mod_id, mk_path()));
107113
}
114+
115+
// If we've added a path to a trait, add the trait's methods to the method map.
116+
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
117+
import_map.collect_trait_methods(db, tr);
118+
}
108119
}
109120
}
110121
}
111122

112-
let mut importables = import_map.iter().collect::<Vec<_>>();
123+
let mut importables = import_map.map.iter().collect::<Vec<_>>();
113124

114125
importables.sort_by(cmp);
115126

@@ -133,10 +144,10 @@ impl ImportMap {
133144
builder.insert(key, start as u64).unwrap();
134145
}
135146

136-
let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
137-
let importables = importables.iter().map(|(item, _)| **item).collect();
147+
import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
148+
import_map.importables = importables.iter().map(|(item, _)| **item).collect();
138149

139-
Arc::new(Self { map: import_map, fst, importables })
150+
Arc::new(import_map)
140151
}
141152

142153
/// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
@@ -147,6 +158,13 @@ impl ImportMap {
147158
pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
148159
self.map.get(&item)
149160
}
161+
162+
fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) {
163+
let data = db.trait_data(tr);
164+
for (name, item) in data.items.iter() {
165+
self.assoc_map.entry(name.to_string()).or_default().push(*item);
166+
}
167+
}
150168
}
151169

152170
impl PartialEq for ImportMap {
@@ -290,6 +308,19 @@ pub fn search_dependencies<'a>(
290308
}
291309
}
292310

311+
// Add all exported associated items whose names match the query (exactly).
312+
for map in &import_maps {
313+
if let Some(v) = map.assoc_map.get(&query.query) {
314+
res.extend(v.iter().map(|&assoc| {
315+
ItemInNs::Types(match assoc {
316+
AssocItemId::FunctionId(it) => it.into(),
317+
AssocItemId::ConstId(it) => it.into(),
318+
AssocItemId::TypeAliasId(it) => it.into(),
319+
})
320+
}));
321+
}
322+
}
323+
293324
res
294325
}
295326

0 commit comments

Comments
 (0)