From 97b725e269f4e4d713d3474dd92cceaf10a13ded Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 29 Jun 2023 17:40:22 +0900 Subject: [PATCH 1/5] Remove `name_only` from import map query --- crates/hir-def/src/import_map.rs | 32 +++++++----------------------- crates/ide-db/src/items_locator.rs | 8 +++----- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index ec150dc0689..1124302182b 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -318,7 +318,6 @@ pub enum SearchMode { pub struct Query { query: String, lowercased: String, - name_only: bool, assoc_items_only: bool, search_mode: SearchMode, case_sensitive: bool, @@ -332,7 +331,6 @@ impl Query { Self { query, lowercased, - name_only: false, assoc_items_only: false, search_mode: SearchMode::Contains, case_sensitive: false, @@ -341,13 +339,6 @@ impl Query { } } - /// Matches entries' names only, ignoring the rest of - /// the qualifier. - /// Example: for `std::marker::PhantomData`, the name is `PhantomData`. - pub fn name_only(self) -> Self { - Self { name_only: true, ..self } - } - /// Matches only the entries that are associated items, ignoring the rest. pub fn assoc_items_only(self) -> Self { Self { assoc_items_only: true, ..self } @@ -389,17 +380,13 @@ impl Query { return false; } - let mut input = if import.is_trait_assoc_item || self.name_only { - import.path.segments.last().unwrap().display(db.upcast()).to_string() - } else { - import.path.display(db).to_string() - }; - if enforce_lowercase || !self.case_sensitive { + let mut input = import.path.segments.last().unwrap().display(db.upcast()).to_string(); + let case_insensitive = enforce_lowercase || !self.case_sensitive; + if case_insensitive { input.make_ascii_lowercase(); } - let query_string = - if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased }; + let query_string = if case_insensitive { &self.lowercased } else { &self.query }; match self.search_mode { SearchMode::Equals => &input == query_string, @@ -875,7 +862,6 @@ mod tests { Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), expect![[r#" dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::FMT_CONST (a) dep::fmt::Display::format_function (a) dep::fmt::Display::format_method (a) @@ -917,9 +903,8 @@ mod tests { .search_mode(SearchMode::Fuzzy) .exclude_import_kind(ImportKind::AssociatedItem), expect![[r#" - dep::fmt (t) - dep::fmt::Display (t) - "#]], + dep::fmt (t) + "#]], ); check_search( @@ -968,7 +953,6 @@ mod tests { dep::Fmt (t) dep::Fmt (v) dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::fmt (a) dep::format (f) "#]], @@ -996,7 +980,6 @@ mod tests { dep::Fmt (t) dep::Fmt (v) dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::fmt (a) "#]], ); @@ -1037,7 +1020,6 @@ mod tests { dep::Fmt (t) dep::Fmt (v) dep::fmt (t) - dep::fmt::Display (t) dep::fmt::Display::fmt (a) "#]], ); @@ -1045,7 +1027,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).name_only(), + Query::new("fmt".to_string()), expect![[r#" dep::Fmt (m) dep::Fmt (t) diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 46f1353e2e1..5ba6df3694f 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -48,9 +48,8 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(exact_name.clone()); local_query.exact(); - let external_query = import_map::Query::new(exact_name) - .name_only() - .search_mode(import_map::SearchMode::Equals); + let external_query = + import_map::Query::new(exact_name).search_mode(import_map::SearchMode::Equals); ( local_query, @@ -61,8 +60,7 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone()); let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) - .search_mode(import_map::SearchMode::Fuzzy) - .name_only(); + .search_mode(import_map::SearchMode::Fuzzy); match assoc_item_search { AssocItemSearch::Include => {} AssocItemSearch::Exclude => { From 8cd4e9f7ec2bc2b0663f488e5d0f7ad922911266 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 29 Jun 2023 18:39:37 +0900 Subject: [PATCH 2/5] Merge `assoc_items_only` and `exclude_import_kinds` into `assoc_mode` --- crates/hir-def/src/import_map.rs | 125 ++++-------------- .../replace_derive_with_manual_impl.rs | 2 +- crates/ide-completion/src/lib.rs | 2 +- crates/ide-db/src/imports/import_assets.rs | 8 +- crates/ide-db/src/items_locator.rs | 55 +++----- 5 files changed, 49 insertions(+), 143 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 1124302182b..dd5f6e575cd 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -286,22 +286,6 @@ fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { s } -#[derive(Debug, Eq, PartialEq, Hash)] -pub enum ImportKind { - Module, - Function, - Adt, - EnumVariant, - Const, - Static, - Trait, - TraitAlias, - TypeAlias, - BuiltinType, - AssociatedItem, - Macro, -} - /// A way to match import map contents against the search query. #[derive(Debug)] pub enum SearchMode { @@ -314,15 +298,25 @@ pub enum SearchMode { Fuzzy, } +/// Three possible ways to search for the name in associated and/or other items. +#[derive(Debug, Clone, Copy)] +pub enum AssocSearchMode { + /// Search for the name in both associated and other items. + Include, + /// Search for the name in other items only. + Exclude, + /// Search for the name in the associated items only. + AssocItemsOnly, +} + #[derive(Debug)] pub struct Query { query: String, lowercased: String, - assoc_items_only: bool, search_mode: SearchMode, + assoc_mode: AssocSearchMode, case_sensitive: bool, limit: usize, - exclude_import_kinds: FxHashSet, } impl Query { @@ -331,24 +325,23 @@ impl Query { Self { query, lowercased, - assoc_items_only: false, search_mode: SearchMode::Contains, + assoc_mode: AssocSearchMode::Include, case_sensitive: false, limit: usize::max_value(), - exclude_import_kinds: FxHashSet::default(), } } - /// Matches only the entries that are associated items, ignoring the rest. - pub fn assoc_items_only(self) -> Self { - Self { assoc_items_only: true, ..self } - } - /// Specifies the way to search for the entries using the query. pub fn search_mode(self, search_mode: SearchMode) -> Self { Self { search_mode, ..self } } + /// Specifies whether we want to include associated items in the result. + pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self { + Self { assoc_mode, ..self } + } + /// Limits the returned number of items to `limit`. pub fn limit(self, limit: usize) -> Self { Self { limit, ..self } @@ -359,12 +352,6 @@ impl Query { Self { case_sensitive: true, ..self } } - /// Do not include imports of the specified kind in the search results. - pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self { - self.exclude_import_kinds.insert(import_kind); - self - } - fn import_matches( &self, db: &dyn DefDatabase, @@ -372,12 +359,10 @@ impl Query { enforce_lowercase: bool, ) -> bool { let _p = profile::span("import_map::Query::import_matches"); - if import.is_trait_assoc_item { - if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { - return false; - } - } else if self.assoc_items_only { - return false; + match (import.is_trait_assoc_item, self.assoc_mode) { + (true, AssocSearchMode::Exclude) => return false, + (false, AssocSearchMode::AssocItemsOnly) => return false, + _ => {} } let mut input = import.path.segments.last().unwrap().display(db.upcast()).to_string(); @@ -458,10 +443,6 @@ pub fn search_dependencies( .take_while(|item| { common_importables_path_fst == fst_path(db, &import_map.map[item].path) }) - .filter(|&item| match item_import_kind(item) { - Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), - None => true, - }) .filter(|item| { !query.case_sensitive // we've already checked the common importables path case-insensitively || query.import_matches(db, &import_map.map[item], false) @@ -476,22 +457,6 @@ pub fn search_dependencies( res } -fn item_import_kind(item: ItemInNs) -> Option { - Some(match item.as_module_def_id()? { - ModuleDefId::ModuleId(_) => ImportKind::Module, - ModuleDefId::FunctionId(_) => ImportKind::Function, - ModuleDefId::AdtId(_) => ImportKind::Adt, - ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant, - ModuleDefId::ConstId(_) => ImportKind::Const, - ModuleDefId::StaticId(_) => ImportKind::Static, - ModuleDefId::TraitId(_) => ImportKind::Trait, - ModuleDefId::TraitAliasId(_) => ImportKind::TraitAlias, - ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias, - ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType, - ModuleDefId::MacroId(_) => ImportKind::Macro, - }) -} - #[cfg(test)] mod tests { use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; @@ -888,7 +853,9 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), + Query::new("fmt".to_string()) + .search_mode(SearchMode::Fuzzy) + .assoc_search_mode(AssocSearchMode::AssocItemsOnly), expect![[r#" dep::fmt::Display::FMT_CONST (a) dep::fmt::Display::format_function (a) @@ -901,21 +868,11 @@ mod tests { "main", Query::new("fmt".to_string()) .search_mode(SearchMode::Fuzzy) - .exclude_import_kind(ImportKind::AssociatedItem), + .assoc_search_mode(AssocSearchMode::Exclude), expect![[r#" dep::fmt (t) "#]], ); - - check_search( - ra_fixture, - "main", - Query::new("fmt".to_string()) - .search_mode(SearchMode::Fuzzy) - .assoc_items_only() - .exclude_import_kind(ImportKind::AssociatedItem), - expect![[r#""#]], - ); } #[test] @@ -1101,34 +1058,4 @@ mod tests { "#]], ); } - - #[test] - fn search_exclusions() { - let ra_fixture = r#" - //- /main.rs crate:main deps:dep - //- /dep.rs crate:dep - - pub struct fmt; - pub struct FMT; - "#; - - check_search( - ra_fixture, - "main", - Query::new("FMT".to_string()), - expect![[r#" - dep::FMT (t) - dep::FMT (v) - dep::fmt (t) - dep::fmt (v) - "#]], - ); - - check_search( - ra_fixture, - "main", - Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt), - expect![[r#""#]], - ); - } } 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 3bdd795bea8..c03bc2f41d5 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 @@ -73,7 +73,7 @@ pub(crate) fn replace_derive_with_manual_impl( &ctx.sema, current_crate, NameToImport::exact_case_sensitive(path.segments().last()?.to_string()), - items_locator::AssocItemSearch::Exclude, + items_locator::AssocSearchMode::Exclude, Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| match item.as_module_def()? { diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 106d4e1e52f..2eaa42040a0 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -231,7 +231,7 @@ pub fn resolve_completion_edits( &sema, current_crate, NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocItemSearch::Include, + items_locator::AssocSearchMode::Include, Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()), ); let import = items_with_name diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 901d592c691..e52dc356775 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -13,7 +13,7 @@ use syntax::{ use crate::{ helpers::item_name, - items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, + items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, RootDatabase, }; @@ -317,7 +317,7 @@ fn path_applicable_imports( // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear // // see also an ignored test under FIXME comment in the qualify_path.rs module - AssocItemSearch::Exclude, + AssocSearchMode::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| { @@ -334,7 +334,7 @@ fn path_applicable_imports( sema, current_crate, path_candidate.name.clone(), - AssocItemSearch::Include, + AssocSearchMode::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|item| { @@ -483,7 +483,7 @@ fn trait_applicable_items( sema, current_crate, trait_candidate.assoc_item_name.clone(), - AssocItemSearch::AssocItemsOnly, + AssocSearchMode::AssocItemsOnly, Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), ) .filter_map(|input| item_as_assoc(db, input)) diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 5ba6df3694f..8fab33f38be 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -3,10 +3,7 @@ //! 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::{self, ImportKind}, - AsAssocItem, Crate, ItemInNs, Semantics, -}; +use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics}; use limit::Limit; use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; @@ -14,23 +11,14 @@ use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; /// A value to use, when uncertain which limit to pick. pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40); -/// Three possible ways to search for the name in associated and/or other items. -#[derive(Debug, Clone, Copy)] -pub enum AssocItemSearch { - /// Search for the name in both associated and other items. - Include, - /// Search for the name in other items only. - Exclude, - /// Search for the name in the associated items only. - AssocItemsOnly, -} +pub use import_map::AssocSearchMode; /// Searches for importable items with the given name in the crate and its dependencies. pub fn items_with_name<'a>( sema: &'a Semantics<'_, RootDatabase>, krate: Crate, name: NameToImport, - assoc_item_search: AssocItemSearch, + assoc_item_search: AssocSearchMode, limit: Option, ) -> impl Iterator + 'a { let _p = profile::span("items_with_name").detail(|| { @@ -60,16 +48,8 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone()); let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) - .search_mode(import_map::SearchMode::Fuzzy); - match assoc_item_search { - AssocItemSearch::Include => {} - AssocItemSearch::Exclude => { - external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem); - } - AssocItemSearch::AssocItemsOnly => { - external_query = external_query.assoc_items_only(); - } - } + .search_mode(import_map::SearchMode::Fuzzy) + .assoc_search_mode(assoc_item_search); if fuzzy_search_string.to_lowercase() != fuzzy_search_string { local_query.case_sensitive(); @@ -91,13 +71,15 @@ pub fn items_with_name<'a>( fn find_items<'a>( sema: &'a Semantics<'_, RootDatabase>, krate: Crate, - assoc_item_search: AssocItemSearch, + assoc_item_search: AssocSearchMode, local_query: symbol_index::Query, external_query: import_map::Query, ) -> impl Iterator + 'a { let _p = profile::span("find_items"); let db = sema.db; + // NOTE: `external_query` includes `assoc_item_search`, so we don't need to + // filter on our own. let external_importables = krate.query_external_importables(db, external_query).map(|external_importable| { match external_importable { @@ -110,18 +92,15 @@ fn find_items<'a>( let local_results = local_query .search(&symbol_index::crate_symbols(db, krate)) .into_iter() - .filter_map(|local_candidate| match local_candidate.def { - hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)), - def => Some(ItemInNs::from(def)), + .filter(move |candidate| match assoc_item_search { + AssocSearchMode::Include => true, + AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(), + AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(), + }) + .map(|local_candidate| match local_candidate.def { + hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), + def => ItemInNs::from(def), }); - external_importables.chain(local_results).filter(move |&item| match assoc_item_search { - AssocItemSearch::Include => true, - AssocItemSearch::Exclude => !is_assoc_item(item, sema.db), - AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db), - }) -} - -fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { - item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some() + external_importables.chain(local_results) } From 860628af7cf9a2bde0827ff58b5f850e3db83e14 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 29 Jun 2023 18:53:59 +0900 Subject: [PATCH 3/5] Remove `SearchMode:Contains` Also micro-optimizes fuzzy search. --- crates/hir-def/src/import_map.rs | 62 +++++++++--------------------- crates/ide-db/src/items_locator.rs | 5 +-- 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index dd5f6e575cd..fb7870c72f3 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -288,11 +288,9 @@ fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { /// A way to match import map contents against the search query. #[derive(Debug)] -pub enum SearchMode { +enum SearchMode { /// Import map entry should strictly match the query string. - Equals, - /// Import map entry should contain the query string. - Contains, + Exact, /// Import map entry should contain all letters from the query string, /// in the same order, but not necessary adjacent. Fuzzy, @@ -325,16 +323,16 @@ impl Query { Self { query, lowercased, - search_mode: SearchMode::Contains, + search_mode: SearchMode::Exact, assoc_mode: AssocSearchMode::Include, case_sensitive: false, - limit: usize::max_value(), + limit: usize::MAX, } } - /// Specifies the way to search for the entries using the query. - pub fn search_mode(self, search_mode: SearchMode) -> Self { - Self { search_mode, ..self } + /// Fuzzy finds items instead of exact matching. + pub fn fuzzy(self) -> Self { + Self { search_mode: SearchMode::Fuzzy, ..self } } /// Specifies whether we want to include associated items in the result. @@ -374,22 +372,15 @@ impl Query { let query_string = if case_insensitive { &self.lowercased } else { &self.query }; match self.search_mode { - SearchMode::Equals => &input == query_string, - SearchMode::Contains => input.contains(query_string), + SearchMode::Exact => &input == query_string, SearchMode::Fuzzy => { - let mut unchecked_query_chars = query_string.chars(); - let mut mismatching_query_char = unchecked_query_chars.next(); - - for input_char in input.chars() { - match mismatching_query_char { - None => return true, - Some(matching_query_char) if matching_query_char == input_char => { - mismatching_query_char = unchecked_query_chars.next(); - } - _ => (), + let mut input_chars = input.chars(); + for query_char in query_string.chars() { + if input_chars.find(|&it| it == query_char).is_none() { + return false; } } - mismatching_query_char.is_none() + true } } } @@ -824,7 +815,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), + Query::new("fmt".to_string()).fuzzy(), expect![[r#" dep::fmt (t) dep::fmt::Display::FMT_CONST (a) @@ -854,7 +845,7 @@ mod tests { ra_fixture, "main", Query::new("fmt".to_string()) - .search_mode(SearchMode::Fuzzy) + .fuzzy() .assoc_search_mode(AssocSearchMode::AssocItemsOnly), expect![[r#" dep::fmt::Display::FMT_CONST (a) @@ -866,9 +857,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()) - .search_mode(SearchMode::Fuzzy) - .assoc_search_mode(AssocSearchMode::Exclude), + Query::new("fmt".to_string()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude), expect![[r#" dep::fmt (t) "#]], @@ -904,7 +893,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), + Query::new("fmt".to_string()).fuzzy(), expect![[r#" dep::Fmt (m) dep::Fmt (t) @@ -918,20 +907,7 @@ mod tests { check_search( ra_fixture, "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Equals), - expect![[r#" - dep::Fmt (m) - dep::Fmt (t) - dep::Fmt (v) - dep::fmt (t) - dep::fmt::Display::fmt (a) - "#]], - ); - - check_search( - ra_fixture, - "main", - Query::new("fmt".to_string()).search_mode(SearchMode::Contains), + Query::new("fmt".to_string()), expect![[r#" dep::Fmt (m) dep::Fmt (t) @@ -1049,7 +1025,7 @@ mod tests { pub fn no() {} "#, "main", - Query::new("".to_string()).limit(2), + Query::new("".to_string()).fuzzy().limit(2), expect![[r#" dep::Fmt (m) dep::Fmt (t) diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 8fab33f38be..3f7a3ec2d0f 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -36,8 +36,7 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(exact_name.clone()); local_query.exact(); - let external_query = - import_map::Query::new(exact_name).search_mode(import_map::SearchMode::Equals); + let external_query = import_map::Query::new(exact_name); ( local_query, @@ -48,7 +47,7 @@ pub fn items_with_name<'a>( let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone()); let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) - .search_mode(import_map::SearchMode::Fuzzy) + .fuzzy() .assoc_search_mode(assoc_item_search); if fuzzy_search_string.to_lowercase() != fuzzy_search_string { From 2b106648a75f2d76b817b9411a8449d2a79f5283 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Fri, 30 Jun 2023 23:24:15 +0900 Subject: [PATCH 4/5] Only store item name instead of full path --- crates/hir-def/src/find_path.rs | 2 +- crates/hir-def/src/import_map.rs | 361 +++++++++++++------------------ 2 files changed, 149 insertions(+), 214 deletions(-) diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 8c49ae1c4af..df2af4c89b0 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -360,7 +360,7 @@ fn calculate_best_path( prefer_no_std, )?; cov_mark::hit!(partially_imported); - path.push_segment(info.path.segments.last()?.clone()); + path.push_segment(info.name.clone()); Some(path) }) }); diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index fb7870c72f3..63caaa8f831 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -1,13 +1,14 @@ //! A map of all publicly exported items in a crate. +use std::collections::hash_map::Entry; use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; use fst::{self, Streamer}; use hir_expand::name::Name; -use indexmap::{map::Entry, IndexMap}; +use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashSet, FxHasher}; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use triomphe::Arc; use crate::{ @@ -17,52 +18,23 @@ use crate::{ type FxIndexMap = IndexMap>; +// FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should +// have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`. /// Item import details stored in the `ImportMap`. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ImportInfo { - /// A path that can be used to import the item, relative to the crate's root. - pub path: ImportPath, + /// A name that can be used to import the item, relative to the crate's root. + pub name: Name, /// The module containing this item. pub container: ModuleId, /// Whether the import is a trait associated item or not. pub is_trait_assoc_item: bool, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ImportPath { - pub segments: Vec, -} - -impl ImportPath { - pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a { - struct Display<'a> { - db: &'a dyn DefDatabase, - path: &'a ImportPath, - } - impl fmt::Display for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt( - &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"), - f, - ) - } - } - Display { db, path: self } - } - - fn len(&self) -> usize { - self.segments.len() - } -} - -/// A map from publicly exported items to the path needed to import/name them from a downstream -/// crate. +/// A map from publicly exported items to its name. /// /// Reexports of items are taken into account, ie. if something is exported under multiple /// names, the one with the shortest import path will be used. -/// -/// Note that all paths are relative to the containing crate's root, so the crate name still needs -/// to be prepended to the `ModPath` before the path is valid. #[derive(Default)] pub struct ImportMap { map: FxIndexMap, @@ -70,84 +42,50 @@ pub struct ImportMap { /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// - /// Since a path can refer to multiple items due to namespacing, we store all items with the - /// same path right after each other. This allows us to find all items after the FST gives us + /// Since a name can refer to multiple items due to namespacing, we store all items with the + /// same name right after each other. This allows us to find all items after the FST gives us /// the index of the first one. importables: Vec, fst: fst::Map>, } impl ImportMap { - pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { + pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("import_map_query"); let mut import_map = collect_import_map(db, krate); - let mut importables = import_map + let mut importables: Vec<_> = import_map .map .iter() - .map(|(item, info)| (item, fst_path(db, &info.path))) - .collect::>(); - importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2)); + // We've only collected items, whose name cannot be tuple field. + .map(|(item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + .collect(); + importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); // Build the FST, taking care not to insert duplicate values. - let mut builder = fst::MapBuilder::memory(); - let mut last_batch_start = 0; - - for idx in 0..importables.len() { - let key = &importables[last_batch_start].1; - if let Some((_, fst_path)) = importables.get(idx + 1) { - if key == fst_path { - continue; - } - } - - let _ = builder.insert(key, last_batch_start as u64); - - last_batch_start = idx + 1; + let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1); + for (start_idx, (_, name)) in iter { + let _ = builder.insert(name, start_idx as u64); } import_map.fst = builder.into_map(); - import_map.importables = importables.iter().map(|&(&item, _)| item).collect(); + import_map.importables = importables.into_iter().map(|(&item, _)| item).collect(); Arc::new(import_map) } - /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. - pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> { - self.import_info_for(item).map(|it| &it.path) - } - pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { self.map.get(&item) } - #[cfg(test)] - fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { - let mut importable_paths: Vec<_> = self - .map - .iter() - .map(|(item, info)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({ns})", info.path.display(db)) - }) - .collect(); - - importable_paths.sort(); - importable_paths.join("\n") - } - fn collect_trait_assoc_items( &mut self, db: &dyn DefDatabase, tr: TraitId, is_type_in_ns: bool, - original_import_info: &ImportInfo, + trait_import_info: &ImportInfo, ) { let _p = profile::span("collect_trait_assoc_items"); for (assoc_item_name, item) in &db.trait_data(tr).items { @@ -167,9 +105,11 @@ impl ImportMap { ItemInNs::Values(module_def_id) }; - let mut assoc_item_info = original_import_info.clone(); - assoc_item_info.path.segments.push(assoc_item_name.to_owned()); - assoc_item_info.is_trait_assoc_item = true; + let assoc_item_info = ImportInfo { + container: trait_import_info.container, + name: assoc_item_name.clone(), + is_trait_assoc_item: true, + }; self.map.insert(assoc_item, assoc_item_info); } } @@ -182,10 +122,10 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { let mut import_map = ImportMap::default(); // We look only into modules that are public(ly reexported), starting with the crate root. - let empty = ImportPath { segments: vec![] }; let root = def_map.module_id(DefMap::ROOT); - let mut worklist = vec![(root, empty)]; - while let Some((module, mod_path)) = worklist.pop() { + let mut worklist = vec![(root, 0)]; + let mut depth_map = FxHashMap::default(); + while let Some((module, depth)) = worklist.pop() { let ext_def_map; let mod_data = if module.krate == krate { &def_map[module.local_id] @@ -205,17 +145,25 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { }); for (name, per_ns) in visible_items { - let mk_path = || { - let mut path = mod_path.clone(); - path.segments.push(name.clone()); - path - }; - for item in per_ns.iter_items() { - let path = mk_path(); - let path_len = path.len(); - let import_info = - ImportInfo { path, container: module, is_trait_assoc_item: false }; + let import_info = ImportInfo { + name: name.clone(), + container: module, + is_trait_assoc_item: false, + }; + + match depth_map.entry(item) { + Entry::Vacant(entry) => { + entry.insert(depth); + } + Entry::Occupied(mut entry) => { + if depth < *entry.get() { + entry.insert(depth); + } else { + continue; + } + } + } if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { import_map.collect_trait_assoc_items( @@ -226,25 +174,13 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { ); } - match import_map.map.entry(item) { - Entry::Vacant(entry) => { - entry.insert(import_info); - } - Entry::Occupied(mut entry) => { - // If the new path is shorter, prefer that one. - if path_len < entry.get().path.len() { - *entry.get_mut() = import_info; - } else { - continue; - } - } - } + import_map.map.insert(item, import_info); - // If we've just added a path to a module, descend into it. We might traverse - // modules multiple times, but only if the new path to it is shorter than the - // first (else we `continue` above). + // If we've just added a module, descend into it. We might traverse modules + // multiple times, but only if the module depth is smaller (else we `continue` + // above). if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { - worklist.push((mod_id, mk_path())); + worklist.push((mod_id, depth + 1)); } } } @@ -264,7 +200,7 @@ impl Eq for ImportMap {} impl fmt::Debug for ImportMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut importable_paths: Vec<_> = self + let mut importable_names: Vec<_> = self .map .iter() .map(|(item, _)| match item { @@ -274,18 +210,11 @@ impl fmt::Debug for ImportMap { }) .collect(); - importable_paths.sort(); - f.write_str(&importable_paths.join("\n")) + importable_names.sort(); + f.write_str(&importable_names.join("\n")) } } -fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { - let _p = profile::span("fst_path"); - let mut s = path.display(db).to_string(); - s.make_ascii_lowercase(); - s -} - /// A way to match import map contents against the search query. #[derive(Debug)] enum SearchMode { @@ -363,7 +292,7 @@ impl Query { _ => {} } - let mut input = import.path.segments.last().unwrap().display(db.upcast()).to_string(); + let mut input = import.name.display(db.upcast()).to_string(); let case_insensitive = enforce_lowercase || !self.case_sensitive; if case_insensitive { input.make_ascii_lowercase(); @@ -386,7 +315,7 @@ impl Query { } } -/// Searches dependencies of `krate` for an importable path matching `query`. +/// Searches dependencies of `krate` for an importable name matching `query`. /// /// This returns a list of items that could be imported from dependencies of `krate`. pub fn search_dependencies( @@ -409,39 +338,38 @@ pub fn search_dependencies( let mut stream = op.union(); - let mut all_indexed_values = FxHashSet::default(); - while let Some((_, indexed_values)) = stream.next() { - all_indexed_values.extend(indexed_values.iter().copied()); - } - let mut res = FxHashSet::default(); - for indexed_value in all_indexed_values { - let import_map = &import_maps[indexed_value.index]; - let importables = &import_map.importables[indexed_value.value as usize..]; + while let Some((_, indexed_values)) = stream.next() { + for indexed_value in indexed_values { + let import_map = &import_maps[indexed_value.index]; + let importables = &import_map.importables[indexed_value.value as usize..]; - let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(db, common_importable_data, true) { - continue; - } + let common_importable_data = &import_map.map[&importables[0]]; + if !query.import_matches(db, common_importable_data, true) { + continue; + } - // Path shared by the importable items in this group. - let common_importables_path_fst = fst_path(db, &common_importable_data.path); - // Add the items from this `ModPath` group. Those are all subsequent items in - // `importables` whose paths match `path`. - let iter = importables - .iter() - .copied() - .take_while(|item| { - common_importables_path_fst == fst_path(db, &import_map.map[item].path) - }) - .filter(|item| { - !query.case_sensitive // we've already checked the common importables path case-insensitively + // Name shared by the importable items in this group. + let common_importable_name = + common_importable_data.name.to_smol_str().to_ascii_lowercase(); + // Add the items from this name group. Those are all subsequent items in + // `importables` whose name match `common_importable_name`. + let iter = importables + .iter() + .copied() + .take_while(|item| { + common_importable_name + == import_map.map[item].name.to_smol_str().to_ascii_lowercase() + }) + .filter(|item| { + !query.case_sensitive // we've already checked the common importables name case-insensitively || query.import_matches(db, &import_map.map[item], false) - }); - res.extend(iter); + }); + res.extend(iter); - if res.len() >= query.limit { - return res; + if res.len() >= query.limit { + return res; + } } } @@ -457,16 +385,39 @@ mod tests { use super::*; + impl ImportMap { + fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, info)| { + let path = render_path(db, info); + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {path} ({ns})") + }) + .collect(); + + importable_paths.sort(); + importable_paths.join("\n") + } + } + fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) { let db = TestDB::with_files(ra_fixture); let crate_graph = db.crate_graph(); let krate = crate_graph .iter() - .find(|krate| { - crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) - == Some(crate_name.to_string()) + .find(|&krate| { + crate_graph[krate] + .display_name + .as_ref() + .is_some_and(|it| &**it.crate_name() == crate_name) }) - .unwrap(); + .expect("could not find crate"); let actual = search_dependencies(db.upcast(), krate, query) .into_iter() @@ -477,7 +428,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - dependency_imports.path_of(dependency)?.display(&db).to_string(), + render_path(&db, dependency_imports.import_info_for(dependency)?), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -507,57 +458,25 @@ mod tests { dependency_imports: &ImportMap, dependency: ItemInNs, ) -> Option { - let dependency_assoc_item_id = match dependency { - ItemInNs::Types(ModuleDefId::FunctionId(id)) - | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id), - ItemInNs::Types(ModuleDefId::ConstId(id)) - | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id), - ItemInNs::Types(ModuleDefId::TypeAliasId(id)) - | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id), + let (dependency_assoc_item_id, container) = match dependency.as_module_def_id()? { + ModuleDefId::FunctionId(id) => (AssocItemId::from(id), id.lookup(db).container), + ModuleDefId::ConstId(id) => (AssocItemId::from(id), id.lookup(db).container), + ModuleDefId::TypeAliasId(id) => (AssocItemId::from(id), id.lookup(db).container), _ => return None, }; - let trait_ = assoc_to_trait(db, dependency)?; - if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? { - let trait_data = db.trait_data(tr); - let assoc_item_name = - trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| { - if &dependency_assoc_item_id == assoc_item_id { - Some(assoc_item_name) - } else { - None - } - })?; - return Some(format!( - "{}::{}", - dependency_imports.path_of(trait_)?.display(db), - assoc_item_name.display(db.upcast()) - )); - } - None - } - - fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option { - let assoc: AssocItemId = match item { - ItemInNs::Types(it) | ItemInNs::Values(it) => match it { - ModuleDefId::TypeAliasId(it) => it.into(), - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - _ => return None, - }, - _ => return None, + let ItemContainerId::TraitId(trait_id) = container else { + return None; }; - let container = match assoc { - AssocItemId::FunctionId(it) => it.lookup(db).container, - AssocItemId::ConstId(it) => it.lookup(db).container, - AssocItemId::TypeAliasId(it) => it.lookup(db).container, - }; + let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?; - match container { - ItemContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())), - _ => None, - } + let trait_data = db.trait_data(trait_id); + let (assoc_item_name, _) = trait_data + .items + .iter() + .find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?; + Some(format!("{}::{}", render_path(db, trait_info), assoc_item_name.display(db.upcast()))) } fn check(ra_fixture: &str, expect: Expect) { @@ -580,6 +499,24 @@ mod tests { expect.assert_eq(&actual) } + fn render_path(db: &dyn DefDatabase, info: &ImportInfo) -> String { + let mut module = info.container; + let mut segments = vec![&info.name]; + + let def_map = module.def_map(db); + assert!(def_map.block_id().is_none(), "block local items should not be in `ImportMap`"); + + while let Some(parent) = module.containing_module(db) { + let parent_data = &def_map[parent.local_id]; + let (name, _) = + parent_data.children.iter().find(|(_, id)| **id == module.local_id).unwrap(); + segments.push(name); + module = parent; + } + + segments.iter().rev().map(|it| it.display(db.upcast())).join("::") + } + #[test] fn smoke() { check( @@ -696,6 +633,7 @@ mod tests { #[test] fn module_reexport() { // Reexporting modules from a dependency adds all contents to the import map. + // XXX: The rendered paths are relative to the defining crate. check( r" //- /main.rs crate:main deps:lib @@ -711,9 +649,9 @@ mod tests { - module::S (t) - module::S (v) main: + - module::S (t) + - module::S (v) - reexported_module (t) - - reexported_module::S (t) - - reexported_module::S (v) "#]], ); } @@ -1025,12 +963,9 @@ mod tests { pub fn no() {} "#, "main", - Query::new("".to_string()).fuzzy().limit(2), + Query::new("".to_string()).fuzzy().limit(1), expect![[r#" - dep::Fmt (m) - dep::Fmt (t) - dep::Fmt (v) - dep::fmt (t) + dep::fmt::Display (t) "#]], ); } From 5db0e1a34630b05d19308008d5139b1ff170d69e Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Fri, 30 Jun 2023 23:35:32 +0900 Subject: [PATCH 5/5] Restructure `ImportMap` collection --- crates/hir-def/src/import_map.rs | 97 ++++++++++++++++---------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 63caaa8f831..d742b2eef0a 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -53,13 +53,12 @@ impl ImportMap { pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("import_map_query"); - let mut import_map = collect_import_map(db, krate); + let map = collect_import_map(db, krate); - let mut importables: Vec<_> = import_map - .map + let mut importables: Vec<_> = map .iter() // We've only collected items, whose name cannot be tuple field. - .map(|(item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + .map(|(&item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) .collect(); importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); @@ -70,61 +69,30 @@ impl ImportMap { let _ = builder.insert(name, start_idx as u64); } - import_map.fst = builder.into_map(); - import_map.importables = importables.into_iter().map(|(&item, _)| item).collect(); - - Arc::new(import_map) + Arc::new(ImportMap { + map, + fst: builder.into_map(), + importables: importables.into_iter().map(|(item, _)| item).collect(), + }) } pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { self.map.get(&item) } - - fn collect_trait_assoc_items( - &mut self, - db: &dyn DefDatabase, - tr: TraitId, - is_type_in_ns: bool, - trait_import_info: &ImportInfo, - ) { - let _p = profile::span("collect_trait_assoc_items"); - for (assoc_item_name, item) in &db.trait_data(tr).items { - let module_def_id = match item { - AssocItemId::FunctionId(f) => ModuleDefId::from(*f), - AssocItemId::ConstId(c) => ModuleDefId::from(*c), - // cannot use associated type aliases directly: need a `::TypeAlias` - // qualifier, ergo no need to store it for imports in import_map - AssocItemId::TypeAliasId(_) => { - cov_mark::hit!(type_aliases_ignored); - continue; - } - }; - let assoc_item = if is_type_in_ns { - ItemInNs::Types(module_def_id) - } else { - ItemInNs::Values(module_def_id) - }; - - let assoc_item_info = ImportInfo { - container: trait_import_info.container, - name: assoc_item_name.clone(), - is_trait_assoc_item: true, - }; - self.map.insert(assoc_item, assoc_item_info); - } - } } -fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { +fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap { let _p = profile::span("collect_import_map"); let def_map = db.crate_def_map(krate); - let mut import_map = ImportMap::default(); + let mut map = FxIndexMap::default(); // We look only into modules that are public(ly reexported), starting with the crate root. let root = def_map.module_id(DefMap::ROOT); let mut worklist = vec![(root, 0)]; + // Records items' minimum module depth. let mut depth_map = FxHashMap::default(); + while let Some((module, depth)) = worklist.pop() { let ext_def_map; let mod_data = if module.krate == krate { @@ -166,15 +134,16 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { } if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { - import_map.collect_trait_assoc_items( + collect_trait_assoc_items( db, + &mut map, tr, matches!(item, ItemInNs::Types(_)), &import_info, ); } - import_map.map.insert(item, import_info); + map.insert(item, import_info); // If we've just added a module, descend into it. We might traverse modules // multiple times, but only if the module depth is smaller (else we `continue` @@ -186,7 +155,41 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { } } - import_map + map +} + +fn collect_trait_assoc_items( + db: &dyn DefDatabase, + map: &mut FxIndexMap, + tr: TraitId, + is_type_in_ns: bool, + trait_import_info: &ImportInfo, +) { + let _p = profile::span("collect_trait_assoc_items"); + for (assoc_item_name, item) in &db.trait_data(tr).items { + let module_def_id = match item { + AssocItemId::FunctionId(f) => ModuleDefId::from(*f), + AssocItemId::ConstId(c) => ModuleDefId::from(*c), + // cannot use associated type aliases directly: need a `::TypeAlias` + // qualifier, ergo no need to store it for imports in import_map + AssocItemId::TypeAliasId(_) => { + cov_mark::hit!(type_aliases_ignored); + continue; + } + }; + let assoc_item = if is_type_in_ns { + ItemInNs::Types(module_def_id) + } else { + ItemInNs::Values(module_def_id) + }; + + let assoc_item_info = ImportInfo { + container: trait_import_info.container, + name: assoc_item_name.clone(), + is_trait_assoc_item: true, + }; + map.insert(assoc_item, assoc_item_info); + } } impl PartialEq for ImportMap {