aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs140
-rw-r--r--crates/ra_hir_def/src/import_map.rs76
2 files changed, 207 insertions, 9 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 7b6499a08..4cd77adbf 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -811,6 +811,146 @@ fn main() {
811 } 811 }
812 812
813 #[test] 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
953 #[test]
814 fn not_applicable_for_imported_trait_for_method() { 954 fn not_applicable_for_imported_trait_for_method() {
815 check_assist_not_applicable( 955 check_assist_not_applicable(
816 auto_import, 956 auto_import,
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};
5use fst::{self, Streamer}; 5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap}; 6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId; 7use ra_db::CrateId;
8use rustc_hash::FxHasher; 8use ra_syntax::SmolStr;
9use rustc_hash::{FxHashMap, FxHasher};
10use smallvec::SmallVec;
9 11
10use crate::{ 12use 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
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 20type 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)]
37pub struct ImportMap { 40pub 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
50impl ImportMap { 57impl 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
152impl PartialEq for ImportMap { 171impl 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)]
297mod tests { 329mod 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