aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs185
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.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; 3use hir::{
4 AsAssocItem, AssocItem, Crate, ItemInNs, MacroDef, ModPath, Module, ModuleDef, PathResolution,
5 PrefixKind, Semantics,
6};
4use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode}; 8use 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
211fn applicable_defs<'a>( 224fn 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
258fn 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
267fn 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
340fn 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
278fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { 353fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {