aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2021-02-23 23:20:00 +0000
committerKirill Bulatov <[email protected]>2021-03-08 21:58:48 +0000
commit309421c117fc20e58b9f30fb28a01a89f50b0086 (patch)
tree77ab1a1a1dd426a5ffa2f817e0df49c4b804782e /crates/ide_db/src
parentc395c3311dc2ac59251e86eaa6b86b597358d31f (diff)
Draft the qualifier import resolution
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs173
-rw-r--r--crates/ide_db/src/imports_locator.rs1
2 files changed, 137 insertions, 37 deletions
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 976dc92fb..dc3b92a64 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,8 +1,8 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{ 3use hir::{
4 AsAssocItem, AssocItem, Crate, ItemInNs, MacroDef, ModPath, Module, ModuleDef, PrefixKind, 4 AsAssocItem, AssocItem, Crate, ItemInNs, MacroDef, ModPath, Module, ModuleDef, Name,
5 Semantics, 5 PrefixKind, Semantics,
6}; 6};
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode}; 8use syntax::{ast, AstNode};
@@ -34,11 +34,17 @@ pub struct TraitImportCandidate {
34 34
35#[derive(Debug)] 35#[derive(Debug)]
36pub struct PathImportCandidate { 36pub struct PathImportCandidate {
37 pub unresolved_qualifier: Option<ast::Path>, 37 pub qualifier: Qualifier,
38 pub name: NameToImport, 38 pub name: NameToImport,
39} 39}
40 40
41#[derive(Debug)] 41#[derive(Debug)]
42pub enum Qualifier {
43 Absent,
44 FirstSegmentUnresolved(ast::PathSegment, ast::Path),
45}
46
47#[derive(Debug)]
42pub enum NameToImport { 48pub enum NameToImport {
43 Exact(String), 49 Exact(String),
44 Fuzzy(String), 50 Fuzzy(String),
@@ -162,8 +168,9 @@ impl ImportAssets {
162 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { 168 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
163 (AssocItemSearch::AssocItemsOnly, None) 169 (AssocItemSearch::AssocItemsOnly, None)
164 } else { 170 } else {
165 (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)) 171 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
166 }; 172 };
173
167 imports_locator::find_similar_imports( 174 imports_locator::find_similar_imports(
168 sema, 175 sema,
169 current_crate, 176 current_crate,
@@ -192,17 +199,16 @@ impl ImportAssets {
192 let db = sema.db; 199 let db = sema.db;
193 200
194 match &self.import_candidate { 201 match &self.import_candidate {
195 ImportCandidate::Path(path_candidate) => Box::new(path_applicable_items( 202 ImportCandidate::Path(path_candidate) => Box::new(
196 sema, 203 path_applicable_items(
197 path_candidate, 204 db,
198 unfiltered_defs 205 path_candidate,
199 .into_iter() 206 &self.module_with_candidate,
200 .map(|def| def.either(ItemInNs::from, ItemInNs::from)) 207 prefixed,
201 .filter_map(move |item_to_search| { 208 unfiltered_defs,
202 get_mod_path(db, item_to_search, &self.module_with_candidate, prefixed) 209 )
203 .zip(Some(item_to_search)) 210 .into_iter(),
204 }), 211 ),
205 )),
206 ImportCandidate::TraitAssocItem(trait_candidate) => Box::new( 212 ImportCandidate::TraitAssocItem(trait_candidate) => Box::new(
207 trait_applicable_defs(db, current_crate, trait_candidate, true, unfiltered_defs) 213 trait_applicable_defs(db, current_crate, trait_candidate, true, unfiltered_defs)
208 .into_iter() 214 .into_iter()
@@ -224,27 +230,110 @@ impl ImportAssets {
224} 230}
225 231
226fn path_applicable_items<'a>( 232fn path_applicable_items<'a>(
227 sema: &'a Semantics<RootDatabase>, 233 db: &'a RootDatabase,
228 path_candidate: &PathImportCandidate, 234 path_candidate: &'a PathImportCandidate,
229 unfiltered_defs: impl Iterator<Item = (ModPath, ItemInNs)> + 'a, 235 module_with_candidate: &hir::Module,
230) -> Box<dyn Iterator<Item = (ModPath, ItemInNs)> + 'a> { 236 prefixed: Option<hir::PrefixKind>,
231 let unresolved_qualifier = match &path_candidate.unresolved_qualifier { 237 unfiltered_defs: impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a,
232 Some(qualifier) => qualifier, 238) -> FxHashSet<(ModPath, ItemInNs)> {
233 None => { 239 let applicable_items = unfiltered_defs
234 return Box::new(unfiltered_defs); 240 .filter_map(|def| {
241 let (assoc_original, candidate) = match def {
242 Either::Left(module_def) => match module_def.as_assoc_item(db) {
243 Some(assoc_item) => match assoc_item.container(db) {
244 hir::AssocItemContainer::Trait(trait_) => {
245 (Some(module_def), ItemInNs::from(ModuleDef::from(trait_)))
246 }
247 hir::AssocItemContainer::Impl(impl_) => (
248 Some(module_def),
249 ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)),
250 ),
251 },
252 None => (None, ItemInNs::from(module_def)),
253 },
254 Either::Right(macro_def) => (None, ItemInNs::from(macro_def)),
255 };
256 Some((assoc_original, candidate))
257 })
258 .filter_map(|(assoc_original, candidate)| {
259 get_mod_path(db, candidate, module_with_candidate, prefixed)
260 .zip(Some((assoc_original, candidate)))
261 });
262
263 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
264 Qualifier::Absent => {
265 return applicable_items
266 .map(|(candidate_path, (_, candidate))| (candidate_path, candidate))
267 .collect();
235 } 268 }
269 Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier),
236 }; 270 };
237 271
238 let qualifier_string = unresolved_qualifier.to_string(); 272 // TODO kb need to remove turbofish from the qualifier, maybe use the segments instead?
239 Box::new(unfiltered_defs.filter(move |(candidate_path, _)| { 273 let unresolved_qualifier_string = unresolved_qualifier.to_string();
240 let mut candidate_qualifier = candidate_path.clone(); 274 let unresolved_first_segment_string = unresolved_first_segment.to_string();
241 candidate_qualifier.pop_segment(); 275
276 applicable_items
277 .filter(|(candidate_path, _)| {
278 let candidate_path_string = candidate_path.to_string();
279 candidate_path_string.contains(&unresolved_qualifier_string)
280 && candidate_path_string.contains(&unresolved_first_segment_string)
281 })
282 // TODO kb need to adjust the return type: I get the results rendered rather badly
283 .filter_map(|(candidate_path, (assoc_original, candidate))| {
284 if let Some(assoc_original) = assoc_original {
285 if item_name(db, candidate)?.to_string() == unresolved_first_segment_string {
286 return Some((candidate_path, ItemInNs::from(assoc_original)));
287 }
288 }
289
290 let matching_module =
291 module_with_matching_name(db, &unresolved_first_segment_string, candidate)?;
292 let path = get_mod_path(
293 db,
294 ItemInNs::from(ModuleDef::from(matching_module)),
295 module_with_candidate,
296 prefixed,
297 )?;
298 Some((path, candidate))
299 })
300 .collect()
301}
302
303fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
304 match item {
305 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
306 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db),
307 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
308 }
309}
242 310
243 // TODO kb 311fn item_module(db: &RootDatabase, item: ItemInNs) -> Option<Module> {
244 // * take 1st segment of `unresolved_qualifier` and return it instead of the original `ItemInNs` 312 match item {
245 // * Update `ModPath`: pop until 1st segment of `unresolved_qualifier` reached (do not rely on name comparison, nested mod names can repeat) 313 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db),
246 candidate_qualifier.to_string().ends_with(&qualifier_string) 314 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db),
247 })) 315 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db),
316 }
317}
318
319fn module_with_matching_name(
320 db: &RootDatabase,
321 unresolved_first_segment_string: &str,
322 candidate: ItemInNs,
323) -> Option<Module> {
324 let mut current_module = item_module(db, candidate);
325 while let Some(module) = current_module {
326 match module.name(db) {
327 Some(module_name) => {
328 if module_name.to_string().as_str() == unresolved_first_segment_string {
329 return Some(module);
330 }
331 }
332 None => {}
333 }
334 current_module = module.parent(db);
335 }
336 None
248} 337}
249 338
250fn trait_applicable_defs<'a>( 339fn trait_applicable_defs<'a>(
@@ -367,10 +456,20 @@ fn path_import_candidate(
367) -> Option<ImportCandidate> { 456) -> Option<ImportCandidate> {
368 Some(match qualifier { 457 Some(match qualifier {
369 Some(qualifier) => match sema.resolve_path(&qualifier) { 458 Some(qualifier) => match sema.resolve_path(&qualifier) {
370 None => ImportCandidate::Path(PathImportCandidate { 459 None => {
371 unresolved_qualifier: Some(qualifier), 460 let qualifier_start =
372 name, 461 qualifier.syntax().descendants().find_map(ast::PathSegment::cast)?;
373 }), 462 let qualifier_start_path =
463 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
464 if sema.resolve_path(&qualifier_start_path).is_none() {
465 ImportCandidate::Path(PathImportCandidate {
466 qualifier: Qualifier::FirstSegmentUnresolved(qualifier_start, qualifier),
467 name,
468 })
469 } else {
470 return None;
471 }
472 }
374 Some(hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path))) => { 473 Some(hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path))) => {
375 ImportCandidate::TraitAssocItem(TraitImportCandidate { 474 ImportCandidate::TraitAssocItem(TraitImportCandidate {
376 receiver_ty: assoc_item_path.ty(sema.db), 475 receiver_ty: assoc_item_path.ty(sema.db),
@@ -379,6 +478,6 @@ fn path_import_candidate(
379 } 478 }
380 Some(_) => return None, 479 Some(_) => return None,
381 }, 480 },
382 None => ImportCandidate::Path(PathImportCandidate { unresolved_qualifier: None, name }), 481 None => ImportCandidate::Path(PathImportCandidate { qualifier: Qualifier::Absent, name }),
383 }) 482 })
384} 483}
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 502e8281a..480cbf1ea 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -40,6 +40,7 @@ pub fn find_exact_imports<'a>(
40 )) 40 ))
41} 41}
42 42
43#[derive(Debug)]
43pub enum AssocItemSearch { 44pub enum AssocItemSearch {
44 Include, 45 Include,
45 Exclude, 46 Exclude,