diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/auto_import.rs | 12 | ||||
-rw-r--r-- | crates/assists/src/utils/import_assets.rs | 134 |
2 files changed, 84 insertions, 62 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 7182a2a5d..e595b5b93 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -63,13 +63,13 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
63 | 63 | ||
64 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | 64 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { |
65 | let name = match import_candidate { | 65 | let name = match import_candidate { |
66 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), | 66 | ImportCandidate::UnqualifiedName(candidate) |
67 | ImportCandidate::QualifierStart(qualifier_start) => format!("Import {}", qualifier_start), | 67 | | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name), |
68 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { | 68 | ImportCandidate::TraitAssocItem(candidate) => { |
69 | format!("Import a trait for item {}", trait_assoc_item_name) | 69 | format!("Import a trait for item {}", &candidate.name) |
70 | } | 70 | } |
71 | ImportCandidate::TraitMethod(_, trait_method_name) => { | 71 | ImportCandidate::TraitMethod(candidate) => { |
72 | format!("Import a trait for method {}", trait_method_name) | 72 | format!("Import a trait for method {}", &candidate.name) |
73 | } | 73 | } |
74 | }; | 74 | }; |
75 | GroupLabel(name) | 75 | GroupLabel(name) |
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index b816edc82..601f51098 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -12,18 +12,29 @@ use crate::assist_config::InsertUseConfig; | |||
12 | #[derive(Debug)] | 12 | #[derive(Debug)] |
13 | pub(crate) enum ImportCandidate { | 13 | pub(crate) enum ImportCandidate { |
14 | /// Simple name like 'HashMap' | 14 | /// Simple name like 'HashMap' |
15 | UnqualifiedName(String), | 15 | UnqualifiedName(PathImportCandidate), |
16 | /// First part of the qualified name. | 16 | /// First part of the qualified name. |
17 | /// For 'std::collections::HashMap', that will be 'std'. | 17 | /// For 'std::collections::HashMap', that will be 'std'. |
18 | QualifierStart(String), | 18 | QualifierStart(PathImportCandidate), |
19 | /// A trait associated function (with no self parameter) or associated constant. | 19 | /// A trait associated function (with no self parameter) or associated constant. |
20 | /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type | 20 | /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type |
21 | /// and `String` is the `test_function` | 21 | /// and `name` is the `test_function` |
22 | TraitAssocItem(hir::Type, String), | 22 | TraitAssocItem(TraitImportCandidate), |
23 | /// A trait method with self parameter. | 23 | /// A trait method with self parameter. |
24 | /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type | 24 | /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type |
25 | /// and `String` is the `test_method` | 25 | /// and `name` is the `test_method` |
26 | TraitMethod(hir::Type, String), | 26 | TraitMethod(TraitImportCandidate), |
27 | } | ||
28 | |||
29 | #[derive(Debug)] | ||
30 | pub(crate) struct TraitImportCandidate { | ||
31 | pub ty: hir::Type, | ||
32 | pub name: String, | ||
33 | } | ||
34 | |||
35 | #[derive(Debug)] | ||
36 | pub(crate) struct PathImportCandidate { | ||
37 | pub name: String, | ||
27 | } | 38 | } |
28 | 39 | ||
29 | #[derive(Debug)] | 40 | #[derive(Debug)] |
@@ -74,10 +85,10 @@ impl ImportAssets { | |||
74 | 85 | ||
75 | fn get_search_query(&self) -> &str { | 86 | fn get_search_query(&self) -> &str { |
76 | match &self.import_candidate { | 87 | match &self.import_candidate { |
77 | ImportCandidate::UnqualifiedName(name) => name, | 88 | ImportCandidate::UnqualifiedName(candidate) |
78 | ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, | 89 | | ImportCandidate::QualifierStart(candidate) => &candidate.name, |
79 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, | 90 | ImportCandidate::TraitAssocItem(candidate) |
80 | ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, | 91 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, |
81 | } | 92 | } |
82 | } | 93 | } |
83 | 94 | ||
@@ -106,27 +117,29 @@ impl ImportAssets { | |||
106 | prefixed: Option<hir::PrefixKind>, | 117 | prefixed: Option<hir::PrefixKind>, |
107 | ) -> BTreeSet<hir::ModPath> { | 118 | ) -> BTreeSet<hir::ModPath> { |
108 | let db = sema.db; | 119 | let db = sema.db; |
120 | let mut trait_candidates = FxHashSet::default(); | ||
109 | let current_crate = self.module_with_name_to_import.krate(); | 121 | let current_crate = self.module_with_name_to_import.krate(); |
110 | imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | 122 | |
111 | .into_iter() | 123 | let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { |
112 | .filter_map(|candidate| match &self.import_candidate { | 124 | trait_candidates.clear(); |
113 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | 125 | match &self.import_candidate { |
126 | ImportCandidate::TraitAssocItem(trait_candidate) => { | ||
114 | let located_assoc_item = match candidate { | 127 | let located_assoc_item = match candidate { |
115 | Either::Left(ModuleDef::Function(located_function)) => located_function | 128 | Either::Left(ModuleDef::Function(located_function)) => { |
116 | .as_assoc_item(db) | 129 | located_function.as_assoc_item(db) |
117 | .map(|assoc| assoc.container(db)) | 130 | } |
118 | .and_then(Self::assoc_to_trait), | 131 | Either::Left(ModuleDef::Const(located_const)) => { |
119 | Either::Left(ModuleDef::Const(located_const)) => located_const | 132 | located_const.as_assoc_item(db) |
120 | .as_assoc_item(db) | 133 | } |
121 | .map(|assoc| assoc.container(db)) | ||
122 | .and_then(Self::assoc_to_trait), | ||
123 | _ => None, | 134 | _ => None, |
124 | }?; | 135 | } |
136 | .map(|assoc| assoc.container(db)) | ||
137 | .and_then(Self::assoc_to_trait)?; | ||
125 | 138 | ||
126 | let mut trait_candidates = FxHashSet::default(); | ||
127 | trait_candidates.insert(located_assoc_item.into()); | 139 | trait_candidates.insert(located_assoc_item.into()); |
128 | 140 | ||
129 | assoc_item_type | 141 | trait_candidate |
142 | .ty | ||
130 | .iterate_path_candidates( | 143 | .iterate_path_candidates( |
131 | db, | 144 | db, |
132 | current_crate, | 145 | current_crate, |
@@ -137,7 +150,7 @@ impl ImportAssets { | |||
137 | .map(ModuleDef::from) | 150 | .map(ModuleDef::from) |
138 | .map(Either::Left) | 151 | .map(Either::Left) |
139 | } | 152 | } |
140 | ImportCandidate::TraitMethod(function_callee, _) => { | 153 | ImportCandidate::TraitMethod(trait_candidate) => { |
141 | let located_assoc_item = | 154 | let located_assoc_item = |
142 | if let Either::Left(ModuleDef::Function(located_function)) = candidate { | 155 | if let Either::Left(ModuleDef::Function(located_function)) = candidate { |
143 | located_function | 156 | located_function |
@@ -148,10 +161,10 @@ impl ImportAssets { | |||
148 | None | 161 | None |
149 | }?; | 162 | }?; |
150 | 163 | ||
151 | let mut trait_candidates = FxHashSet::default(); | ||
152 | trait_candidates.insert(located_assoc_item.into()); | 164 | trait_candidates.insert(located_assoc_item.into()); |
153 | 165 | ||
154 | function_callee | 166 | trait_candidate |
167 | .ty | ||
155 | .iterate_method_candidates( | 168 | .iterate_method_candidates( |
156 | db, | 169 | db, |
157 | current_crate, | 170 | current_crate, |
@@ -165,12 +178,14 @@ impl ImportAssets { | |||
165 | .map(Either::Left) | 178 | .map(Either::Left) |
166 | } | 179 | } |
167 | _ => Some(candidate), | 180 | _ => Some(candidate), |
168 | }) | 181 | } |
182 | }; | ||
183 | |||
184 | imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | ||
185 | .into_iter() | ||
186 | .filter_map(filter) | ||
169 | .filter_map(|candidate| { | 187 | .filter_map(|candidate| { |
170 | let item: hir::ItemInNs = match candidate { | 188 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); |
171 | Either::Left(module_def) => module_def.into(), | ||
172 | Either::Right(macro_def) => macro_def.into(), | ||
173 | }; | ||
174 | if let Some(prefix_kind) = prefixed { | 189 | if let Some(prefix_kind) = prefixed { |
175 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) | 190 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) |
176 | } else { | 191 | } else { |
@@ -196,13 +211,13 @@ impl ImportCandidate { | |||
196 | sema: &Semantics<RootDatabase>, | 211 | sema: &Semantics<RootDatabase>, |
197 | method_call: &ast::MethodCallExpr, | 212 | method_call: &ast::MethodCallExpr, |
198 | ) -> Option<Self> { | 213 | ) -> Option<Self> { |
199 | if sema.resolve_method_call(method_call).is_some() { | 214 | match sema.resolve_method_call(method_call) { |
200 | return None; | 215 | Some(_) => None, |
216 | None => Some(Self::TraitMethod(TraitImportCandidate { | ||
217 | ty: sema.type_of_expr(&method_call.receiver()?)?, | ||
218 | name: method_call.name_ref()?.syntax().to_string(), | ||
219 | })), | ||
201 | } | 220 | } |
202 | Some(Self::TraitMethod( | ||
203 | sema.type_of_expr(&method_call.receiver()?)?, | ||
204 | method_call.name_ref()?.syntax().to_string(), | ||
205 | )) | ||
206 | } | 221 | } |
207 | 222 | ||
208 | fn for_regular_path( | 223 | fn for_regular_path( |
@@ -214,7 +229,7 @@ impl ImportCandidate { | |||
214 | } | 229 | } |
215 | 230 | ||
216 | let segment = path_under_caret.segment()?; | 231 | let segment = path_under_caret.segment()?; |
217 | if let Some(qualifier) = path_under_caret.qualifier() { | 232 | let candidate = if let Some(qualifier) = path_under_caret.qualifier() { |
218 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 233 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; |
219 | let qualifier_start_path = | 234 | let qualifier_start_path = |
220 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 235 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; |
@@ -224,23 +239,30 @@ impl ImportCandidate { | |||
224 | } else { | 239 | } else { |
225 | sema.resolve_path(&qualifier)? | 240 | sema.resolve_path(&qualifier)? |
226 | }; | 241 | }; |
227 | if let hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) = | 242 | match qualifier_resolution { |
228 | qualifier_resolution | 243 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { |
229 | { | 244 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
230 | Some(ImportCandidate::TraitAssocItem( | 245 | ty: assoc_item_path.ty(sema.db), |
231 | assoc_item_path.ty(sema.db), | 246 | name: segment.syntax().to_string(), |
232 | segment.syntax().to_string(), | 247 | }) |
233 | )) | 248 | } |
234 | } else { | 249 | _ => return None, |
235 | None | ||
236 | } | 250 | } |
237 | } else { | 251 | } else { |
238 | Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) | 252 | ImportCandidate::QualifierStart(PathImportCandidate { |
253 | name: qualifier_start.syntax().to_string(), | ||
254 | }) | ||
239 | } | 255 | } |
240 | } else { | 256 | } else { |
241 | Some(ImportCandidate::UnqualifiedName( | 257 | ImportCandidate::UnqualifiedName(PathImportCandidate { |
242 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), | 258 | name: segment |
243 | )) | 259 | .syntax() |
244 | } | 260 | .descendants() |
261 | .find_map(ast::NameRef::cast)? | ||
262 | .syntax() | ||
263 | .to_string(), | ||
264 | }) | ||
265 | }; | ||
266 | Some(candidate) | ||
245 | } | 267 | } |
246 | } | 268 | } |