diff options
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 140 | ||||
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 47 |
2 files changed, 179 insertions, 8 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..299fe82a8 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -5,14 +5,15 @@ 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 rustc_hash::{FxHashMap, FxHasher}; |
9 | use smallvec::SmallVec; | ||
9 | 10 | ||
10 | use crate::{ | 11 | use crate::{ |
11 | db::DefDatabase, | 12 | db::DefDatabase, |
12 | item_scope::ItemInNs, | 13 | item_scope::ItemInNs, |
13 | path::{ModPath, PathKind}, | 14 | path::{ModPath, PathKind}, |
14 | visibility::Visibility, | 15 | visibility::Visibility, |
15 | ModuleDefId, ModuleId, | 16 | AssocItemId, ModuleDefId, ModuleId, TraitId, |
16 | }; | 17 | }; |
17 | 18 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | 19 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; |
@@ -34,6 +35,7 @@ pub struct ImportInfo { | |||
34 | /// | 35 | /// |
35 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | 36 | /// 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. | 37 | /// to be prepended to the `ModPath` before the path is valid. |
38 | #[derive(Default)] | ||
37 | pub struct ImportMap { | 39 | pub struct ImportMap { |
38 | map: FxIndexMap<ItemInNs, ImportInfo>, | 40 | map: FxIndexMap<ItemInNs, ImportInfo>, |
39 | 41 | ||
@@ -45,13 +47,17 @@ pub struct ImportMap { | |||
45 | /// the index of the first one. | 47 | /// the index of the first one. |
46 | importables: Vec<ItemInNs>, | 48 | importables: Vec<ItemInNs>, |
47 | fst: fst::Map<Vec<u8>>, | 49 | 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]>>, | ||
48 | } | 54 | } |
49 | 55 | ||
50 | impl ImportMap { | 56 | impl ImportMap { |
51 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 57 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
52 | let _p = ra_prof::profile("import_map_query"); | 58 | let _p = ra_prof::profile("import_map_query"); |
53 | let def_map = db.crate_def_map(krate); | 59 | 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(); |
55 | 61 | ||
56 | // We look only into modules that are public(ly reexported), starting with the crate root. | 62 | // We look only into modules that are public(ly reexported), starting with the crate root. |
57 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 63 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; |
@@ -85,7 +91,7 @@ impl ImportMap { | |||
85 | 91 | ||
86 | for item in per_ns.iter_items() { | 92 | for item in per_ns.iter_items() { |
87 | let path = mk_path(); | 93 | let path = mk_path(); |
88 | match import_map.entry(item) { | 94 | match import_map.map.entry(item) { |
89 | Entry::Vacant(entry) => { | 95 | Entry::Vacant(entry) => { |
90 | entry.insert(ImportInfo { path, container: module }); | 96 | entry.insert(ImportInfo { path, container: module }); |
91 | } | 97 | } |
@@ -105,11 +111,16 @@ impl ImportMap { | |||
105 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | 111 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { |
106 | worklist.push((mod_id, mk_path())); | 112 | worklist.push((mod_id, mk_path())); |
107 | } | 113 | } |
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 | } | ||
108 | } | 119 | } |
109 | } | 120 | } |
110 | } | 121 | } |
111 | 122 | ||
112 | let mut importables = import_map.iter().collect::<Vec<_>>(); | 123 | let mut importables = import_map.map.iter().collect::<Vec<_>>(); |
113 | 124 | ||
114 | importables.sort_by(cmp); | 125 | importables.sort_by(cmp); |
115 | 126 | ||
@@ -133,10 +144,10 @@ impl ImportMap { | |||
133 | builder.insert(key, start as u64).unwrap(); | 144 | builder.insert(key, start as u64).unwrap(); |
134 | } | 145 | } |
135 | 146 | ||
136 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | 147 | import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
137 | let importables = importables.iter().map(|(item, _)| **item).collect(); | 148 | import_map.importables = importables.iter().map(|(item, _)| **item).collect(); |
138 | 149 | ||
139 | Arc::new(Self { map: import_map, fst, importables }) | 150 | Arc::new(import_map) |
140 | } | 151 | } |
141 | 152 | ||
142 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 153 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
@@ -147,6 +158,13 @@ impl ImportMap { | |||
147 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | 158 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { |
148 | self.map.get(&item) | 159 | self.map.get(&item) |
149 | } | 160 | } |
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 | } | ||
150 | } | 168 | } |
151 | 169 | ||
152 | impl PartialEq for ImportMap { | 170 | impl PartialEq for ImportMap { |
@@ -290,6 +308,19 @@ pub fn search_dependencies<'a>( | |||
290 | } | 308 | } |
291 | } | 309 | } |
292 | 310 | ||
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 | |||
293 | res | 324 | res |
294 | } | 325 | } |
295 | 326 | ||