diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index b9c5ff72792a..1ebd1ba0e66d 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -21,9 +21,10 @@ pub fn find_path( item: ItemInNs, from: ModuleId, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path"); - find_path_inner(db, item, from, None, prefer_no_std) + find_path_inner(db, item, from, None, prefer_no_std, prefer_prelude) } pub fn find_path_prefixed( @@ -32,9 +33,10 @@ pub fn find_path_prefixed( from: ModuleId, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path_prefixed"); - find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std) + find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std, prefer_prelude) } #[derive(Copy, Clone, Debug)] @@ -88,6 +90,7 @@ fn find_path_inner( from: ModuleId, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { // - if the item is a builtin, it's in scope if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { @@ -109,6 +112,7 @@ fn find_path_inner( MAX_PATH_LEN, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, ) .map(|(item, _)| item); } @@ -134,6 +138,7 @@ fn find_path_inner( from, prefixed, prefer_no_std, + prefer_prelude, ) { let data = db.enum_data(variant.parent); path.push_segment(data.variants[variant.local_id].name.clone()); @@ -156,6 +161,7 @@ fn find_path_inner( from, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, scope_name, ) .map(|(item, _)| item) @@ -171,6 +177,7 @@ fn find_path_for_module( max_len: usize, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; @@ -236,6 +243,7 @@ fn find_path_for_module( from, prefixed, prefer_no_std, + prefer_prelude, scope_name, ) } @@ -316,6 +324,7 @@ fn calculate_best_path( from: ModuleId, mut prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, scope_name: Option<Name>, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { @@ -351,11 +360,14 @@ fn calculate_best_path( best_path_len - 1, prefixed, prefer_no_std, + prefer_prelude, ) { path.0.push_segment(name); let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), + Some(best_path) => { + select_best_path(best_path, path, prefer_no_std, prefer_prelude) + } None => path, }; best_path_len = new_path.0.len(); @@ -367,18 +379,18 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - let crate_graph = db.crate_graph(); - let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { + for dep in &db.crate_graph()[from.krate].dependencies { let import_map = db.import_map(dep.crate_id); - import_map.import_info_for(item).and_then(|info| { + let Some(import_info_for) = import_map.import_info_for(item) else { continue }; + for info in import_info_for { if info.is_doc_hidden { // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate - return None; + continue; } // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let (mut path, path_stability) = find_path_for_module( + let Some((mut path, path_stability)) = find_path_for_module( db, def_map, visited_modules, @@ -388,22 +400,26 @@ fn calculate_best_path( max_len - 1, prefixed, prefer_no_std, - )?; + prefer_prelude, + ) else { + continue; + }; cov_mark::hit!(partially_imported); path.push_segment(info.name.clone()); - Some(( + + let path_with_stab = ( path, zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }), - )) - }) - }); + ); - for path in extern_paths { - let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), - None => path, - }; - update_best_path(&mut best_path, new_path); + let new_path_with_stab = match best_path.take() { + Some(best_path) => { + select_best_path(best_path, path_with_stab, prefer_no_std, prefer_prelude) + } + None => path_with_stab, + }; + update_best_path(&mut best_path, new_path_with_stab); + } } } if let Some(module) = item.module(db) { @@ -420,17 +436,39 @@ fn calculate_best_path( } } +/// Select the best (most relevant) path between two paths. +/// This accounts for stability, path length whether std should be chosen over alloc/core paths as +/// well as ignoring prelude like paths or not. fn select_best_path( - old_path: (ModPath, Stability), - new_path: (ModPath, Stability), + old_path @ (_, old_stability): (ModPath, Stability), + new_path @ (_, new_stability): (ModPath, Stability), prefer_no_std: bool, + prefer_prelude: bool, ) -> (ModPath, Stability) { - match (old_path.1, new_path.1) { + match (old_stability, new_stability) { (Stable, Unstable) => return old_path, (Unstable, Stable) => return new_path, _ => {} } const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; + + let choose = |new_path: (ModPath, _), old_path: (ModPath, _)| { + let new_has_prelude = new_path.0.segments().iter().any(|seg| seg == &known::prelude); + let old_has_prelude = old_path.0.segments().iter().any(|seg| seg == &known::prelude); + match (new_has_prelude, old_has_prelude, prefer_prelude) { + (true, false, true) | (false, true, false) => new_path, + (true, false, false) | (false, true, true) => old_path, + // no prelude difference in the paths, so pick the smaller one + (true, true, _) | (false, false, _) => { + if new_path.0.len() < old_path.0.len() { + new_path + } else { + old_path + } + } + } + }; + match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { let rank = match prefer_no_std { @@ -451,23 +489,11 @@ fn select_best_path( let orank = rank(old); match nrank.cmp(&orank) { Ordering::Less => old_path, - Ordering::Equal => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + Ordering::Equal => choose(new_path, old_path), Ordering::Greater => new_path, } } - _ => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + _ => choose(new_path, old_path), } } @@ -570,7 +596,13 @@ mod tests { /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the /// module the cursor is in. - fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) { + #[track_caller] + fn check_found_path_( + ra_fixture: &str, + path: &str, + prefix_kind: Option<PrefixKind>, + prefer_prelude: bool, + ) { let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_at_position(pos); let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};")); @@ -589,11 +621,17 @@ mod tests { ) .0 .take_types() - .unwrap(); - - let found_path = - find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false); - assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}"); + .expect("path does not resolve to a type"); + + let found_path = find_path_inner( + &db, + ItemInNs::Types(resolved), + module, + prefix_kind, + false, + prefer_prelude, + ); + assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); } fn check_found_path( @@ -603,10 +641,23 @@ mod tests { absolute: &str, self_prefixed: &str, ) { - check_found_path_(ra_fixture, unprefixed, None); - check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain)); - check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate)); - check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf)); + check_found_path_(ra_fixture, unprefixed, None, false); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), false); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), false); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), false); + } + + fn check_found_path_prelude( + ra_fixture: &str, + unprefixed: &str, + prefixed: &str, + absolute: &str, + self_prefixed: &str, + ) { + check_found_path_(ra_fixture, unprefixed, None, true); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), true); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), true); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), true); } #[test] @@ -1421,4 +1472,34 @@ pub mod error { "std::error::Error", ); } + + #[test] + fn respects_prelude_setting() { + let ra_fixture = r#" +//- /main.rs crate:main deps:krate +$0 +//- /krate.rs crate:krate +pub mod prelude { + pub use crate::foo::*; +} + +pub mod foo { + pub struct Foo; +} +"#; + check_found_path( + ra_fixture, + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + ); + check_found_path_prelude( + ra_fixture, + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + ); + } } diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 6461439bb713..649ea13888dd 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, fmt, hash::BuildHasherDefault}; +use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; -use fst::{self, Streamer}; +use fst::{self, raw::IndexedValue, Streamer}; use hir_expand::name::Name; use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use rustc_hash::{FxHashSet, FxHasher}; +use smallvec::SmallVec; use triomphe::Arc; use crate::{ @@ -20,31 +21,28 @@ use crate::{ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; -// 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)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct ImportInfo { /// 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, /// Whether this item is annotated with `#[doc(hidden)]`. pub is_doc_hidden: bool, /// Whether this item is annotated with `#[unstable(..)]`. pub is_unstable: bool, } +type ImportMapIndex = FxIndexMap<ItemInNs, (SmallVec<[ImportInfo; 1]>, IsTraitAssocItem)>; + /// 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. #[derive(Default)] pub struct ImportMap { - map: FxIndexMap<ItemInNs, ImportInfo>, - + map: ImportMapIndex, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// @@ -55,6 +53,12 @@ pub struct ImportMap { fst: fst::Map<Vec<u8>>, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum IsTraitAssocItem { + Yes, + No, +} + impl ImportMap { pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { let _p = profile::span("import_map_query"); @@ -64,13 +68,18 @@ impl ImportMap { 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())) + .flat_map(|(&item, (info, _))| { + info.iter() + .map(move |info| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + }) .collect(); importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); + importables.dedup(); // Build the FST, taking care not to insert duplicate values. let mut builder = fst::MapBuilder::memory(); - let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1); + let iter = + importables.iter().enumerate().dedup_by(|(_, (_, lhs)), (_, (_, rhs))| lhs == rhs); for (start_idx, (_, name)) in iter { let _ = builder.insert(name, start_idx as u64); } @@ -82,12 +91,12 @@ impl ImportMap { }) } - pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { - self.map.get(&item) + pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { + self.map.get(&item).map(|(info, _)| &**info) } } -fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemInNs, ImportInfo> { +fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex { let _p = profile::span("collect_import_map"); let def_map = db.crate_def_map(krate); @@ -95,11 +104,13 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn // 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, 0u32)]; - // Records items' minimum module depth. - let mut depth_map = FxHashMap::default(); + let mut worklist = vec![root]; + let mut visited = FxHashSet::default(); - while let Some((module, depth)) = worklist.pop() { + while let Some(module) = worklist.pop() { + if !visited.insert(module) { + continue; + } let ext_def_map; let mod_data = if module.krate == krate { &def_map[module.local_id] @@ -131,62 +142,18 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn ItemInNs::Macros(id) => Some(id.into()), } }; - let status @ (is_doc_hidden, is_unstable) = - attr_id.map_or((false, false), |attr_id| { - let attrs = db.attrs(attr_id); - (attrs.has_doc_hidden(), attrs.is_unstable()) - }); + let (is_doc_hidden, is_unstable) = attr_id.map_or((false, false), |attr_id| { + let attrs = db.attrs(attr_id); + (attrs.has_doc_hidden(), attrs.is_unstable()) + }); let import_info = ImportInfo { name: name.clone(), container: module, - is_trait_assoc_item: false, is_doc_hidden, is_unstable, }; - match depth_map.entry(item) { - Entry::Vacant(entry) => _ = entry.insert((depth, status)), - Entry::Occupied(mut entry) => { - let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get(); - (depth, occ_depth); - let overwrite = match ( - is_doc_hidden, - occ_is_doc_hidden, - is_unstable, - occ_is_unstable, - ) { - // no change of hiddeness or unstableness - (true, true, true, true) - | (true, true, false, false) - | (false, false, true, true) - | (false, false, false, false) => depth < occ_depth, - - // either less hidden or less unstable, accept - (true, true, false, true) - | (false, true, true, true) - | (false, true, false, true) - | (false, true, false, false) - | (false, false, false, true) => true, - // more hidden or unstable, discard - (true, true, true, false) - | (true, false, true, true) - | (true, false, true, false) - | (true, false, false, false) - | (false, false, true, false) => false, - - // exchanges doc(hidden) for unstable (and vice-versa), - (true, false, false, true) | (false, true, true, false) => { - depth < occ_depth - } - }; - if !overwrite { - continue; - } - entry.insert((depth, status)); - } - } - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { collect_trait_assoc_items( db, @@ -197,13 +164,14 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn ); } - map.insert(item, import_info); + let (infos, _) = + map.entry(item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(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` - // above). + // If we've just added a module, descend into it. if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { - worklist.push((mod_id, depth + 1)); + worklist.push(mod_id); } } } @@ -214,7 +182,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn fn collect_trait_assoc_items( db: &dyn DefDatabase, - map: &mut FxIndexMap<ItemInNs, ImportInfo>, + map: &mut ImportMapIndex, tr: TraitId, is_type_in_ns: bool, trait_import_info: &ImportInfo, @@ -241,11 +209,14 @@ fn collect_trait_assoc_items( let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), - is_trait_assoc_item: true, is_doc_hidden: attrs.has_doc_hidden(), is_unstable: attrs.is_unstable(), }; - map.insert(assoc_item, assoc_item_info); + + let (infos, _) = + map.entry(assoc_item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::Yes)); + infos.reserve_exact(1); + infos.push(assoc_item_info); } } @@ -263,10 +234,13 @@ impl fmt::Debug for ImportMap { let mut importable_names: Vec<_> = self .map .iter() - .map(|(item, _)| match item { - ItemInNs::Types(it) => format!("- {it:?} (t)",), - ItemInNs::Values(it) => format!("- {it:?} (v)",), - ItemInNs::Macros(it) => format!("- {it:?} (m)",), + .map(|(item, (infos, _))| { + let l = infos.len(); + match item { + ItemInNs::Types(it) => format!("- {it:?} (t) [{l}]",), + ItemInNs::Values(it) => format!("- {it:?} (v) [{l}]",), + ItemInNs::Macros(it) => format!("- {it:?} (m) [{l}]",), + } }) .collect(); @@ -276,7 +250,7 @@ impl fmt::Debug for ImportMap { } /// A way to match import map contents against the search query. -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] enum SearchMode { /// Import map entry should strictly match the query string. Exact, @@ -349,6 +323,15 @@ impl Query { Self { case_sensitive: true, ..self } } + fn matches_assoc_mode(&self, is_trait_assoc_item: IsTraitAssocItem) -> bool { + match (is_trait_assoc_item, self.assoc_mode) { + (IsTraitAssocItem::Yes, AssocSearchMode::Exclude) + | (IsTraitAssocItem::No, AssocSearchMode::AssocItemsOnly) => false, + _ => true, + } + } + + /// Checks whether the import map entry matches the query. fn import_matches( &self, db: &dyn DefDatabase, @@ -356,12 +339,8 @@ impl Query { enforce_lowercase: bool, ) -> bool { let _p = profile::span("import_map::Query::import_matches"); - match (import.is_trait_assoc_item, self.assoc_mode) { - (true, AssocSearchMode::Exclude) => return false, - (false, AssocSearchMode::AssocItemsOnly) => return false, - _ => {} - } + // FIXME: Can we get rid of the alloc here? let mut input = import.name.display(db.upcast()).to_string(); let case_insensitive = enforce_lowercase || !self.case_sensitive; if case_insensitive { @@ -392,7 +371,7 @@ impl Query { pub fn search_dependencies( db: &dyn DefDatabase, krate: CrateId, - query: Query, + ref query: Query, ) -> FxHashSet<ItemInNs> { let _p = profile::span("search_dependencies").detail(|| format!("{query:?}")); @@ -410,31 +389,58 @@ pub fn search_dependencies( let mut stream = op.union(); let mut res = FxHashSet::default(); + let mut common_importable_data_scratch = vec![]; 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..]; + for &IndexedValue { index, value } in indexed_values { + let import_map = &import_maps[index]; + let importables @ [importable, ..] = &import_map.importables[value as usize..] else { + continue; + }; - let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(db, common_importable_data, true) { + let &(ref importable_data, is_trait_assoc_item) = &import_map.map[importable]; + if !query.matches_assoc_mode(is_trait_assoc_item) { continue; } - // 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) + common_importable_data_scratch.extend( + importable_data + .iter() + .filter(|&info| query.import_matches(db, info, true)) + // Name shared by the importable items in this group. + .map(|info| info.name.to_smol_str()), + ); + if common_importable_data_scratch.is_empty() { + continue; + } + common_importable_data_scratch.sort(); + common_importable_data_scratch.dedup(); + + let iter = + common_importable_data_scratch.drain(..).flat_map(|common_importable_name| { + // Add the items from this name group. Those are all subsequent items in + // `importables` whose name match `common_importable_name`. + importables + .iter() + .copied() + .take_while(move |item| { + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos.iter().any(|info| { + info.name + .to_smol_str() + .eq_ignore_ascii_case(&common_importable_name) + }) + }) + .filter(move |item| { + !query.case_sensitive || { + // we've already checked the common importables name case-insensitively + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos + .iter() + .any(|info| query.import_matches(db, info, false)) + } + }) }); res.extend(iter); @@ -461,6 +467,7 @@ mod tests { let mut importable_paths: Vec<_> = self .map .iter() + .flat_map(|(item, (info, _))| info.iter().map(move |info| (item, info))) .map(|(item, info)| { let path = render_path(db, info); let ns = match item { @@ -499,7 +506,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - render_path(&db, dependency_imports.import_info_for(dependency)?), + render_path(&db, &dependency_imports.import_info_for(dependency)?[0]), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -547,7 +554,12 @@ mod tests { .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()))) + // FIXME: This should check all import infos, not just the first + Some(format!( + "{}::{}", + render_path(db, &trait_info[0]), + assoc_item_name.display(db.upcast()) + )) } fn check(ra_fixture: &str, expect: Expect) { @@ -623,6 +635,7 @@ mod tests { main: - publ1 (t) - real_pu2 (t) + - real_pu2::Pub (t) - real_pub (t) - real_pub::Pub (t) "#]], @@ -648,6 +661,7 @@ mod tests { - sub (t) - sub::Def (t) - sub::subsub (t) + - sub::subsub::Def (t) "#]], ); } @@ -747,7 +761,9 @@ mod tests { - module (t) - module::S (t) - module::S (v) + - module::module (t) - sub (t) + - sub::module (t) "#]], ); } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 495e2d476970..fd8f64d6705b 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -152,7 +152,7 @@ impl TryFrom<ModuleId> for CrateRootModuleId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ModuleId { krate: CrateId, /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index f6d6b00d740a..9ccf467358ec 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -945,6 +945,7 @@ impl HirDisplay for Ty { ItemInNs::Types((*def_id).into()), module_id, false, + true, ) { write!(f, "{}", path.display(f.db.upcast()))?; } else { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 17ffb9acbd19..6fcc02fb9dac 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -664,8 +664,15 @@ impl Module { db: &dyn DefDatabase, item: impl Into<ItemInNs>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { - hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std) + hir_def::find_path::find_path( + db, + item.into().into(), + self.into(), + prefer_no_std, + prefer_prelude, + ) } /// Finds a path that can be used to refer to the given item from within @@ -676,6 +683,7 @@ impl Module { item: impl Into<ItemInNs>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { hir_def::find_path::find_path_prefixed( db, @@ -683,6 +691,7 @@ impl Module { self.into(), prefix_kind, prefer_no_std, + prefer_prelude, ) } } diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index b273ebc85a50..fbe17dbfd771 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -14,5 +14,6 @@ pub struct AssistConfig { pub allowed: Option<Vec<AssistKind>>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub assist_emit_must_use: bool, } diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index c8b78b094169..2374da9a343f 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -88,7 +88,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)?, + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -140,7 +146,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) @@ -173,7 +185,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant.clone(), ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant.clone(), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) @@ -440,11 +458,16 @@ fn build_pat( module: hir::Module, var: ExtendedVariant, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ast::Pat> { match var { ExtendedVariant::Variant(var) => { - let path = - mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var), prefer_no_std)?); + let path = mod_path_to_ast(&module.find_use_path( + db, + ModuleDef::from(var), + prefer_no_std, + prefer_prelude, + )?); // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though Some(match var.source(db)?.value.kind() { diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index cafd57a97727..f508c42c53e4 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -93,6 +93,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< &ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_no_std, ); if proposed_imports.is_empty() { return None; diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index 082839118c58..11facc5bee2a 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -348,6 +348,7 @@ fn augment_references_with_imports( ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .map(|mod_path| { make::path_concat(mod_path_to_ast(&mod_path), make::path_from_text("Bool")) diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index 872b52c98fff..d649f13d6ee3 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -50,7 +50,12 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - _ => return None, }; - mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?) + mod_path_to_ast(&module.find_use_path( + ctx.db(), + src_type_def, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?) }; let dest_type = match &ast_trait { diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 32db5ee8dabf..1f3caa7db33f 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -205,6 +205,7 @@ fn augment_references_with_imports( ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .map(|mod_path| { make::path_concat( diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index de591cfde949..6b48d1588152 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -163,6 +163,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mod_path) = mod_path { diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index e4f64ccc754e..37db27a8fc02 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -384,6 +384,7 @@ fn process_references( *enum_module_def, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs index 81545396175b..473c699b595c 100644 --- a/crates/ide-assists/src/handlers/generate_deref.rs +++ b/crates/ide-assists/src/handlers/generate_deref.rs @@ -58,8 +58,12 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let field_name = field.name()?; @@ -99,8 +103,12 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let target = field.syntax().text_range(); diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs index 824255e4f8e3..7bfd599660db 100644 --- a/crates/ide-assists/src/handlers/generate_new.rs +++ b/crates/ide-assists/src/handlers/generate_new.rs @@ -67,6 +67,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let expr = use_trivial_constructor( diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index 4bf974a5655a..ff65aac82e07 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -48,6 +48,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index 239149dc411d..fde46db3058e 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -37,8 +37,11 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; - let mut proposed_imports = - import_assets.search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std); + let mut proposed_imports = import_assets.search_for_relative_paths( + &ctx.sema, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ); if proposed_imports.is_empty() { return None; } 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 ac45581b7b44..69a4e748b7c5 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 @@ -82,7 +82,12 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std) + .find_use_path( + ctx.sema.db, + hir::ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .as_ref() .map(mod_path_to_ast) .zip(Some(trait_)) diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index dbbc56958f1e..f03eb6118a52 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -68,6 +68,7 @@ pub(crate) fn replace_qualified_name_with_use( module, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) }) .flatten(); diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index cc3e251a8f2a..566384615bb3 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; @@ -44,6 +45,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index f60ac150164c..7d38c638a8ed 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -626,6 +626,7 @@ fn enum_variants_with_paths( ctx.db, hir::ModuleDef::from(variant), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 9daa6984c3e3..d3c817d4b43a 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -175,6 +175,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(strukt), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); @@ -197,6 +198,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(un), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 0961021e48e0..d74d3b264ae5 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,12 @@ fn import_on_the_fly( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -299,7 +304,12 @@ fn import_on_the_fly_pat_( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -336,7 +346,12 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(|import| { !ctx.is_item_hidden(&import.item_to_import) diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 3d025f284bbb..ed5ddde8fbfe 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub snippet_cap: Option<SnippetCap>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub snippets: Vec<Snippet>, pub limit: Option<usize>, } diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 2fad293d16d7..aaf7cd7843af 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -263,6 +263,7 @@ pub fn resolve_completion_edits( candidate, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) }) .find(|mod_path| mod_path.display(db).to_string() == full_import_path); diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 343719c53694..50618296ee43 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -179,6 +179,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<V item, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item, None))) }; diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 284bdd8af21f..9db8e972dd4b 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -68,6 +68,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), prefer_no_std: false, + prefer_prelude: true, insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, prefix_kind: PrefixKind::Plain, diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index da5a951f0b7b..04263d15d0a5 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -220,9 +220,10 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for_imports"); - self.search_for(sema, Some(prefix_kind), prefer_no_std) + self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) } /// This may return non-absolute paths if a part of the returned path is already imported into scope. @@ -230,9 +231,10 @@ impl ImportAssets { &self, sema: &Semantics<'_, RootDatabase>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for_relative_paths"); - self.search_for(sema, None, prefer_no_std) + self.search_for(sema, None, prefer_no_std, prefer_prelude) } /// Requires imports to by prefix instead of fuzzily. @@ -270,6 +272,7 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for"); @@ -281,6 +284,7 @@ impl ImportAssets { &self.module_with_candidate, prefixed, prefer_no_std, + prefer_prelude, ) }; @@ -594,11 +598,18 @@ fn get_mod_path( module_with_candidate: &Module, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { if let Some(prefix_kind) = prefixed { - module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind, prefer_no_std) + module_with_candidate.find_use_path_prefixed( + db, + item_to_search, + prefix_kind, + prefer_no_std, + prefer_prelude, + ) } else { - module_with_candidate.find_use_path(db, item_to_search, prefer_no_std) + module_with_candidate.find_use_path(db, item_to_search, prefer_no_std, prefer_prelude) } } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index fb75b5b45848..fa9339f30f20 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -277,6 +277,7 @@ impl Ctx<'_> { self.source_scope.db.upcast(), hir::ModuleDef::Trait(trait_ref), false, + true, )?; match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), @@ -311,8 +312,12 @@ impl Ctx<'_> { } } - let found_path = - self.target_module.find_use_path(self.source_scope.db.upcast(), def, false)?; + let found_path = self.target_module.find_use_path( + self.source_scope.db.upcast(), + def, + false, + true, + )?; let res = mod_path_to_ast(&found_path).clone_for_update(); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { if let Some(segment) = res.segment() { diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index a337e2660d6a..659b74445f8f 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -136,6 +136,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } @@ -148,6 +149,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index c09be3fee7e1..d7dca1083a07 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -122,6 +122,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; use_trivial_constructor( diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index fe5567544e83..d8e37a848ac0 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -225,6 +225,7 @@ pub struct DiagnosticsConfig { // FIXME: We may want to include a whole `AssistConfig` here pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, } impl DiagnosticsConfig { @@ -247,6 +248,7 @@ impl DiagnosticsConfig { skip_glob_imports: false, }, prefer_no_std: false, + prefer_prelude: true, } } } diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index 60fcbbbd3979..0312a0f11ebd 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -651,7 +651,7 @@ impl Match { for (path, resolved_path) in &template.resolved_paths { if let hir::PathResolution::Def(module_def) = resolved_path.resolution { let mod_path = - module.find_use_path(sema.db, module_def, false).ok_or_else(|| { + module.find_use_path(sema.db, module_def, false, true).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") })?; self.rendered_template_paths.insert(path.clone(), mod_path); diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 230ff5f9b86a..0f6539f224d7 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -762,7 +762,8 @@ impl flags::AnalysisStats { group: true, skip_glob_imports: true, }, - prefer_no_std: Default::default(), + prefer_no_std: false, + prefer_prelude: true, }, ide::AssistResolveStrategy::All, file_id, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c8df4255d96b..6b8de861db95 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -353,6 +353,8 @@ config_data! { imports_merge_glob: bool = "true", /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. imports_prefer_no_std: bool = "false", + /// Whether to prefer import paths containing a `prelude` module. + imports_prefer_prelude: bool = "false", /// The path structure for newly inserted paths to use. imports_prefix: ImportPrefixDef = "\"plain\"", @@ -1118,6 +1120,7 @@ impl Config { }, insert_use: self.insert_use_config(), prefer_no_std: self.data.imports_prefer_no_std, + prefer_prelude: self.data.imports_prefer_prelude, } } @@ -1487,6 +1490,7 @@ impl Config { }, insert_use: self.insert_use_config(), prefer_no_std: self.data.imports_prefer_no_std, + prefer_prelude: self.data.imports_prefer_prelude, snippet_cap: SnippetCap::new(try_or_def!( self.caps .text_document @@ -1516,6 +1520,7 @@ impl Config { allowed: None, insert_use: self.insert_use_config(), prefer_no_std: self.data.imports_prefer_no_std, + prefer_prelude: self.data.imports_prefer_prelude, assist_emit_must_use: self.data.assist_emitMustUse, } } diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 2c402552919f..b7e839ac2a5e 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -146,6 +146,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = @@ -186,6 +187,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 7c76ae81bea0..7460df49fc00 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -498,6 +498,11 @@ Whether to allow import insertion to merge new imports into single path glob imp -- Prefer to unconditionally use imports of the core and alloc crate, over the std crate. -- +[[rust-analyzer.imports.prefer.prelude]]rust-analyzer.imports.prefer.prelude (default: `false`):: ++ +-- +Whether to prefer import paths containing a `prelude` module. +-- [[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index c7b877b2897c..ef9dec5f1470 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1134,6 +1134,11 @@ "default": false, "type": "boolean" }, + "rust-analyzer.imports.prefer.prelude": { + "markdownDescription": "Whether to prefer import paths containing a `prelude` module.", + "default": false, + "type": "boolean" + }, "rust-analyzer.imports.prefix": { "markdownDescription": "The path structure for newly inserted paths to use.", "default": "plain",