Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/imports/import_assets.rs')
| -rw-r--r-- | crates/ide-db/src/imports/import_assets.rs | 116 |
1 files changed, 78 insertions, 38 deletions
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 77fc59b4ec..ac592dfe93 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -3,20 +3,20 @@ use std::ops::ControlFlow; use hir::{ - db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, + AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, HasCrate, ImportPathConfig, ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, - SemanticsScope, Trait, TyFingerprint, Type, + SemanticsScope, Trait, TyFingerprint, Type, db::HirDatabase, }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ - ast::{self, make, HasName}, AstNode, SyntaxNode, + ast::{self, HasName, make}, }; use crate::{ - items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, FxIndexSet, RootDatabase, + items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, }; /// A candidate for import, derived during various IDE activities: @@ -183,6 +183,9 @@ impl ImportAssets { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompleteInFlyimport(pub bool); + /// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`]. /// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details) #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -198,11 +201,31 @@ pub struct LocatedImport { /// the original item is the associated constant, but the import has to be a trait that /// defines this constant. pub original_item: ItemInNs, + /// The value of `#[rust_analyzer::completions(...)]`, if existing. + pub complete_in_flyimport: CompleteInFlyimport, } impl LocatedImport { - pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self { - Self { import_path, item_to_import, original_item } + pub fn new( + import_path: ModPath, + item_to_import: ItemInNs, + original_item: ItemInNs, + complete_in_flyimport: CompleteInFlyimport, + ) -> Self { + Self { import_path, item_to_import, original_item, complete_in_flyimport } + } + + pub fn new_no_completion( + import_path: ModPath, + item_to_import: ItemInNs, + original_item: ItemInNs, + ) -> Self { + Self { + import_path, + item_to_import, + original_item, + complete_in_flyimport: CompleteInFlyimport(true), + } } } @@ -273,12 +296,13 @@ impl ImportAssets { Some(it) => it, None => return <FxIndexSet<_>>::default().into_iter(), }; + let db = sema.db; let krate = self.module_with_candidate.krate(); let scope_definitions = self.scope_definitions(sema); let mod_path = |item| { get_mod_path( - sema.db, - item_for_path_search(sema.db, item)?, + db, + item_for_path_search(db, item)?, &self.module_with_candidate, prefixed, cfg, @@ -288,7 +312,7 @@ impl ImportAssets { match &self.import_candidate { ImportCandidate::Path(path_candidate) => path_applicable_imports( - sema, + db, &scope, krate, path_candidate, @@ -297,7 +321,7 @@ impl ImportAssets { ), ImportCandidate::TraitAssocItem(trait_candidate) | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( - sema, + db, krate, &scope, trait_candidate, @@ -325,7 +349,7 @@ impl ImportAssets { } fn path_applicable_imports( - sema: &Semantics<'_, RootDatabase>, + db: &RootDatabase, scope: &SemanticsScope<'_>, current_crate: Crate, path_candidate: &PathImportCandidate, @@ -337,7 +361,7 @@ fn path_applicable_imports( match &*path_candidate.qualifier { [] => { items_locator::items_with_name( - sema, + db, current_crate, path_candidate.name.clone(), // FIXME: we could look up assoc items by the input and propose those in completion, @@ -350,12 +374,17 @@ fn path_applicable_imports( // see also an ignored test under FIXME comment in the qualify_path.rs module AssocSearchMode::Exclude, ) - .filter_map(|item| { + .filter_map(|(item, do_not_complete)| { if !scope_filter(item) { return None; } let mod_path = mod_path(item)?; - Some(LocatedImport::new(mod_path, item, item)) + Some(LocatedImport::new( + mod_path, + item, + item, + CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport), + )) }) .take(DEFAULT_QUERY_SEARCH_LIMIT) .collect() @@ -365,22 +394,23 @@ fn path_applicable_imports( // what follows // FIXME: This doesn't handle visibility [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name( - sema, + db, current_crate, NameToImport::Exact(first_qsegment.as_str().to_owned(), true), AssocSearchMode::Exclude, ) - .filter_map(|item| { + .filter_map(|(item, do_not_complete)| { // we found imports for `first_qsegment`, now we need to filter these imports by whether // they result in resolving the rest of the path successfully validate_resolvable( - sema, + db, scope, mod_path, scope_filter, &path_candidate.name, item, qualifier_rest, + CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport), ) }) .take(DEFAULT_QUERY_SEARCH_LIMIT) @@ -391,13 +421,14 @@ fn path_applicable_imports( /// Validates and builds an import for `resolved_qualifier` if the `unresolved_qualifier` appended /// to it resolves and there is a validate `candidate` after that. fn validate_resolvable( - sema: &Semantics<'_, RootDatabase>, + db: &RootDatabase, scope: &SemanticsScope<'_>, mod_path: impl Fn(ItemInNs) -> Option<ModPath>, scope_filter: impl Fn(ItemInNs) -> bool, candidate: &NameToImport, resolved_qualifier: ItemInNs, unresolved_qualifier: &[Name], + complete_in_flyimport: CompleteInFlyimport, ) -> Option<LocatedImport> { let _p = tracing::info_span!("ImportAssets::import_for_item").entered(); @@ -406,8 +437,8 @@ fn validate_resolvable( if !unresolved_qualifier.is_empty() { match resolved_qualifier { ItemInNs::Types(ModuleDef::Module(module)) => { - adjusted_resolved_qualifier = sema - .resolve_mod_path_relative(module, unresolved_qualifier.iter().cloned())? + adjusted_resolved_qualifier = module + .resolve_mod_path(db, unresolved_qualifier.iter().cloned())? .next()?; } // can't resolve multiple segments for non-module item path bases @@ -424,7 +455,7 @@ fn validate_resolvable( let ty = match qualifier { ModuleDef::Module(module) => { return items_locator::items_with_name_in_module( - sema, + db, module, candidate.clone(), AssocSearchMode::Exclude, @@ -433,23 +464,30 @@ fn validate_resolvable( false => ControlFlow::Continue(()), }, ) - .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item)) + .map(|item| { + LocatedImport::new( + import_path_candidate, + resolved_qualifier, + item, + complete_in_flyimport, + ) + }); } // FIXME ModuleDef::Trait(_) => return None, // FIXME ModuleDef::TraitAlias(_) => return None, - ModuleDef::TypeAlias(alias) => alias.ty(sema.db), - ModuleDef::BuiltinType(builtin) => builtin.ty(sema.db), - ModuleDef::Adt(adt) => adt.ty(sema.db), + ModuleDef::TypeAlias(alias) => alias.ty(db), + ModuleDef::BuiltinType(builtin) => builtin.ty(db), + ModuleDef::Adt(adt) => adt.ty(db), _ => return None, }; - ty.iterate_path_candidates(sema.db, scope, &FxHashSet::default(), None, None, |assoc| { + ty.iterate_path_candidates(db, scope, &FxHashSet::default(), None, None, |assoc| { // FIXME: Support extra trait imports - if assoc.container_or_implemented_trait(sema.db).is_some() { + if assoc.container_or_implemented_trait(db).is_some() { return None; } - let name = assoc.name(sema.db)?; + let name = assoc.name(db)?; let is_match = match candidate { NameToImport::Prefix(text, true) => name.as_str().starts_with(text), NameToImport::Prefix(text, false) => { @@ -471,6 +509,7 @@ fn validate_resolvable( import_path_candidate.clone(), resolved_qualifier, assoc_to_item(assoc), + complete_in_flyimport, )) }) } @@ -495,7 +534,7 @@ fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Optio } fn trait_applicable_items( - sema: &Semantics<'_, RootDatabase>, + db: &RootDatabase, current_crate: Crate, scope: &SemanticsScope<'_>, trait_candidate: &TraitImportCandidate, @@ -505,21 +544,19 @@ fn trait_applicable_items( ) -> FxIndexSet<LocatedImport> { let _p = tracing::info_span!("ImportAssets::trait_applicable_items").entered(); - let db = sema.db; - let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db); let env_traits = trait_candidate.receiver_ty.env_traits(db); let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>(); - let mut required_assoc_items = FxHashSet::default(); + let mut required_assoc_items = FxHashMap::default(); let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name( - sema, + db, current_crate, trait_candidate.assoc_item_name.clone(), AssocSearchMode::AssocItemsOnly, ) - .filter_map(|input| item_as_assoc(db, input)) - .filter_map(|assoc| { + .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete))) + .filter_map(|(assoc, do_not_complete)| { if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) { return None; } @@ -528,7 +565,8 @@ fn trait_applicable_items( if related_traits.contains(&assoc_item_trait) { return None; } - required_assoc_items.insert(assoc); + required_assoc_items + .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport)); Some(assoc_item_trait.into()) }) .collect(); @@ -600,7 +638,7 @@ fn trait_applicable_items( None, None, |assoc| { - if required_assoc_items.contains(&assoc) { + if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) { let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let import_path = trait_import_paths @@ -611,6 +649,7 @@ fn trait_applicable_items( import_path, trait_item, assoc_to_item(assoc), + complete_in_flyimport, )); } None::<()> @@ -625,7 +664,7 @@ fn trait_applicable_items( None, |function| { let assoc = function.as_assoc_item(db)?; - if required_assoc_items.contains(&assoc) { + if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) { let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let import_path = trait_import_paths @@ -636,6 +675,7 @@ fn trait_applicable_items( import_path, trait_item, assoc_to_item(assoc), + complete_in_flyimport, )); } None::<()> |