diff options
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r-- | crates/ide_db/src/helpers.rs | 10 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 578 | ||||
-rw-r--r-- | crates/ide_db/src/items_locator.rs (renamed from crates/ide_db/src/imports_locator.rs) | 79 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 2 |
4 files changed, 462 insertions, 207 deletions
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 3ff77400b..3c95d3cff 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -2,11 +2,19 @@ | |||
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | 4 | ||
5 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | 5 | use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; |
6 | use syntax::ast::{self, make}; | 6 | use syntax::ast::{self, make}; |
7 | 7 | ||
8 | use crate::RootDatabase; | 8 | use crate::RootDatabase; |
9 | 9 | ||
10 | pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> { | ||
11 | match item { | ||
12 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
13 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
14 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db), | ||
15 | } | ||
16 | } | ||
17 | |||
10 | /// Converts the mod path struct into its ast representation. | 18 | /// Converts the mod path struct into its ast representation. |
11 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | 19 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { |
12 | let _p = profile::span("mod_path_to_ast"); | 20 | let _p = profile::span("mod_path_to_ast"); |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 517abbb4b..e03ccd351 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,19 +1,29 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use either::Either; | 2 | use hir::{ |
3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; | 3 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, |
4 | ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, | ||
5 | }; | ||
6 | use itertools::Itertools; | ||
4 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode}; | 8 | use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode}; |
6 | 9 | ||
7 | use crate::{ | 10 | use crate::{ |
8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, | 11 | items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, |
9 | RootDatabase, | 12 | RootDatabase, |
10 | }; | 13 | }; |
11 | 14 | ||
15 | use super::item_name; | ||
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. | ||
12 | #[derive(Debug)] | 22 | #[derive(Debug)] |
13 | pub enum ImportCandidate { | 23 | pub enum ImportCandidate { |
14 | // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). | 24 | /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`). |
15 | Path(PathImportCandidate), | 25 | Path(PathImportCandidate), |
16 | /// A trait associated function (with no self parameter) or associated constant. | 26 | /// A trait associated function (with no self parameter) or an associated constant. |
17 | /// 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 |
18 | /// and `name` is the `test_function` | 28 | /// and `name` is the `test_function` |
19 | TraitAssocItem(TraitImportCandidate), | 29 | TraitAssocItem(TraitImportCandidate), |
@@ -23,21 +33,40 @@ pub enum ImportCandidate { | |||
23 | TraitMethod(TraitImportCandidate), | 33 | TraitMethod(TraitImportCandidate), |
24 | } | 34 | } |
25 | 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. | ||
26 | #[derive(Debug)] | 39 | #[derive(Debug)] |
27 | pub struct TraitImportCandidate { | 40 | pub struct TraitImportCandidate { |
28 | pub receiver_ty: hir::Type, | 41 | /// A type of the item that has the associated item accessed at. |
29 | pub name: NameToImport, | 42 | pub receiver_ty: Type, |
43 | /// The associated item name that the trait to import should contain. | ||
44 | pub assoc_item_name: NameToImport, | ||
30 | } | 45 | } |
31 | 46 | ||
47 | /// Path import for a given name, qualified or not. | ||
32 | #[derive(Debug)] | 48 | #[derive(Debug)] |
33 | pub struct PathImportCandidate { | 49 | pub struct PathImportCandidate { |
34 | pub qualifier: Option<ast::Path>, | 50 | /// Optional qualifier before name. |
51 | pub qualifier: Option<FirstSegmentUnresolved>, | ||
52 | /// The name the item (struct, trait, enum, etc.) should have. | ||
35 | pub name: NameToImport, | 53 | pub name: NameToImport, |
36 | } | 54 | } |
37 | 55 | ||
56 | /// A qualifier that has a first segment and it's unresolved. | ||
57 | #[derive(Debug)] | ||
58 | pub struct FirstSegmentUnresolved { | ||
59 | fist_segment: ast::NameRef, | ||
60 | full_qualifier: ast::Path, | ||
61 | } | ||
62 | |||
63 | /// A name that will be used during item lookups. | ||
38 | #[derive(Debug)] | 64 | #[derive(Debug)] |
39 | pub enum NameToImport { | 65 | pub enum NameToImport { |
66 | /// Requires items with names that exactly match the given string, case-sensitive. | ||
40 | 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. | ||
41 | Fuzzy(String), | 70 | Fuzzy(String), |
42 | } | 71 | } |
43 | 72 | ||
@@ -50,10 +79,12 @@ impl NameToImport { | |||
50 | } | 79 | } |
51 | } | 80 | } |
52 | 81 | ||
82 | /// A struct to find imports in the project, given a certain name (or its part) and the context. | ||
53 | #[derive(Debug)] | 83 | #[derive(Debug)] |
54 | pub struct ImportAssets { | 84 | pub struct ImportAssets { |
55 | import_candidate: ImportCandidate, | 85 | import_candidate: ImportCandidate, |
56 | module_with_candidate: hir::Module, | 86 | candidate_node: SyntaxNode, |
87 | module_with_candidate: Module, | ||
57 | } | 88 | } |
58 | 89 | ||
59 | impl ImportAssets { | 90 | impl ImportAssets { |
@@ -61,9 +92,11 @@ impl ImportAssets { | |||
61 | method_call: &ast::MethodCallExpr, | 92 | method_call: &ast::MethodCallExpr, |
62 | sema: &Semantics<RootDatabase>, | 93 | sema: &Semantics<RootDatabase>, |
63 | ) -> Option<Self> { | 94 | ) -> Option<Self> { |
95 | let candidate_node = method_call.syntax().clone(); | ||
64 | Some(Self { | 96 | Some(Self { |
65 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, | 97 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, |
66 | module_with_candidate: sema.scope(method_call.syntax()).module()?, | 98 | module_with_candidate: sema.scope(&candidate_node).module()?, |
99 | candidate_node, | ||
67 | }) | 100 | }) |
68 | } | 101 | } |
69 | 102 | ||
@@ -71,94 +104,94 @@ impl ImportAssets { | |||
71 | fully_qualified_path: &ast::Path, | 104 | fully_qualified_path: &ast::Path, |
72 | sema: &Semantics<RootDatabase>, | 105 | sema: &Semantics<RootDatabase>, |
73 | ) -> Option<Self> { | 106 | ) -> Option<Self> { |
74 | let syntax_under_caret = fully_qualified_path.syntax(); | 107 | let candidate_node = fully_qualified_path.syntax().clone(); |
75 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { | 108 | if candidate_node.ancestors().find_map(ast::Use::cast).is_some() { |
76 | return None; | 109 | return None; |
77 | } | 110 | } |
78 | Some(Self { | 111 | Some(Self { |
79 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, | 112 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, |
80 | module_with_candidate: sema.scope(syntax_under_caret).module()?, | 113 | module_with_candidate: sema.scope(&candidate_node).module()?, |
114 | candidate_node, | ||
81 | }) | 115 | }) |
82 | } | 116 | } |
83 | 117 | ||
84 | pub fn for_fuzzy_path( | 118 | pub fn for_fuzzy_path( |
85 | module_with_path: Module, | 119 | module_with_candidate: Module, |
86 | qualifier: Option<ast::Path>, | 120 | qualifier: Option<ast::Path>, |
87 | fuzzy_name: String, | 121 | fuzzy_name: String, |
88 | sema: &Semantics<RootDatabase>, | 122 | sema: &Semantics<RootDatabase>, |
123 | candidate_node: SyntaxNode, | ||
89 | ) -> Option<Self> { | 124 | ) -> Option<Self> { |
90 | Some(match qualifier { | 125 | Some(Self { |
91 | Some(qualifier) => { | 126 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, |
92 | let qualifier_resolution = sema.resolve_path(&qualifier)?; | 127 | module_with_candidate, |
93 | match qualifier_resolution { | 128 | candidate_node, |
94 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self { | ||
95 | import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
96 | receiver_ty: assoc_item_path.ty(sema.db), | ||
97 | name: NameToImport::Fuzzy(fuzzy_name), | ||
98 | }), | ||
99 | module_with_candidate: module_with_path, | ||
100 | }, | ||
101 | _ => Self { | ||
102 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
103 | qualifier: Some(qualifier), | ||
104 | name: NameToImport::Fuzzy(fuzzy_name), | ||
105 | }), | ||
106 | module_with_candidate: module_with_path, | ||
107 | }, | ||
108 | } | ||
109 | } | ||
110 | None => Self { | ||
111 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
112 | qualifier: None, | ||
113 | name: NameToImport::Fuzzy(fuzzy_name), | ||
114 | }), | ||
115 | module_with_candidate: module_with_path, | ||
116 | }, | ||
117 | }) | 129 | }) |
118 | } | 130 | } |
119 | 131 | ||
120 | pub fn for_fuzzy_method_call( | 132 | pub fn for_fuzzy_method_call( |
121 | module_with_method_call: Module, | 133 | module_with_method_call: Module, |
122 | receiver_ty: hir::Type, | 134 | receiver_ty: Type, |
123 | fuzzy_method_name: String, | 135 | fuzzy_method_name: String, |
136 | candidate_node: SyntaxNode, | ||
124 | ) -> Option<Self> { | 137 | ) -> Option<Self> { |
125 | Some(Self { | 138 | Some(Self { |
126 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | 139 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { |
127 | receiver_ty, | 140 | receiver_ty, |
128 | name: NameToImport::Fuzzy(fuzzy_method_name), | 141 | assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name), |
129 | }), | 142 | }), |
130 | module_with_candidate: module_with_method_call, | 143 | module_with_candidate: module_with_method_call, |
144 | candidate_node, | ||
131 | }) | 145 | }) |
132 | } | 146 | } |
133 | } | 147 | } |
134 | 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) | ||
151 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
152 | pub struct LocatedImport { | ||
153 | /// The path to use in the `use` statement for a given candidate to be imported. | ||
154 | pub import_path: ModPath, | ||
155 | /// An item that will be imported with the import path given. | ||
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. | ||
163 | pub original_item: ItemInNs, | ||
164 | /// A path of the original item. | ||
165 | pub original_path: Option<ModPath>, | ||
166 | } | ||
167 | |||
168 | impl LocatedImport { | ||
169 | pub fn new( | ||
170 | import_path: ModPath, | ||
171 | item_to_import: ItemInNs, | ||
172 | original_item: ItemInNs, | ||
173 | original_path: Option<ModPath>, | ||
174 | ) -> Self { | ||
175 | Self { import_path, item_to_import, original_item, original_path } | ||
176 | } | ||
177 | } | ||
178 | |||
135 | impl ImportAssets { | 179 | impl ImportAssets { |
136 | pub fn import_candidate(&self) -> &ImportCandidate { | 180 | pub fn import_candidate(&self) -> &ImportCandidate { |
137 | &self.import_candidate | 181 | &self.import_candidate |
138 | } | 182 | } |
139 | 183 | ||
140 | fn name_to_import(&self) -> &NameToImport { | ||
141 | match &self.import_candidate { | ||
142 | ImportCandidate::Path(candidate) => &candidate.name, | ||
143 | ImportCandidate::TraitAssocItem(candidate) | ||
144 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | pub fn search_for_imports( | 184 | pub fn search_for_imports( |
149 | &self, | 185 | &self, |
150 | sema: &Semantics<RootDatabase>, | 186 | sema: &Semantics<RootDatabase>, |
151 | prefix_kind: PrefixKind, | 187 | prefix_kind: PrefixKind, |
152 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 188 | ) -> Vec<LocatedImport> { |
153 | let _p = profile::span("import_assets::search_for_imports"); | 189 | let _p = profile::span("import_assets::search_for_imports"); |
154 | self.search_for(sema, Some(prefix_kind)) | 190 | self.search_for(sema, Some(prefix_kind)) |
155 | } | 191 | } |
156 | 192 | ||
157 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. | 193 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. |
158 | pub fn search_for_relative_paths( | 194 | pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> { |
159 | &self, | ||
160 | sema: &Semantics<RootDatabase>, | ||
161 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | ||
162 | let _p = profile::span("import_assets::search_for_relative_paths"); | 195 | let _p = profile::span("import_assets::search_for_relative_paths"); |
163 | self.search_for(sema, None) | 196 | self.search_for(sema, None) |
164 | } | 197 | } |
@@ -166,29 +199,29 @@ impl ImportAssets { | |||
166 | fn search_for( | 199 | fn search_for( |
167 | &self, | 200 | &self, |
168 | sema: &Semantics<RootDatabase>, | 201 | sema: &Semantics<RootDatabase>, |
169 | prefixed: Option<hir::PrefixKind>, | 202 | prefixed: Option<PrefixKind>, |
170 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 203 | ) -> Vec<LocatedImport> { |
171 | let current_crate = self.module_with_candidate.krate(); | 204 | let items_with_candidate_name = match self.name_to_import() { |
172 | 205 | NameToImport::Exact(exact_name) => items_locator::with_exact_name( | |
173 | let unfiltered_imports = match self.name_to_import() { | 206 | sema, |
174 | NameToImport::Exact(exact_name) => { | 207 | self.module_with_candidate.krate(), |
175 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) | 208 | exact_name.clone(), |
176 | } | 209 | ), |
177 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: | 210 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: |
178 | // instead, we need to look up all trait impls for a certain struct and search through them only | 211 | // instead, we need to look up all trait impls for a certain struct and search through them only |
179 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 | 212 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 |
180 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 213 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
181 | // for the details | 214 | // for the details |
182 | NameToImport::Fuzzy(fuzzy_name) => { | 215 | NameToImport::Fuzzy(fuzzy_name) => { |
183 | let (assoc_item_search, limit) = match self.import_candidate { | 216 | let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { |
184 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 217 | (AssocItemSearch::AssocItemsOnly, None) |
185 | (AssocItemSearch::AssocItemsOnly, None) | 218 | } else { |
186 | } | 219 | (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT)) |
187 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), | ||
188 | }; | 220 | }; |
189 | imports_locator::find_similar_imports( | 221 | |
222 | items_locator::with_similar_name( | ||
190 | sema, | 223 | sema, |
191 | current_crate, | 224 | self.module_with_candidate.krate(), |
192 | fuzzy_name.clone(), | 225 | fuzzy_name.clone(), |
193 | assoc_item_search, | 226 | assoc_item_search, |
194 | limit, | 227 | limit, |
@@ -196,63 +229,224 @@ impl ImportAssets { | |||
196 | } | 229 | } |
197 | }; | 230 | }; |
198 | 231 | ||
199 | let db = sema.db; | 232 | let scope_definitions = self.scope_definitions(sema); |
200 | let mut res = | 233 | self.applicable_defs(sema.db, prefixed, items_with_candidate_name) |
201 | applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) | 234 | .into_iter() |
202 | .filter_map(|candidate| { | 235 | .filter(|import| import.import_path.len() > 1) |
203 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); | 236 | .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import))) |
204 | 237 | .sorted_by_key(|import| import.import_path.clone()) | |
205 | let item_to_search = match self.import_candidate { | 238 | .collect() |
206 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 239 | } |
207 | let canidate_trait = match candidate { | 240 | |
208 | Either::Left(module_def) => { | 241 | fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> { |
209 | module_def.as_assoc_item(db)?.containing_trait(db) | 242 | let mut scope_definitions = FxHashSet::default(); |
210 | } | 243 | sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| { |
211 | _ => None, | 244 | scope_definitions.insert(scope_def); |
212 | }?; | 245 | }); |
213 | ModuleDef::from(canidate_trait).into() | 246 | scope_definitions |
214 | } | 247 | } |
215 | _ => item, | 248 | |
216 | }; | 249 | fn name_to_import(&self) -> &NameToImport { |
217 | 250 | match &self.import_candidate { | |
218 | if let Some(prefix_kind) = prefixed { | 251 | ImportCandidate::Path(candidate) => &candidate.name, |
219 | self.module_with_candidate.find_use_path_prefixed( | 252 | ImportCandidate::TraitAssocItem(candidate) |
220 | db, | 253 | | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name, |
221 | item_to_search, | 254 | } |
222 | prefix_kind, | 255 | } |
223 | ) | 256 | |
224 | } else { | 257 | fn applicable_defs( |
225 | self.module_with_candidate.find_use_path(db, item_to_search) | 258 | &self, |
226 | } | 259 | db: &RootDatabase, |
227 | .map(|path| (path, item)) | 260 | prefixed: Option<PrefixKind>, |
228 | }) | 261 | items_with_candidate_name: FxHashSet<ItemInNs>, |
229 | .filter(|(use_path, _)| use_path.len() > 1) | 262 | ) -> FxHashSet<LocatedImport> { |
230 | .collect::<Vec<_>>(); | 263 | let _p = profile::span("import_assets::applicable_defs"); |
231 | res.sort_by_cached_key(|(path, _)| path.clone()); | 264 | let current_crate = self.module_with_candidate.krate(); |
232 | res | 265 | |
266 | let mod_path = |item| { | ||
267 | get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) | ||
268 | }; | ||
269 | |||
270 | match &self.import_candidate { | ||
271 | ImportCandidate::Path(path_candidate) => { | ||
272 | path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) | ||
273 | } | ||
274 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( | ||
275 | db, | ||
276 | current_crate, | ||
277 | trait_candidate, | ||
278 | true, | ||
279 | mod_path, | ||
280 | items_with_candidate_name, | ||
281 | ), | ||
282 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | ||
283 | db, | ||
284 | current_crate, | ||
285 | trait_candidate, | ||
286 | false, | ||
287 | mod_path, | ||
288 | items_with_candidate_name, | ||
289 | ), | ||
290 | } | ||
233 | } | 291 | } |
234 | } | 292 | } |
235 | 293 | ||
236 | fn applicable_defs<'a>( | 294 | fn path_applicable_imports( |
237 | import_candidate: &ImportCandidate, | ||
238 | current_crate: Crate, | ||
239 | db: &RootDatabase, | 295 | db: &RootDatabase, |
240 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | 296 | path_candidate: &PathImportCandidate, |
241 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 297 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, |
242 | let receiver_ty = match import_candidate { | 298 | items_with_candidate_name: FxHashSet<ItemInNs>, |
243 | ImportCandidate::Path(_) => return unfiltered_imports, | 299 | ) -> FxHashSet<LocatedImport> { |
244 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | 300 | let _p = profile::span("import_assets::path_applicable_imports"); |
245 | &candidate.receiver_ty | 301 | |
302 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | ||
303 | None => { | ||
304 | return items_with_candidate_name | ||
305 | .into_iter() | ||
306 | .filter_map(|item| { | ||
307 | Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item))) | ||
308 | }) | ||
309 | .collect(); | ||
246 | } | 310 | } |
311 | Some(first_segment_unresolved) => ( | ||
312 | first_segment_unresolved.fist_segment.to_string(), | ||
313 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), | ||
314 | ), | ||
247 | }; | 315 | }; |
248 | 316 | ||
317 | items_with_candidate_name | ||
318 | .into_iter() | ||
319 | .filter_map(|item| { | ||
320 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | ||
321 | }) | ||
322 | .collect() | ||
323 | } | ||
324 | |||
325 | fn import_for_item( | ||
326 | db: &RootDatabase, | ||
327 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
328 | unresolved_first_segment: &str, | ||
329 | unresolved_qualifier: &str, | ||
330 | original_item: ItemInNs, | ||
331 | ) -> Option<LocatedImport> { | ||
332 | let _p = profile::span("import_assets::import_for_item"); | ||
333 | |||
334 | let original_item_candidate = item_for_path_search(db, original_item)?; | ||
335 | let import_path_candidate = mod_path(original_item_candidate)?; | ||
336 | let import_path_string = import_path_candidate.to_string(); | ||
337 | |||
338 | let expected_import_end = if item_as_assoc(db, original_item).is_some() { | ||
339 | unresolved_qualifier.to_string() | ||
340 | } else { | ||
341 | format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?) | ||
342 | }; | ||
343 | if !import_path_string.contains(unresolved_first_segment) | ||
344 | || !import_path_string.ends_with(&expected_import_end) | ||
345 | { | ||
346 | return None; | ||
347 | } | ||
348 | |||
349 | let segment_import = | ||
350 | find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; | ||
351 | let trait_item_to_import = item_as_assoc(db, original_item) | ||
352 | .and_then(|assoc| assoc.containing_trait(db)) | ||
353 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); | ||
354 | Some(match (segment_import == original_item_candidate, trait_item_to_import) { | ||
355 | (true, Some(_)) => { | ||
356 | // FIXME we should be able to import both the trait and the segment, | ||
357 | // but it's unclear what to do with overlapping edits (merge imports?) | ||
358 | // especially in case of lazy completion edit resolutions. | ||
359 | return None; | ||
360 | } | ||
361 | (false, Some(trait_to_import)) => LocatedImport::new( | ||
362 | mod_path(trait_to_import)?, | ||
363 | trait_to_import, | ||
364 | original_item, | ||
365 | mod_path(original_item), | ||
366 | ), | ||
367 | (true, None) => LocatedImport::new( | ||
368 | import_path_candidate, | ||
369 | original_item_candidate, | ||
370 | original_item, | ||
371 | mod_path(original_item), | ||
372 | ), | ||
373 | (false, None) => LocatedImport::new( | ||
374 | mod_path(segment_import)?, | ||
375 | segment_import, | ||
376 | original_item, | ||
377 | mod_path(original_item), | ||
378 | ), | ||
379 | }) | ||
380 | } | ||
381 | |||
382 | fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> { | ||
383 | Some(match item { | ||
384 | ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { | ||
385 | Some(assoc_item) => match assoc_item.container(db) { | ||
386 | AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), | ||
387 | AssocItemContainer::Impl(impl_) => { | ||
388 | ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)) | ||
389 | } | ||
390 | }, | ||
391 | None => item, | ||
392 | }, | ||
393 | ItemInNs::Macros(_) => item, | ||
394 | }) | ||
395 | } | ||
396 | |||
397 | fn find_import_for_segment( | ||
398 | db: &RootDatabase, | ||
399 | original_item: ItemInNs, | ||
400 | unresolved_first_segment: &str, | ||
401 | ) -> Option<ItemInNs> { | ||
402 | let segment_is_name = item_name(db, original_item) | ||
403 | .map(|name| name.to_string() == unresolved_first_segment) | ||
404 | .unwrap_or(false); | ||
405 | |||
406 | Some(if segment_is_name { | ||
407 | original_item | ||
408 | } else { | ||
409 | let matching_module = | ||
410 | module_with_segment_name(db, &unresolved_first_segment, original_item)?; | ||
411 | ItemInNs::from(ModuleDef::from(matching_module)) | ||
412 | }) | ||
413 | } | ||
414 | |||
415 | fn module_with_segment_name( | ||
416 | db: &RootDatabase, | ||
417 | segment_name: &str, | ||
418 | candidate: ItemInNs, | ||
419 | ) -> Option<Module> { | ||
420 | let mut current_module = match candidate { | ||
421 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
422 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
423 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db), | ||
424 | }; | ||
425 | while let Some(module) = current_module { | ||
426 | if let Some(module_name) = module.name(db) { | ||
427 | if module_name.to_string() == segment_name { | ||
428 | return Some(module); | ||
429 | } | ||
430 | } | ||
431 | current_module = module.parent(db); | ||
432 | } | ||
433 | None | ||
434 | } | ||
435 | |||
436 | fn trait_applicable_items( | ||
437 | db: &RootDatabase, | ||
438 | current_crate: Crate, | ||
439 | trait_candidate: &TraitImportCandidate, | ||
440 | trait_assoc_item: bool, | ||
441 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
442 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
443 | ) -> FxHashSet<LocatedImport> { | ||
444 | let _p = profile::span("import_assets::trait_applicable_items"); | ||
249 | let mut required_assoc_items = FxHashSet::default(); | 445 | let mut required_assoc_items = FxHashSet::default(); |
250 | 446 | ||
251 | let trait_candidates = unfiltered_imports | 447 | let trait_candidates = items_with_candidate_name |
252 | .filter_map(|input| match input { | 448 | .into_iter() |
253 | Either::Left(module_def) => module_def.as_assoc_item(db), | 449 | .filter_map(|input| item_as_assoc(db, input)) |
254 | _ => None, | ||
255 | }) | ||
256 | .filter_map(|assoc| { | 450 | .filter_map(|assoc| { |
257 | let assoc_item_trait = assoc.containing_trait(db)?; | 451 | let assoc_item_trait = assoc.containing_trait(db)?; |
258 | required_assoc_items.insert(assoc); | 452 | required_assoc_items.insert(assoc); |
@@ -260,11 +454,10 @@ fn applicable_defs<'a>( | |||
260 | }) | 454 | }) |
261 | .collect(); | 455 | .collect(); |
262 | 456 | ||
263 | let mut applicable_defs = FxHashSet::default(); | 457 | let mut located_imports = FxHashSet::default(); |
264 | 458 | ||
265 | match import_candidate { | 459 | if trait_assoc_item { |
266 | ImportCandidate::Path(_) => unreachable!(), | 460 | trait_candidate.receiver_ty.iterate_path_candidates( |
267 | ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates( | ||
268 | db, | 461 | db, |
269 | current_crate, | 462 | current_crate, |
270 | &trait_candidates, | 463 | &trait_candidates, |
@@ -276,12 +469,21 @@ fn applicable_defs<'a>( | |||
276 | return None; | 469 | return None; |
277 | } | 470 | } |
278 | } | 471 | } |
279 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 472 | |
473 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | ||
474 | let original_item = assoc_to_item(assoc); | ||
475 | located_imports.insert(LocatedImport::new( | ||
476 | mod_path(item)?, | ||
477 | item, | ||
478 | original_item, | ||
479 | mod_path(original_item), | ||
480 | )); | ||
280 | } | 481 | } |
281 | None::<()> | 482 | None::<()> |
282 | }, | 483 | }, |
283 | ), | 484 | ) |
284 | ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( | 485 | } else { |
486 | trait_candidate.receiver_ty.iterate_method_candidates( | ||
285 | db, | 487 | db, |
286 | current_crate, | 488 | current_crate, |
287 | &trait_candidates, | 489 | &trait_candidates, |
@@ -289,21 +491,41 @@ fn applicable_defs<'a>( | |||
289 | |_, function| { | 491 | |_, function| { |
290 | let assoc = function.as_assoc_item(db)?; | 492 | let assoc = function.as_assoc_item(db)?; |
291 | if required_assoc_items.contains(&assoc) { | 493 | if required_assoc_items.contains(&assoc) { |
292 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 494 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
495 | let original_item = assoc_to_item(assoc); | ||
496 | located_imports.insert(LocatedImport::new( | ||
497 | mod_path(item)?, | ||
498 | item, | ||
499 | original_item, | ||
500 | mod_path(original_item), | ||
501 | )); | ||
293 | } | 502 | } |
294 | None::<()> | 503 | None::<()> |
295 | }, | 504 | }, |
296 | ), | 505 | ) |
297 | }; | 506 | }; |
298 | 507 | ||
299 | Box::new(applicable_defs.into_iter()) | 508 | located_imports |
300 | } | 509 | } |
301 | 510 | ||
302 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { | 511 | fn assoc_to_item(assoc: AssocItem) -> ItemInNs { |
303 | match assoc { | 512 | match assoc { |
304 | AssocItem::Function(f) => f.into(), | 513 | AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)), |
305 | AssocItem::Const(c) => c.into(), | 514 | AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)), |
306 | AssocItem::TypeAlias(t) => t.into(), | 515 | AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)), |
516 | } | ||
517 | } | ||
518 | |||
519 | fn get_mod_path( | ||
520 | db: &RootDatabase, | ||
521 | item_to_search: ItemInNs, | ||
522 | module_with_candidate: &Module, | ||
523 | prefixed: Option<PrefixKind>, | ||
524 | ) -> Option<ModPath> { | ||
525 | if let Some(prefix_kind) = prefixed { | ||
526 | module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind) | ||
527 | } else { | ||
528 | module_with_candidate.find_use_path(db, item_to_search) | ||
307 | } | 529 | } |
308 | } | 530 | } |
309 | 531 | ||
@@ -316,7 +538,7 @@ impl ImportCandidate { | |||
316 | Some(_) => None, | 538 | Some(_) => None, |
317 | None => Some(Self::TraitMethod(TraitImportCandidate { | 539 | None => Some(Self::TraitMethod(TraitImportCandidate { |
318 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, | 540 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, |
319 | name: NameToImport::Exact(method_call.name_ref()?.to_string()), | 541 | assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()), |
320 | })), | 542 | })), |
321 | } | 543 | } |
322 | } | 544 | } |
@@ -325,41 +547,63 @@ impl ImportCandidate { | |||
325 | if sema.resolve_path(path).is_some() { | 547 | if sema.resolve_path(path).is_some() { |
326 | return None; | 548 | return None; |
327 | } | 549 | } |
550 | path_import_candidate( | ||
551 | sema, | ||
552 | path.qualifier(), | ||
553 | NameToImport::Exact(path.segment()?.name_ref()?.to_string()), | ||
554 | ) | ||
555 | } | ||
328 | 556 | ||
329 | let segment = path.segment()?; | 557 | fn for_fuzzy_path( |
330 | let candidate = if let Some(qualifier) = path.qualifier() { | 558 | qualifier: Option<ast::Path>, |
331 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 559 | fuzzy_name: String, |
332 | let qualifier_start_path = | 560 | sema: &Semantics<RootDatabase>, |
333 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 561 | ) -> Option<Self> { |
334 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { | 562 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
335 | let qualifier_resolution = if qualifier_start_path == qualifier { | 563 | } |
336 | qualifier_start_resolution | 564 | |
565 | fn is_trait_candidate(&self) -> bool { | ||
566 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) | ||
567 | } | ||
568 | } | ||
569 | |||
570 | fn path_import_candidate( | ||
571 | sema: &Semantics<RootDatabase>, | ||
572 | qualifier: Option<ast::Path>, | ||
573 | name: NameToImport, | ||
574 | ) -> Option<ImportCandidate> { | ||
575 | Some(match qualifier { | ||
576 | Some(qualifier) => match sema.resolve_path(&qualifier) { | ||
577 | None => { | ||
578 | let qualifier_start = | ||
579 | qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
580 | let qualifier_start_path = | ||
581 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
582 | if sema.resolve_path(&qualifier_start_path).is_none() { | ||
583 | ImportCandidate::Path(PathImportCandidate { | ||
584 | qualifier: Some(FirstSegmentUnresolved { | ||
585 | fist_segment: qualifier_start, | ||
586 | full_qualifier: qualifier, | ||
587 | }), | ||
588 | name, | ||
589 | }) | ||
337 | } else { | 590 | } else { |
338 | sema.resolve_path(&qualifier)? | 591 | return None; |
339 | }; | ||
340 | match qualifier_resolution { | ||
341 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | ||
342 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
343 | receiver_ty: assoc_item_path.ty(sema.db), | ||
344 | name: NameToImport::Exact(segment.name_ref()?.to_string()), | ||
345 | }) | ||
346 | } | ||
347 | _ => return None, | ||
348 | } | 592 | } |
349 | } else { | 593 | } |
350 | ImportCandidate::Path(PathImportCandidate { | 594 | Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { |
351 | qualifier: Some(qualifier), | 595 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
352 | name: NameToImport::Exact(qualifier_start.to_string()), | 596 | receiver_ty: assoc_item_path.ty(sema.db), |
597 | assoc_item_name: name, | ||
353 | }) | 598 | }) |
354 | } | 599 | } |
355 | } else { | 600 | Some(_) => return None, |
356 | ImportCandidate::Path(PathImportCandidate { | 601 | }, |
357 | qualifier: None, | 602 | None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), |
358 | name: NameToImport::Exact( | 603 | }) |
359 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), | 604 | } |
360 | ), | 605 | |
361 | }) | 606 | fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> { |
362 | }; | 607 | item.as_module_def_id() |
363 | Some(candidate) | 608 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
364 | } | ||
365 | } | 609 | } |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs index 502e8281a..8a7f02935 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/items_locator.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | //! This module contains an import search functionality that is provided to the assists module. | 1 | //! This module contains an import search functionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use either::Either; | ||
4 | use hir::{ | 5 | use hir::{ |
5 | import_map::{self, ImportKind}, | 6 | import_map::{self, ImportKind}, |
6 | AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, | 7 | AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics, |
7 | }; | 8 | }; |
8 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 9 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
9 | 10 | ||
@@ -12,47 +13,47 @@ use crate::{ | |||
12 | symbol_index::{self, FileSymbol}, | 13 | symbol_index::{self, FileSymbol}, |
13 | RootDatabase, | 14 | RootDatabase, |
14 | }; | 15 | }; |
15 | use either::Either; | ||
16 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
17 | 17 | ||
18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
19 | 19 | ||
20 | pub fn find_exact_imports<'a>( | 20 | pub fn with_exact_name( |
21 | sema: &Semantics<'a, RootDatabase>, | 21 | sema: &Semantics<'_, RootDatabase>, |
22 | krate: Crate, | 22 | krate: Crate, |
23 | name_to_import: String, | 23 | exact_name: String, |
24 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { | 24 | ) -> FxHashSet<ItemInNs> { |
25 | let _p = profile::span("find_exact_imports"); | 25 | let _p = profile::span("find_exact_imports"); |
26 | Box::new(find_imports( | 26 | find_items( |
27 | sema, | 27 | sema, |
28 | krate, | 28 | krate, |
29 | { | 29 | { |
30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); | 30 | let mut local_query = symbol_index::Query::new(exact_name.clone()); |
31 | local_query.exact(); | 31 | local_query.exact(); |
32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); | 32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); |
33 | local_query | 33 | local_query |
34 | }, | 34 | }, |
35 | import_map::Query::new(name_to_import) | 35 | import_map::Query::new(exact_name) |
36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) | 36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) |
37 | .name_only() | 37 | .name_only() |
38 | .search_mode(import_map::SearchMode::Equals) | 38 | .search_mode(import_map::SearchMode::Equals) |
39 | .case_sensitive(), | 39 | .case_sensitive(), |
40 | )) | 40 | ) |
41 | } | 41 | } |
42 | 42 | ||
43 | #[derive(Debug)] | ||
43 | pub enum AssocItemSearch { | 44 | pub enum AssocItemSearch { |
44 | Include, | 45 | Include, |
45 | Exclude, | 46 | Exclude, |
46 | AssocItemsOnly, | 47 | AssocItemsOnly, |
47 | } | 48 | } |
48 | 49 | ||
49 | pub fn find_similar_imports<'a>( | 50 | pub fn with_similar_name( |
50 | sema: &Semantics<'a, RootDatabase>, | 51 | sema: &Semantics<'_, RootDatabase>, |
51 | krate: Crate, | 52 | krate: Crate, |
52 | fuzzy_search_string: String, | 53 | fuzzy_search_string: String, |
53 | assoc_item_search: AssocItemSearch, | 54 | assoc_item_search: AssocItemSearch, |
54 | limit: Option<usize>, | 55 | limit: Option<usize>, |
55 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 56 | ) -> FxHashSet<ItemInNs> { |
56 | let _p = profile::span("find_similar_imports"); | 57 | let _p = profile::span("find_similar_imports"); |
57 | 58 | ||
58 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 59 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>( | |||
76 | local_query.limit(limit); | 77 | local_query.limit(limit); |
77 | } | 78 | } |
78 | 79 | ||
79 | let db = sema.db; | 80 | find_items(sema, krate, local_query, external_query) |
80 | Box::new(find_imports(sema, krate, local_query, external_query).filter( | 81 | .into_iter() |
81 | move |import_candidate| match assoc_item_search { | 82 | .filter(move |&item| match assoc_item_search { |
82 | AssocItemSearch::Include => true, | 83 | AssocItemSearch::Include => true, |
83 | AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), | 84 | AssocItemSearch::Exclude => !is_assoc_item(item, sema.db), |
84 | AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), | 85 | AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db), |
85 | }, | 86 | }) |
86 | )) | 87 | .collect() |
87 | } | 88 | } |
88 | 89 | ||
89 | fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { | 90 | fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { |
90 | match import_candidate { | 91 | item.as_module_def_id() |
91 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), | 92 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
92 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), | 93 | .is_some() |
93 | Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(), | ||
94 | _ => false, | ||
95 | } | ||
96 | } | 94 | } |
97 | 95 | ||
98 | fn find_imports<'a>( | 96 | fn find_items( |
99 | sema: &Semantics<'a, RootDatabase>, | 97 | sema: &Semantics<'_, RootDatabase>, |
100 | krate: Crate, | 98 | krate: Crate, |
101 | local_query: symbol_index::Query, | 99 | local_query: symbol_index::Query, |
102 | external_query: import_map::Query, | 100 | external_query: import_map::Query, |
103 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 101 | ) -> FxHashSet<ItemInNs> { |
104 | let _p = profile::span("find_similar_imports"); | 102 | let _p = profile::span("find_similar_imports"); |
105 | let db = sema.db; | 103 | let db = sema.db; |
106 | 104 | ||
107 | // Query dependencies first. | 105 | // Query dependencies first. |
108 | let mut candidates: FxHashSet<_> = | 106 | let mut candidates = krate |
109 | krate.query_external_importables(db, external_query).collect(); | 107 | .query_external_importables(db, external_query) |
108 | .map(|external_importable| match external_importable { | ||
109 | Either::Left(module_def) => ItemInNs::from(module_def), | ||
110 | Either::Right(macro_def) => ItemInNs::from(macro_def), | ||
111 | }) | ||
112 | .collect::<FxHashSet<_>>(); | ||
110 | 113 | ||
111 | // Query the local crate using the symbol index. | 114 | // Query the local crate using the symbol index. |
112 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); | 115 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); |
@@ -114,19 +117,19 @@ fn find_imports<'a>( | |||
114 | candidates.extend( | 117 | candidates.extend( |
115 | local_results | 118 | local_results |
116 | .into_iter() | 119 | .into_iter() |
117 | .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) | 120 | .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) |
118 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 121 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
119 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), | 122 | Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), |
120 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), | 123 | Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), |
121 | _ => None, | 124 | _ => None, |
122 | }), | 125 | }), |
123 | ); | 126 | ); |
124 | 127 | ||
125 | candidates.into_iter() | 128 | candidates |
126 | } | 129 | } |
127 | 130 | ||
128 | fn get_name_definition<'a>( | 131 | fn get_name_definition( |
129 | sema: &Semantics<'a, RootDatabase>, | 132 | sema: &Semantics<'_, RootDatabase>, |
130 | import_candidate: &FileSymbol, | 133 | import_candidate: &FileSymbol, |
131 | ) -> Option<Definition> { | 134 | ) -> Option<Definition> { |
132 | let _p = profile::span("get_name_definition"); | 135 | let _p = profile::span("get_name_definition"); |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 6eb34b06b..88ee4a87d 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -8,7 +8,7 @@ pub mod line_index; | |||
8 | pub mod symbol_index; | 8 | pub mod symbol_index; |
9 | pub mod defs; | 9 | pub mod defs; |
10 | pub mod search; | 10 | pub mod search; |
11 | pub mod imports_locator; | 11 | pub mod items_locator; |
12 | pub mod source_change; | 12 | pub mod source_change; |
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
14 | pub mod traits; | 14 | pub mod traits; |