diff options
Diffstat (limited to 'crates/ide_db')
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 82 |
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. |
2 | use hir::{ | 2 | use 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 | }; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
@@ -14,11 +14,16 @@ use crate::{ | |||
14 | 14 | ||
15 | use super::item_name; | 15 | use 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)] |
18 | pub enum ImportCandidate { | 23 | pub 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)] |
32 | pub struct TraitImportCandidate { | 40 | pub 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)] |
38 | pub struct PathImportCandidate { | 49 | pub 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)] |
44 | pub enum Qualifier { | 58 | pub 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)] |
50 | pub enum NameToImport { | 65 | pub 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)] |
65 | pub struct ImportAssets { | 84 | pub 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)] |
131 | pub struct LocatedImport { | 152 | pub 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 | ||
158 | impl ImportAssets { | 179 | impl 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 | ||