diff options
Diffstat (limited to 'crates/ide_db/src/helpers')
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 185 |
1 files changed, 130 insertions, 55 deletions
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 513128eae..0da4bdd0e 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use either::Either; | 2 | use either::Either; |
3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; | 3 | use hir::{ |
4 | AsAssocItem, AssocItem, Crate, ItemInNs, MacroDef, ModPath, Module, ModuleDef, PathResolution, | ||
5 | PrefixKind, Semantics, | ||
6 | }; | ||
4 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode}; | 8 | use syntax::{ast, AstNode}; |
6 | 9 | ||
@@ -145,7 +148,6 @@ impl ImportAssets { | |||
145 | prefixed: Option<hir::PrefixKind>, | 148 | prefixed: Option<hir::PrefixKind>, |
146 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 149 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
147 | let current_crate = self.module_with_candidate.krate(); | 150 | let current_crate = self.module_with_candidate.krate(); |
148 | let import_candidate = &self.import_candidate; | ||
149 | 151 | ||
150 | let imports_for_candidate_name = match self.name_to_import() { | 152 | let imports_for_candidate_name = match self.name_to_import() { |
151 | NameToImport::Exact(exact_name) => { | 153 | NameToImport::Exact(exact_name) => { |
@@ -157,7 +159,7 @@ impl ImportAssets { | |||
157 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 159 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
158 | // for the details | 160 | // for the details |
159 | NameToImport::Fuzzy(fuzzy_name) => { | 161 | NameToImport::Fuzzy(fuzzy_name) => { |
160 | let (assoc_item_search, limit) = if import_candidate.is_trait_candidate() { | 162 | let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { |
161 | (AssocItemSearch::AssocItemsOnly, None) | 163 | (AssocItemSearch::AssocItemsOnly, None) |
162 | } else { | 164 | } else { |
163 | (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)) | 165 | (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)) |
@@ -172,59 +174,108 @@ impl ImportAssets { | |||
172 | } | 174 | } |
173 | }; | 175 | }; |
174 | 176 | ||
175 | let db = sema.db; | 177 | let mut res = self |
176 | let mut res = | 178 | .applicable_defs(sema, prefixed, imports_for_candidate_name) |
177 | applicable_defs(import_candidate, current_crate, db, imports_for_candidate_name) | 179 | .filter(|(use_path, _)| use_path.len() > 1) |
178 | .filter_map(|candidate| { | 180 | .collect::<Vec<_>>(); |
179 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); | ||
180 | |||
181 | let item_to_search = if import_candidate.is_trait_candidate() { | ||
182 | let canidate_trait = match candidate { | ||
183 | Either::Left(module_def) => { | ||
184 | module_def.as_assoc_item(db)?.containing_trait(db) | ||
185 | } | ||
186 | _ => None, | ||
187 | }?; | ||
188 | ModuleDef::from(canidate_trait).into() | ||
189 | } else { | ||
190 | item | ||
191 | }; | ||
192 | let mod_path = if let Some(prefix_kind) = prefixed { | ||
193 | self.module_with_candidate.find_use_path_prefixed( | ||
194 | db, | ||
195 | item_to_search, | ||
196 | prefix_kind, | ||
197 | ) | ||
198 | } else { | ||
199 | self.module_with_candidate.find_use_path(db, item_to_search) | ||
200 | }; | ||
201 | |||
202 | mod_path.zip(Some(item)) | ||
203 | }) | ||
204 | .filter(|(use_path, _)| use_path.len() > 1) | ||
205 | .collect::<Vec<_>>(); | ||
206 | res.sort_by_cached_key(|(path, _)| path.clone()); | 181 | res.sort_by_cached_key(|(path, _)| path.clone()); |
207 | res | 182 | res |
208 | } | 183 | } |
184 | |||
185 | fn applicable_defs<'a>( | ||
186 | &self, | ||
187 | sema: &'a Semantics<RootDatabase>, | ||
188 | prefixed: Option<hir::PrefixKind>, | ||
189 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | ||
190 | ) -> Box<dyn Iterator<Item = (ModPath, ItemInNs)> + 'a> { | ||
191 | let current_crate = self.module_with_candidate.krate(); | ||
192 | let db = sema.db; | ||
193 | |||
194 | match &self.import_candidate { | ||
195 | ImportCandidate::Path(path_candidate) => path_applicable_defs( | ||
196 | sema, | ||
197 | path_candidate, | ||
198 | unfiltered_imports, | ||
199 | self.module_with_candidate, | ||
200 | prefixed, | ||
201 | ), | ||
202 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_defs( | ||
203 | db, | ||
204 | current_crate, | ||
205 | trait_candidate, | ||
206 | true, | ||
207 | unfiltered_imports, | ||
208 | self.module_with_candidate, | ||
209 | prefixed, | ||
210 | ), | ||
211 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_defs( | ||
212 | db, | ||
213 | current_crate, | ||
214 | trait_candidate, | ||
215 | false, | ||
216 | unfiltered_imports, | ||
217 | self.module_with_candidate, | ||
218 | prefixed, | ||
219 | ), | ||
220 | } | ||
221 | } | ||
209 | } | 222 | } |
210 | 223 | ||
211 | fn applicable_defs<'a>( | 224 | fn path_applicable_defs<'a>( |
212 | import_candidate: &ImportCandidate, | 225 | sema: &'a Semantics<RootDatabase>, |
213 | current_crate: Crate, | 226 | path_candidate: &PathImportCandidate, |
214 | db: &RootDatabase, | 227 | unfiltered_defs: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, |
215 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | 228 | module_with_candidate: Module, |
216 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 229 | prefixed: Option<hir::PrefixKind>, |
217 | // TODO kb this needs to consider various path prefixes, etc. | 230 | ) -> Box<dyn Iterator<Item = (ModPath, ItemInNs)> + 'a> { |
218 | let receiver_ty = match import_candidate { | 231 | let applicable_defs = unfiltered_defs |
219 | ImportCandidate::Path(_) => return unfiltered_imports, | 232 | .map(|candidate| candidate.either(ItemInNs::from, ItemInNs::from)) |
220 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | 233 | .filter_map(move |item_to_search| { |
221 | &candidate.receiver_ty | 234 | get_mod_path(sema.db, item_to_search, &module_with_candidate, prefixed) |
235 | .zip(Some(item_to_search)) | ||
236 | }); | ||
237 | |||
238 | let unresolved_qualifier = match &path_candidate.unresolved_qualifier { | ||
239 | Some(qualifier) => qualifier, | ||
240 | None => { | ||
241 | // TODO kb too many boxes tossed around | ||
242 | return Box::new(applicable_defs); | ||
222 | } | 243 | } |
223 | }; | 244 | }; |
224 | 245 | ||
246 | // TODO kb filter out items: found path should end with `qualifier::Name` or `qualifier::Something` for fuzzy search case. | ||
247 | |||
248 | // TODO kb find a way to turn a qualifier into the corresponding ModuleDef. Maybe through the unfiltered data? | ||
249 | if let Some(qualifier_start_resolution) = resolve_qualifier_start(sema, unresolved_qualifier) { | ||
250 | // TODO kb ascend until an unresolved segment part appears | ||
251 | } else { | ||
252 | // first segment is already unresolved, need to turn it into ModuleDef somehow | ||
253 | } | ||
254 | |||
255 | return Box::new(applicable_defs); | ||
256 | } | ||
257 | |||
258 | fn resolve_qualifier_start( | ||
259 | sema: &Semantics<RootDatabase>, | ||
260 | qualifier: &ast::Path, | ||
261 | ) -> Option<PathResolution> { | ||
262 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
263 | let qualifier_start_path = qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
264 | sema.resolve_path(&qualifier_start_path) | ||
265 | } | ||
266 | |||
267 | fn trait_applicable_defs<'a>( | ||
268 | db: &'a RootDatabase, | ||
269 | current_crate: Crate, | ||
270 | trait_candidate: &TraitImportCandidate, | ||
271 | trait_assoc_item: bool, | ||
272 | unfiltered_defs: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | ||
273 | module_with_candidate: Module, | ||
274 | prefixed: Option<hir::PrefixKind>, | ||
275 | ) -> Box<dyn Iterator<Item = (ModPath, ItemInNs)> + 'a> { | ||
225 | let mut required_assoc_items = FxHashSet::default(); | 276 | let mut required_assoc_items = FxHashSet::default(); |
226 | 277 | ||
227 | let trait_candidates = unfiltered_imports | 278 | let trait_candidates = unfiltered_defs |
228 | .filter_map(|input| match input { | 279 | .filter_map(|input| match input { |
229 | Either::Left(module_def) => module_def.as_assoc_item(db), | 280 | Either::Left(module_def) => module_def.as_assoc_item(db), |
230 | _ => None, | 281 | _ => None, |
@@ -238,9 +289,8 @@ fn applicable_defs<'a>( | |||
238 | 289 | ||
239 | let mut applicable_defs = FxHashSet::default(); | 290 | let mut applicable_defs = FxHashSet::default(); |
240 | 291 | ||
241 | match import_candidate { | 292 | if trait_assoc_item { |
242 | ImportCandidate::Path(_) => unreachable!(), | 293 | trait_candidate.receiver_ty.iterate_path_candidates( |
243 | ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates( | ||
244 | db, | 294 | db, |
245 | current_crate, | 295 | current_crate, |
246 | &trait_candidates, | 296 | &trait_candidates, |
@@ -252,12 +302,13 @@ fn applicable_defs<'a>( | |||
252 | return None; | 302 | return None; |
253 | } | 303 | } |
254 | } | 304 | } |
255 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 305 | applicable_defs.insert(assoc_to_module_def(assoc)); |
256 | } | 306 | } |
257 | None::<()> | 307 | None::<()> |
258 | }, | 308 | }, |
259 | ), | 309 | ) |
260 | ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( | 310 | } else { |
311 | trait_candidate.receiver_ty.iterate_method_candidates( | ||
261 | db, | 312 | db, |
262 | current_crate, | 313 | current_crate, |
263 | &trait_candidates, | 314 | &trait_candidates, |
@@ -265,14 +316,38 @@ fn applicable_defs<'a>( | |||
265 | |_, function| { | 316 | |_, function| { |
266 | let assoc = function.as_assoc_item(db)?; | 317 | let assoc = function.as_assoc_item(db)?; |
267 | if required_assoc_items.contains(&assoc) { | 318 | if required_assoc_items.contains(&assoc) { |
268 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 319 | applicable_defs.insert(assoc_to_module_def(assoc)); |
269 | } | 320 | } |
270 | None::<()> | 321 | None::<()> |
271 | }, | 322 | }, |
272 | ), | 323 | ) |
273 | }; | 324 | }; |
274 | 325 | ||
275 | Box::new(applicable_defs.into_iter()) | 326 | Box::new( |
327 | applicable_defs | ||
328 | .into_iter() | ||
329 | .filter_map(move |candidate| { | ||
330 | let canidate_trait = candidate.as_assoc_item(db)?.containing_trait(db)?; | ||
331 | Some(ItemInNs::from(ModuleDef::from(canidate_trait))) | ||
332 | }) | ||
333 | .filter_map(move |item_to_search| { | ||
334 | get_mod_path(db, item_to_search, &module_with_candidate, prefixed) | ||
335 | .zip(Some(item_to_search)) | ||
336 | }), | ||
337 | ) | ||
338 | } | ||
339 | |||
340 | fn get_mod_path( | ||
341 | db: &RootDatabase, | ||
342 | item_to_search: ItemInNs, | ||
343 | module_with_candidate: &Module, | ||
344 | prefixed: Option<hir::PrefixKind>, | ||
345 | ) -> Option<ModPath> { | ||
346 | if let Some(prefix_kind) = prefixed { | ||
347 | module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind) | ||
348 | } else { | ||
349 | module_with_candidate.find_use_path(db, item_to_search) | ||
350 | } | ||
276 | } | 351 | } |
277 | 352 | ||
278 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { | 353 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { |