Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18699 from Veykril/push-ltqwwusvtlot
fix: Fix path qualified auto-importing completions not working with re-exports
Lukas Wirth 2024-12-16
parent 65c8d12 · parent ada5f20 · commit dc6af80
-rw-r--r--crates/hir/src/lib.rs6
-rw-r--r--crates/hir/src/semantics.rs19
-rw-r--r--crates/ide-assists/src/handlers/qualify_method_call.rs2
-rw-r--r--crates/ide-assists/src/handlers/qualify_path.rs10
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs2
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type.rs2
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs42
-rw-r--r--crates/ide-db/src/imports/import_assets.rs213
-rw-r--r--crates/ide-db/src/items_locator.rs59
9 files changed, 221 insertions, 134 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 3bc2eee1e7..dfc91c7343 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3105,10 +3105,10 @@ impl From<ModuleDef> for ItemInNs {
}
impl ItemInNs {
- pub fn as_module_def(self) -> Option<ModuleDef> {
+ pub fn into_module_def(self) -> ModuleDef {
match self {
- ItemInNs::Types(id) | ItemInNs::Values(id) => Some(id),
- ItemInNs::Macros(_) => None,
+ ItemInNs::Types(id) | ItemInNs::Values(id) => id,
+ ItemInNs::Macros(id) => ModuleDef::Macro(id),
}
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index f030c1862a..c576d07a0e 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -39,8 +39,8 @@ use stdx::TupleExt;
use syntax::{
algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams},
- AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
- TextSize,
+ AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
+ TextRange, TextSize,
};
use triomphe::Arc;
@@ -1540,6 +1540,21 @@ impl<'db> SemanticsImpl<'db> {
Some(items.iter_items().map(|(item, _)| item.into()))
}
+ pub fn resolve_mod_path_relative(
+ &self,
+ to: Module,
+ segments: impl IntoIterator<Item = SmolStr>,
+ ) -> Option<impl Iterator<Item = ItemInNs>> {
+ let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
+ self.db.upcast(),
+ &ModPath::from_segments(
+ hir_def::path::PathKind::Plain,
+ segments.into_iter().map(|it| Name::new(&it, SyntaxContextId::ROOT)),
+ ),
+ );
+ Some(items.iter_items().map(|(item, _)| item.into()))
+ }
+
fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit)
}
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs
index 14518c4d2c..c3600af5a6 100644
--- a/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -86,7 +86,7 @@ fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs
}
fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
- item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
+ item.into_module_def().as_assoc_item(db)
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs
index ac88861fe4..849b8a42c6 100644
--- a/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/crates/ide-assists/src/handlers/qualify_path.rs
@@ -51,7 +51,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let candidate = import_assets.import_candidate();
let qualify_candidate = match syntax_under_caret.clone() {
NodeOrToken::Node(syntax_under_caret) => match candidate {
- ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
+ ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => {
cov_mark::hit!(qualify_path_qualifier_start);
let path = ast::Path::cast(syntax_under_caret)?;
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
@@ -219,11 +219,9 @@ fn find_trait_method(
}
fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
- let item_module_def = item.as_module_def()?;
-
- match item_module_def {
+ match item.into_module_def() {
hir::ModuleDef::Trait(trait_) => Some(trait_),
- _ => item_module_def.as_assoc_item(db)?.container_trait(db),
+ item_module_def => item_module_def.as_assoc_item(db)?.container_trait(db),
}
}
@@ -247,7 +245,7 @@ fn label(
let import_path = &import.import_path;
match candidate {
- ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
+ ImportCandidate::Path(candidate) if candidate.qualifier.is_empty() => {
format!("Qualify as `{}`", import_path.display(db, edition))
}
_ => format!("Qualify with `{}`", import_path.display(db, edition)),
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 2dec876215..31e828eae2 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -78,7 +78,7 @@ pub(crate) fn replace_derive_with_manual_impl(
NameToImport::exact_case_sensitive(path.segments().last()?.to_string()),
items_locator::AssocSearchMode::Exclude,
)
- .filter_map(|item| match item.as_module_def()? {
+ .filter_map(|item| match item.into_module_def() {
ModuleDef::Trait(trait_) => Some(trait_),
_ => None,
})
diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs
index 2d918a5b1c..658600cd2d 100644
--- a/crates/ide-assists/src/handlers/wrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -198,7 +198,7 @@ fn wrapper_alias(
);
ctx.sema.resolve_mod_path(ret_type.syntax(), &wrapper_path).and_then(|def| {
- def.filter_map(|def| match def.as_module_def()? {
+ def.filter_map(|def| match def.into_module_def() {
hir::ModuleDef::TypeAlias(alias) => {
let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?;
(&enum_ty == core_wrapper).then_some(alias)
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 4b949e0d65..f31b0d4910 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1720,3 +1720,45 @@ fn function() {
"#]],
);
}
+
+#[test]
+fn intrinsics() {
+ check(
+ r#"
+ //- /core.rs crate:core
+ pub mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn transmute<Src, Dst>(src: Src) -> Dst;
+ }
+ }
+ pub mod mem {
+ pub use crate::intrinsics::transmute;
+ }
+ //- /main.rs crate:main deps:core
+ fn function() {
+ transmute$0
+ }
+ "#,
+ expect![[r#"
+ fn transmute(…) (use core::mem::transmute) unsafe fn(Src) -> Dst
+ "#]],
+ );
+ check(
+ r#"
+//- /core.rs crate:core
+pub mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn transmute<Src, Dst>(src: Src) -> Dst;
+ }
+}
+pub mod mem {
+ pub use crate::intrinsics::transmute;
+}
+//- /main.rs crate:main deps:core
+fn function() {
+ mem::transmute$0
+}
+"#,
+ expect![""],
+ );
+}
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 82a182806a..dab36bf20b 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -2,10 +2,10 @@
use hir::{
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
- ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
+ ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics,
SemanticsScope, Trait, TyFingerprint, Type,
};
-use itertools::{EitherOrBoth, Itertools};
+use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{self, make, HasName},
@@ -13,7 +13,6 @@ use syntax::{
};
use crate::{
- helpers::item_name,
items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT},
FxIndexSet, RootDatabase,
};
@@ -52,7 +51,7 @@ pub struct TraitImportCandidate {
#[derive(Debug)]
pub struct PathImportCandidate {
/// Optional qualifier before name.
- pub qualifier: Option<Vec<SmolStr>>,
+ pub qualifier: Vec<SmolStr>,
/// The name the item (struct, trait, enum, etc.) should have.
pub name: NameToImport,
}
@@ -264,7 +263,6 @@ impl ImportAssets {
Some(it) => it,
None => return <FxIndexSet<_>>::default().into_iter(),
};
-
let krate = self.module_with_candidate.krate();
let scope_definitions = self.scope_definitions(sema);
let mod_path = |item| {
@@ -279,11 +277,14 @@ impl ImportAssets {
};
match &self.import_candidate {
- ImportCandidate::Path(path_candidate) => {
- path_applicable_imports(sema, krate, path_candidate, mod_path, |item_to_import| {
- !scope_definitions.contains(&ScopeDef::from(item_to_import))
- })
- }
+ ImportCandidate::Path(path_candidate) => path_applicable_imports(
+ sema,
+ &scope,
+ krate,
+ path_candidate,
+ mod_path,
+ |item_to_import| !scope_definitions.contains(&ScopeDef::from(item_to_import)),
+ ),
ImportCandidate::TraitAssocItem(trait_candidate)
| ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
sema,
@@ -315,6 +316,7 @@ impl ImportAssets {
fn path_applicable_imports(
sema: &Semantics<'_, RootDatabase>,
+ scope: &SemanticsScope<'_>,
current_crate: Crate,
path_candidate: &PathImportCandidate,
mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
@@ -322,8 +324,8 @@ fn path_applicable_imports(
) -> FxIndexSet<LocatedImport> {
let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered();
- match &path_candidate.qualifier {
- None => {
+ match &*path_candidate.qualifier {
+ [] => {
items_locator::items_with_name(
sema,
current_crate,
@@ -348,89 +350,107 @@ fn path_applicable_imports(
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
.collect()
}
- Some(qualifier) => items_locator::items_with_name(
+ [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
sema,
current_crate,
- path_candidate.name.clone(),
- AssocSearchMode::Include,
+ NameToImport::Exact(first_qsegment.to_string(), true),
+ AssocSearchMode::Exclude,
)
- .filter_map(|item| import_for_item(sema.db, mod_path, qualifier, item, scope_filter))
+ .filter_map(|item| {
+ import_for_item(
+ sema,
+ scope,
+ mod_path,
+ &path_candidate.name,
+ item,
+ qualifier_rest,
+ scope_filter,
+ )
+ })
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
.collect(),
}
}
fn import_for_item(
- db: &RootDatabase,
+ sema: &Semantics<'_, RootDatabase>,
+ scope: &SemanticsScope<'_>,
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
+ candidate: &NameToImport,
+ resolved_qualifier: ItemInNs,
unresolved_qualifier: &[SmolStr],
- original_item: ItemInNs,
scope_filter: impl Fn(ItemInNs) -> bool,
) -> Option<LocatedImport> {
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
- let [first_segment, ..] = unresolved_qualifier else { return None };
-
- let item_as_assoc = item_as_assoc(db, original_item);
- let (original_item_candidate, trait_item_to_import) = match item_as_assoc {
- Some(assoc_item) => match assoc_item.container(db) {
- AssocItemContainer::Trait(trait_) => {
- let trait_ = ItemInNs::from(ModuleDef::from(trait_));
- (trait_, Some(trait_))
- }
- AssocItemContainer::Impl(impl_) => {
- (ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), None)
+ let qualifier = {
+ let mut adjusted_resolved_qualifier = resolved_qualifier;
+ 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())?
+ .next()?;
+ }
+ // can't resolve multiple segments for non-module item path bases
+ _ => return None,
}
- },
- None => (original_item, None),
- };
- let import_path_candidate = mod_path(original_item_candidate)?;
-
- let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev();
- let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it {
- // segments match, check next one
- EitherOrBoth::Both(a, b) if b.as_str() == &**a => None,
- // segments mismatch / qualifier is longer than the path, bail out
- EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false),
- // all segments match and we have exhausted the qualifier, proceed
- EitherOrBoth::Right(_) => Some(true),
- };
- if item_as_assoc.is_none() {
- let item_name = item_name(db, original_item)?;
- let last_segment = import_path_candidate_segments.next()?;
- if *last_segment != item_name {
- return None;
}
- }
- let ends_with = unresolved_qualifier
- .iter()
- .rev()
- .zip_longest(import_path_candidate_segments)
- .find_map(predicate)
- .unwrap_or(true);
- if !ends_with {
- return None;
- }
- let segment_import = find_import_for_segment(db, original_item_candidate, first_segment)?;
-
- Some(match (segment_import == original_item_candidate, trait_item_to_import) {
- (true, Some(_)) => {
- // FIXME we should be able to import both the trait and the segment,
- // but it's unclear what to do with overlapping edits (merge imports?)
- // especially in case of lazy completion edit resolutions.
- return None;
+ match adjusted_resolved_qualifier {
+ ItemInNs::Types(def) => def,
+ _ => return None,
}
- (false, Some(trait_to_import)) if scope_filter(trait_to_import) => {
- LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
+ };
+ let import_path_candidate = mod_path(resolved_qualifier)?;
+ let ty = match qualifier {
+ ModuleDef::Module(module) => {
+ return items_locator::items_with_name_in_module(
+ sema,
+ module,
+ candidate.clone(),
+ AssocSearchMode::Exclude,
+ )
+ .find(|&it| scope_filter(it))
+ .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item))
}
- (true, None) if scope_filter(original_item_candidate) => {
- LocatedImport::new(import_path_candidate, original_item_candidate, original_item)
+ // 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),
+ _ => return None,
+ };
+ ty.iterate_path_candidates(sema.db, scope, &FxHashSet::default(), None, None, |assoc| {
+ // FIXME: Support extra trait imports
+ if assoc.container_or_implemented_trait(sema.db).is_some() {
+ return None;
}
- (false, None) if scope_filter(segment_import) => {
- LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
+ let name = assoc.name(sema.db)?;
+ let is_match = match candidate {
+ NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
+ NameToImport::Prefix(text, false) => {
+ name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
+ name_char.eq_ignore_ascii_case(&candidate_char)
+ })
+ }
+ NameToImport::Exact(text, true) => name.as_str() == text,
+ NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
+ NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
+ NameToImport::Fuzzy(text, false) => text
+ .chars()
+ .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
+ };
+ if !is_match {
+ return None;
}
- _ => return None,
+ Some(LocatedImport::new(
+ import_path_candidate.clone(),
+ resolved_qualifier,
+ assoc_to_item(assoc),
+ ))
})
}
@@ -453,45 +473,6 @@ fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Optio
})
}
-fn find_import_for_segment(
- db: &RootDatabase,
- original_item: ItemInNs,
- unresolved_first_segment: &str,
-) -> Option<ItemInNs> {
- let segment_is_name = item_name(db, original_item)
- .map(|name| name.eq_ident(unresolved_first_segment))
- .unwrap_or(false);
-
- Some(if segment_is_name {
- original_item
- } else {
- let matching_module =
- module_with_segment_name(db, unresolved_first_segment, original_item)?;
- ItemInNs::from(ModuleDef::from(matching_module))
- })
-}
-
-fn module_with_segment_name(
- db: &RootDatabase,
- segment_name: &str,
- candidate: ItemInNs,
-) -> Option<Module> {
- let mut current_module = match candidate {
- ItemInNs::Types(module_def_id) => module_def_id.module(db),
- ItemInNs::Values(module_def_id) => module_def_id.module(db),
- ItemInNs::Macros(macro_def_id) => ModuleDef::from(macro_def_id).module(db),
- };
- while let Some(module) = current_module {
- if let Some(module_name) = module.name(db) {
- if module_name.eq_ident(segment_name) {
- return Some(module);
- }
- }
- current_module = module.parent(db);
- }
- None
-}
-
fn trait_applicable_items(
sema: &Semantics<'_, RootDatabase>,
current_crate: Crate,
@@ -703,7 +684,7 @@ impl ImportCandidate {
return None;
}
Some(ImportCandidate::Path(PathImportCandidate {
- qualifier: None,
+ qualifier: vec![],
name: NameToImport::exact_case_sensitive(name.to_string()),
}))
}
@@ -730,7 +711,7 @@ fn path_import_candidate(
.segments()
.map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
.collect::<Option<Vec<_>>>()?;
- ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name })
+ ImportCandidate::Path(PathImportCandidate { qualifier, name })
} else {
return None;
}
@@ -754,10 +735,10 @@ fn path_import_candidate(
}
Some(_) => return None,
},
- None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
+ None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name }),
})
}
fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
- item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
+ item.into_module_def().as_assoc_item(db)
}
diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs
index 47549a1d00..7f66ea0c10 100644
--- a/crates/ide-db/src/items_locator.rs
+++ b/crates/ide-db/src/items_locator.rs
@@ -3,10 +3,14 @@
//! The main reason for this module to exist is the fact that project's items and dependencies' items
//! are located in different caches, with different APIs.
use either::Either;
-use hir::{import_map, Crate, ItemInNs, Semantics};
+use hir::{import_map, Crate, ItemInNs, Module, Semantics};
use limit::Limit;
-use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
+use crate::{
+ imports::import_assets::NameToImport,
+ symbol_index::{self, SymbolsDatabase as _},
+ RootDatabase,
+};
/// A value to use, when uncertain which limit to pick.
pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
@@ -20,8 +24,7 @@ pub fn items_with_name<'a>(
name: NameToImport,
assoc_item_search: AssocSearchMode,
) -> impl Iterator<Item = ItemInNs> + 'a {
- let krate_name = krate.display_name(sema.db).map(|name| name.to_string());
- let _p = tracing::info_span!("items_with_name", name = name.text(), assoc_item_search = ?assoc_item_search, crate = ?krate_name)
+ let _p = tracing::info_span!("items_with_name", name = name.text(), assoc_item_search = ?assoc_item_search, crate = ?krate.display_name(sema.db).map(|name| name.to_string()))
.entered();
let prefix = matches!(name, NameToImport::Prefix(..));
@@ -66,6 +69,54 @@ pub fn items_with_name<'a>(
find_items(sema, krate, local_query, external_query)
}
+/// Searches for importable items with the given name in the crate and its dependencies.
+pub fn items_with_name_in_module<'a>(
+ sema: &'a Semantics<'_, RootDatabase>,
+ module: Module,
+ name: NameToImport,
+ assoc_item_search: AssocSearchMode,
+) -> impl Iterator<Item = ItemInNs> + 'a {
+ let _p = tracing::info_span!("items_with_name_in", name = name.text(), assoc_item_search = ?assoc_item_search, ?module)
+ .entered();
+
+ let prefix = matches!(name, NameToImport::Prefix(..));
+ let local_query = match name {
+ NameToImport::Prefix(exact_name, case_sensitive)
+ | NameToImport::Exact(exact_name, case_sensitive) => {
+ let mut local_query = symbol_index::Query::new(exact_name.clone());
+ local_query.assoc_search_mode(assoc_item_search);
+ if prefix {
+ local_query.prefix();
+ } else {
+ local_query.exact();
+ }
+ if case_sensitive {
+ local_query.case_sensitive();
+ }
+ local_query
+ }
+ NameToImport::Fuzzy(fuzzy_search_string, case_sensitive) => {
+ let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
+ local_query.fuzzy();
+ local_query.assoc_search_mode(assoc_item_search);
+
+ if case_sensitive {
+ local_query.case_sensitive();
+ }
+
+ local_query
+ }
+ };
+ let mut local_results = Vec::new();
+ local_query.search(&[sema.db.module_symbols(module)], |local_candidate| {
+ local_results.push(match local_candidate.def {
+ hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
+ def => ItemInNs::from(def),
+ })
+ });
+ local_results.into_iter()
+}
+
fn find_items<'a>(
sema: &'a Semantics<'_, RootDatabase>,
krate: Crate,