aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/helpers')
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs212
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs8
2 files changed, 98 insertions, 122 deletions
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 7c8844e95..1881c746f 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -61,7 +61,7 @@ pub struct FirstSegmentUnresolved {
61} 61}
62 62
63/// A name that will be used during item lookups. 63/// A name that will be used during item lookups.
64#[derive(Debug)] 64#[derive(Debug, Clone)]
65pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive. 66 /// Requires items with names that exactly match the given string, case-sensitive.
67 Exact(String), 67 Exact(String),
@@ -201,129 +201,101 @@ impl ImportAssets {
201 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
202 prefixed: Option<PrefixKind>, 202 prefixed: Option<PrefixKind>,
203 ) -> Vec<LocatedImport> { 203 ) -> Vec<LocatedImport> {
204 let items_with_candidate_name = match self.name_to_import() { 204 let _p = profile::span("import_assets::search_for");
205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
206 sema,
207 self.module_with_candidate.krate(),
208 exact_name.clone(),
209 ),
210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
211 // instead, we need to look up all trait impls for a certain struct and search through them only
212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
214 // for the details
215 NameToImport::Fuzzy(fuzzy_name) => {
216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
217 (AssocItemSearch::AssocItemsOnly, None)
218 } else {
219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
220 };
221
222 items_locator::with_similar_name(
223 sema,
224 self.module_with_candidate.krate(),
225 fuzzy_name.clone(),
226 assoc_item_search,
227 limit,
228 )
229 }
230 };
231 205
232 let scope_definitions = self.scope_definitions(sema); 206 let scope_definitions = self.scope_definitions(sema);
233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
234 .into_iter()
235 .filter(|import| import.import_path.len() > 1)
236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
237 .sorted_by_key(|import| import.import_path.clone())
238 .collect()
239 }
240
241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
242 let mut scope_definitions = FxHashSet::default();
243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
244 scope_definitions.insert(scope_def);
245 });
246 scope_definitions
247 }
248
249 fn name_to_import(&self) -> &NameToImport {
250 match &self.import_candidate {
251 ImportCandidate::Path(candidate) => &candidate.name,
252 ImportCandidate::TraitAssocItem(candidate)
253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
254 }
255 }
256
257 fn applicable_defs(
258 &self,
259 db: &RootDatabase,
260 prefixed: Option<PrefixKind>,
261 items_with_candidate_name: FxHashSet<ItemInNs>,
262 ) -> FxHashSet<LocatedImport> {
263 let _p = profile::span("import_assets::applicable_defs");
264 let current_crate = self.module_with_candidate.krate(); 207 let current_crate = self.module_with_candidate.krate();
265
266 let mod_path = |item| { 208 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) 209 get_mod_path(
210 sema.db,
211 item_for_path_search(sema.db, item)?,
212 &self.module_with_candidate,
213 prefixed,
214 )
268 }; 215 };
269 216
270 match &self.import_candidate { 217 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => { 218 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) 219 path_applicable_imports(sema, current_crate, path_candidate, mod_path)
220 }
221 ImportCandidate::TraitAssocItem(trait_candidate) => {
222 trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path)
223 }
224 ImportCandidate::TraitMethod(trait_candidate) => {
225 trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path)
273 } 226 }
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 } 227 }
228 .into_iter()
229 .filter(|import| import.import_path.len() > 1)
230 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
231 .sorted_by_key(|import| import.import_path.clone())
232 .collect()
233 }
234
235 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
236 let _p = profile::span("import_assets::scope_definitions");
237 let mut scope_definitions = FxHashSet::default();
238 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
239 scope_definitions.insert(scope_def);
240 });
241 scope_definitions
291 } 242 }
292} 243}
293 244
294fn path_applicable_imports( 245fn path_applicable_imports(
295 db: &RootDatabase, 246 sema: &Semantics<RootDatabase>,
247 current_crate: Crate,
296 path_candidate: &PathImportCandidate, 248 path_candidate: &PathImportCandidate,
297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, 249 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
298 items_with_candidate_name: FxHashSet<ItemInNs>,
299) -> FxHashSet<LocatedImport> { 250) -> FxHashSet<LocatedImport> {
300 let _p = profile::span("import_assets::path_applicable_imports"); 251 let _p = profile::span("import_assets::path_applicable_imports");
301 252
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { 253 match &path_candidate.qualifier {
303 None => { 254 None => {
304 return items_with_candidate_name 255 items_locator::items_with_name(
305 .into_iter() 256 sema,
306 .filter_map(|item| { 257 current_crate,
307 let mut mod_path = mod_path(item)?; 258 path_candidate.name.clone(),
308 if let Some(assoc_item) = item_as_assoc(db, item) { 259 // FIXME: we could look up assoc items by the input and propose those in completion,
309 mod_path.push_segment(assoc_item.name(db)?); 260 // but that requries more preparation first:
310 } 261 // * store non-trait assoc items in import_map to fully enable this lookup
311 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) 262 // * ensure that does not degrade the performance (bencmark it)
312 }) 263 // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now)
313 .collect(); 264 // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
265 //
266 // see also an ignored test under FIXME comment in the qualify_path.rs module
267 AssocItemSearch::Exclude,
268 Some(DEFAULT_QUERY_SEARCH_LIMIT),
269 )
270 .filter_map(|item| {
271 let mod_path = mod_path(item)?;
272 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path)))
273 })
274 .collect()
314 } 275 }
315 Some(first_segment_unresolved) => ( 276 Some(first_segment_unresolved) => {
316 first_segment_unresolved.fist_segment.to_string(), 277 let unresolved_qualifier =
317 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), 278 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier);
318 ), 279 let unresolved_first_segment = first_segment_unresolved.fist_segment.text();
319 }; 280 items_locator::items_with_name(
320 281 sema,
321 items_with_candidate_name 282 current_crate,
322 .into_iter() 283 path_candidate.name.clone(),
323 .filter_map(|item| { 284 AssocItemSearch::Include,
324 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) 285 Some(DEFAULT_QUERY_SEARCH_LIMIT),
325 }) 286 )
326 .collect() 287 .filter_map(|item| {
288 import_for_item(
289 sema.db,
290 mod_path,
291 unresolved_first_segment,
292 &unresolved_qualifier,
293 item,
294 )
295 })
296 .collect()
297 }
298 }
327} 299}
328 300
329fn import_for_item( 301fn import_for_item(
@@ -438,25 +410,31 @@ fn module_with_segment_name(
438} 410}
439 411
440fn trait_applicable_items( 412fn trait_applicable_items(
441 db: &RootDatabase, 413 sema: &Semantics<RootDatabase>,
442 current_crate: Crate, 414 current_crate: Crate,
443 trait_candidate: &TraitImportCandidate, 415 trait_candidate: &TraitImportCandidate,
444 trait_assoc_item: bool, 416 trait_assoc_item: bool,
445 mod_path: impl Fn(ItemInNs) -> Option<ModPath>, 417 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
446 items_with_candidate_name: FxHashSet<ItemInNs>,
447) -> FxHashSet<LocatedImport> { 418) -> FxHashSet<LocatedImport> {
448 let _p = profile::span("import_assets::trait_applicable_items"); 419 let _p = profile::span("import_assets::trait_applicable_items");
449 let mut required_assoc_items = FxHashSet::default();
450 420
451 let trait_candidates = items_with_candidate_name 421 let db = sema.db;
452 .into_iter() 422
453 .filter_map(|input| item_as_assoc(db, input)) 423 let mut required_assoc_items = FxHashSet::default();
454 .filter_map(|assoc| { 424 let trait_candidates = items_locator::items_with_name(
455 let assoc_item_trait = assoc.containing_trait(db)?; 425 sema,
456 required_assoc_items.insert(assoc); 426 current_crate,
457 Some(assoc_item_trait.into()) 427 trait_candidate.assoc_item_name.clone(),
458 }) 428 AssocItemSearch::AssocItemsOnly,
459 .collect(); 429 Some(DEFAULT_QUERY_SEARCH_LIMIT),
430 )
431 .filter_map(|input| item_as_assoc(db, input))
432 .filter_map(|assoc| {
433 let assoc_item_trait = assoc.containing_trait(db)?;
434 required_assoc_items.insert(assoc);
435 Some(assoc_item_trait.into())
436 })
437 .collect();
460 438
461 let mut located_imports = FxHashSet::default(); 439 let mut located_imports = FxHashSet::default();
462 440
@@ -565,10 +543,6 @@ impl ImportCandidate {
565 ) -> Option<Self> { 543 ) -> Option<Self> {
566 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) 544 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
567 } 545 }
568
569 fn is_trait_candidate(&self) -> bool {
570 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
571 }
572} 546}
573 547
574fn path_import_candidate( 548fn path_import_candidate(
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 9e0cb91c3..20c195f82 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -14,10 +14,12 @@ use syntax::{
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
15}; 15};
16 16
17pub use hir::PrefixKind;
18
17#[derive(Clone, Copy, Debug, PartialEq, Eq)] 19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct InsertUseConfig { 20pub struct InsertUseConfig {
19 pub merge: Option<MergeBehavior>, 21 pub merge: Option<MergeBehavior>,
20 pub prefix_kind: hir::PrefixKind, 22 pub prefix_kind: PrefixKind,
21 pub group: bool, 23 pub group: bool,
22} 24}
23 25
@@ -211,7 +213,7 @@ pub fn try_merge_imports(
211 let lhs_tree = lhs.use_tree()?; 213 let lhs_tree = lhs.use_tree()?;
212 let rhs_tree = rhs.use_tree()?; 214 let rhs_tree = rhs.use_tree()?;
213 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; 215 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
214 Some(lhs.with_use_tree(merged)) 216 Some(lhs.with_use_tree(merged).clone_for_update())
215} 217}
216 218
217pub fn try_merge_trees( 219pub fn try_merge_trees(
@@ -232,7 +234,7 @@ pub fn try_merge_trees(
232 } else { 234 } else {
233 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) 235 (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
234 }; 236 };
235 recursive_merge(&lhs, &rhs, merge) 237 recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
236} 238}
237 239
238/// Recursively "zips" together lhs and rhs. 240/// Recursively "zips" together lhs and rhs.