aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs82
1 files changed, 52 insertions, 30 deletions
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index f2866af13..3d9df463d 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,7 +1,7 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use hir::{ 2use hir::{
3 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, 3 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
4 ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, Type, 4 ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
5}; 5};
6use itertools::Itertools; 6use itertools::Itertools;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
@@ -14,11 +14,16 @@ use crate::{
14 14
15use super::item_name; 15use super::item_name;
16 16
17/// A candidate for import, derived during various IDE activities:
18/// * completion with imports on the fly proposals
19/// * completion edit resolve requests
20/// * assists
21/// * etc.
17#[derive(Debug)] 22#[derive(Debug)]
18pub enum ImportCandidate { 23pub enum ImportCandidate {
19 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). 24 /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
20 Path(PathImportCandidate), 25 Path(PathImportCandidate),
21 /// A trait associated function (with no self parameter) or associated constant. 26 /// A trait associated function (with no self parameter) or an associated constant.
22 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 27 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
23 /// and `name` is the `test_function` 28 /// and `name` is the `test_function`
24 TraitAssocItem(TraitImportCandidate), 29 TraitAssocItem(TraitImportCandidate),
@@ -28,27 +33,40 @@ pub enum ImportCandidate {
28 TraitMethod(TraitImportCandidate), 33 TraitMethod(TraitImportCandidate),
29} 34}
30 35
36/// A trait import needed for a given associated item access.
37/// For `some::path::SomeStruct::ASSOC_`, contains the
38/// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
31#[derive(Debug)] 39#[derive(Debug)]
32pub struct TraitImportCandidate { 40pub struct TraitImportCandidate {
41 /// A type of the item that has the associated item accessed at.
33 pub receiver_ty: Type, 42 pub receiver_ty: Type,
34 pub name: NameToImport, 43 /// The associated item name that the trait to import should contain.
44 pub assoc_item_name: NameToImport,
35} 45}
36 46
47/// Path import for a given name, qualified or not.
37#[derive(Debug)] 48#[derive(Debug)]
38pub struct PathImportCandidate { 49pub struct PathImportCandidate {
39 pub qualifier: Qualifier, 50 /// Optional qualifier before name.
51 pub qualifier: Option<FirstSegmentUnresolved>,
52 /// The name the item (struct, trait, enum, etc.) should have.
40 pub name: NameToImport, 53 pub name: NameToImport,
41} 54}
42 55
56/// A qualifier that has a first segment and it's unresolved.
43#[derive(Debug)] 57#[derive(Debug)]
44pub enum Qualifier { 58pub struct FirstSegmentUnresolved {
45 Absent, 59 fist_segment: ast::NameRef,
46 FirstSegmentUnresolved(ast::NameRef, ModPath), 60 full_qualifier: ModPath,
47} 61}
48 62
63/// A name that will be used during item lookups.
49#[derive(Debug)] 64#[derive(Debug)]
50pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive.
51 Exact(String), 67 Exact(String),
68 /// Requires items with names that case-insensitively contain all letters from the string,
69 /// in the same order, but not necessary adjacent.
52 Fuzzy(String), 70 Fuzzy(String),
53} 71}
54 72
@@ -61,6 +79,7 @@ impl NameToImport {
61 } 79 }
62} 80}
63 81
82/// A struct to find imports in the project, given a certain name (or its part) and the context.
64#[derive(Debug)] 83#[derive(Debug)]
65pub struct ImportAssets { 84pub struct ImportAssets {
66 import_candidate: ImportCandidate, 85 import_candidate: ImportCandidate,
@@ -119,7 +138,7 @@ impl ImportAssets {
119 Some(Self { 138 Some(Self {
120 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { 139 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
121 receiver_ty, 140 receiver_ty,
122 name: NameToImport::Fuzzy(fuzzy_method_name), 141 assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
123 }), 142 }),
124 module_with_candidate: module_with_method_call, 143 module_with_candidate: module_with_method_call,
125 candidate_node, 144 candidate_node,
@@ -127,11 +146,22 @@ impl ImportAssets {
127 } 146 }
128} 147}
129 148
149/// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`].
150/// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details)
130#[derive(Debug, Clone, PartialEq, Eq, Hash)] 151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131pub struct LocatedImport { 152pub struct LocatedImport {
153 /// The path to use in the `use` statement for a given candidate to be imported.
132 pub import_path: ModPath, 154 pub import_path: ModPath,
155 /// An item that will be imported with the import path given.
133 pub item_to_import: ItemInNs, 156 pub item_to_import: ItemInNs,
157 /// The path import candidate, resolved.
158 ///
159 /// Not necessary matches the import:
160 /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
161 /// the original item is the associated constant, but the import has to be a trait that
162 /// defines this constant.
134 pub original_item: ItemInNs, 163 pub original_item: ItemInNs,
164 /// A path of the original item.
135 pub original_path: Option<ModPath>, 165 pub original_path: Option<ModPath>,
136} 166}
137 167
@@ -144,15 +174,6 @@ impl LocatedImport {
144 ) -> Self { 174 ) -> Self {
145 Self { import_path, item_to_import, original_item, original_path } 175 Self { import_path, item_to_import, original_item, original_path }
146 } 176 }
147
148 pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> {
149 match self.original_item {
150 ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => {
151 ModuleDef::from(module_def_id).name(db)
152 }
153 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
154 }
155 }
156} 177}
157 178
158impl ImportAssets { 179impl ImportAssets {
@@ -229,7 +250,7 @@ impl ImportAssets {
229 match &self.import_candidate { 250 match &self.import_candidate {
230 ImportCandidate::Path(candidate) => &candidate.name, 251 ImportCandidate::Path(candidate) => &candidate.name,
231 ImportCandidate::TraitAssocItem(candidate) 252 ImportCandidate::TraitAssocItem(candidate)
232 | ImportCandidate::TraitMethod(candidate) => &candidate.name, 253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
233 } 254 }
234 } 255 }
235 256
@@ -279,7 +300,7 @@ fn path_applicable_imports(
279 let _p = profile::span("import_assets::path_applicable_imports"); 300 let _p = profile::span("import_assets::path_applicable_imports");
280 301
281 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { 302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
282 Qualifier::Absent => { 303 None => {
283 return items_with_candidate_name 304 return items_with_candidate_name
284 .into_iter() 305 .into_iter()
285 .filter_map(|item| { 306 .filter_map(|item| {
@@ -287,9 +308,10 @@ fn path_applicable_imports(
287 }) 308 })
288 .collect(); 309 .collect();
289 } 310 }
290 Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => { 311 Some(first_segment_unresolved) => (
291 (first_segment.to_string(), qualifier.to_string()) 312 first_segment_unresolved.fist_segment.to_string(),
292 } 313 first_segment_unresolved.full_qualifier.to_string(),
314 ),
293 }; 315 };
294 316
295 items_with_candidate_name 317 items_with_candidate_name
@@ -516,7 +538,7 @@ impl ImportCandidate {
516 Some(_) => None, 538 Some(_) => None,
517 None => Some(Self::TraitMethod(TraitImportCandidate { 539 None => Some(Self::TraitMethod(TraitImportCandidate {
518 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, 540 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
519 name: NameToImport::Exact(method_call.name_ref()?.to_string()), 541 assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
520 })), 542 })),
521 } 543 }
522 } 544 }
@@ -559,10 +581,10 @@ fn path_import_candidate(
559 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 581 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
560 if sema.resolve_path(&qualifier_start_path).is_none() { 582 if sema.resolve_path(&qualifier_start_path).is_none() {
561 ImportCandidate::Path(PathImportCandidate { 583 ImportCandidate::Path(PathImportCandidate {
562 qualifier: Qualifier::FirstSegmentUnresolved( 584 qualifier: Some(FirstSegmentUnresolved {
563 qualifier_start, 585 fist_segment: qualifier_start,
564 ModPath::from_src_unhygienic(qualifier)?, 586 full_qualifier: ModPath::from_src_unhygienic(qualifier)?,
565 ), 587 }),
566 name, 588 name,
567 }) 589 })
568 } else { 590 } else {
@@ -572,12 +594,12 @@ fn path_import_candidate(
572 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { 594 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
573 ImportCandidate::TraitAssocItem(TraitImportCandidate { 595 ImportCandidate::TraitAssocItem(TraitImportCandidate {
574 receiver_ty: assoc_item_path.ty(sema.db), 596 receiver_ty: assoc_item_path.ty(sema.db),
575 name, 597 assoc_item_name: name,
576 }) 598 })
577 } 599 }
578 Some(_) => return None, 600 Some(_) => return None,
579 }, 601 },
580 None => ImportCandidate::Path(PathImportCandidate { qualifier: Qualifier::Absent, name }), 602 None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
581 }) 603 })
582} 604}
583 605