diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index b251564016c7..e1fe13f9af05 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -12,7 +12,7 @@ use crate::{ ConstId, ExternCrateId, FunctionId, HasModule, ImplId, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ProcMacroId, StaticId, TraitAliasId, TraitId, TypeAliasId, db::DefDatabase, - item_tree::{self, FnFlags, ModItem}, + item_tree::{self, FnFlags, ModItem, StaticFlags}, nameres::proc_macro::{ProcMacroKind, parse_macro_name_and_helper_attrs}, path::ImportAlias, type_ref::{TraitRef, TypeBound, TypeRefId, TypesMap}, @@ -27,9 +27,8 @@ pub struct FunctionData { pub visibility: RawVisibility, pub abi: Option, pub legacy_const_generics_indices: Option>>, - pub rustc_allow_incoherent_impl: bool, pub types_map: Arc, - flags: FnFlags, + pub flags: FnFlags, } impl FunctionData { @@ -72,7 +71,9 @@ impl FunctionData { } let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); - let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); + if attrs.by_key(&sym::rustc_allow_incoherent_impl).exists() { + flags |= FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL; + } if flags.contains(FnFlags::HAS_UNSAFE_KW) && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists() { @@ -101,51 +102,65 @@ impl FunctionData { legacy_const_generics_indices: attrs.rustc_legacy_const_generics(), types_map: func.types_map.clone(), flags, - rustc_allow_incoherent_impl, }) } + #[inline] pub fn has_body(&self) -> bool { self.flags.contains(FnFlags::HAS_BODY) } /// True if the first param is `self`. This is relevant to decide whether this /// can be called as a method. + #[inline] pub fn has_self_param(&self) -> bool { self.flags.contains(FnFlags::HAS_SELF_PARAM) } + #[inline] pub fn is_default(&self) -> bool { self.flags.contains(FnFlags::HAS_DEFAULT_KW) } + #[inline] pub fn is_const(&self) -> bool { self.flags.contains(FnFlags::HAS_CONST_KW) } + #[inline] pub fn is_async(&self) -> bool { self.flags.contains(FnFlags::HAS_ASYNC_KW) } + #[inline] pub fn is_unsafe(&self) -> bool { self.flags.contains(FnFlags::HAS_UNSAFE_KW) } + #[inline] pub fn is_deprecated_safe_2024(&self) -> bool { self.flags.contains(FnFlags::DEPRECATED_SAFE_2024) } + #[inline] pub fn is_safe(&self) -> bool { self.flags.contains(FnFlags::HAS_SAFE_KW) } + #[inline] pub fn is_varargs(&self) -> bool { self.flags.contains(FnFlags::IS_VARARGS) } + #[inline] pub fn has_target_feature(&self) -> bool { self.flags.contains(FnFlags::HAS_TARGET_FEATURE) } + + #[inline] + pub fn rustc_allow_incoherent_impl(&self) -> bool { + self.flags.contains(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL) + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -153,15 +168,37 @@ pub struct TypeAliasData { pub name: Name, pub type_ref: Option, pub visibility: RawVisibility, - pub is_extern: bool, - pub rustc_has_incoherent_inherent_impls: bool, - pub rustc_allow_incoherent_impl: bool, + pub flags: TypeAliasFlags, /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). pub bounds: Box<[TypeBound]>, pub types_map: Arc, } +bitflags::bitflags! { + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct TypeAliasFlags: u8 { + const IS_EXTERN = 1 << 0; + const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 2; + } +} + impl TypeAliasData { + #[inline] + pub fn is_extern(&self) -> bool { + self.flags.contains(TypeAliasFlags::IS_EXTERN) + } + + #[inline] + pub fn rustc_has_incoherent_inherent_impls(&self) -> bool { + self.flags.contains(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) + } + + #[inline] + pub fn rustc_allow_incoherent_impl(&self) -> bool { + self.flags.contains(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL) + } + pub(crate) fn type_alias_data_query( db: &dyn DefDatabase, typ: TypeAliasId, @@ -180,17 +217,24 @@ impl TypeAliasData { loc.container.module(db).krate(), ModItem::from(loc.id.value).into(), ); - let rustc_has_incoherent_inherent_impls = - attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); - let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); + + let mut flags = TypeAliasFlags::empty(); + + if matches!(loc.container, ItemContainerId::ExternBlockId(_)) { + flags |= TypeAliasFlags::IS_EXTERN; + } + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { + flags |= TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; + } + if attrs.by_key(&sym::rustc_allow_incoherent_impl).exists() { + flags |= TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL; + } Arc::new(TypeAliasData { name: typ.name.clone(), type_ref: typ.type_ref, visibility, - is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), - rustc_has_incoherent_inherent_impls, - rustc_allow_incoherent_impl, + flags, bounds: typ.bounds.clone(), types_map: typ.types_map.clone(), }) @@ -199,7 +243,7 @@ impl TypeAliasData { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub struct TraitFlags: u8 { + pub struct TraitFlags: u16 { const IS_AUTO = 1 << 0; const IS_UNSAFE = 1 << 1; const IS_FUNDAMENTAL = 1 << 2; @@ -332,9 +376,9 @@ impl Macro2Data { let loc = makro.lookup(db); let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; + let attrs = item_tree.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()); - let helpers = item_tree - .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) + let helpers = attrs .by_key(&sym::rustc_builtin_macro) .tt_values() .next() @@ -362,11 +406,9 @@ impl MacroRulesData { let loc = makro.lookup(db); let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; + let attrs = item_tree.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()); - let macro_export = item_tree - .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .by_key(&sym::macro_export) - .exists(); + let macro_export = attrs.by_key(&sym::macro_export).exists(); Arc::new(MacroRulesData { name: makro.name.clone(), macro_export }) } @@ -387,11 +429,9 @@ impl ProcMacroData { let loc = makro.lookup(db); let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; + let attrs = item_tree.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()); - let (name, helpers) = if let Some(def) = item_tree - .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .parse_proc_macro_decl(&makro.name) - { + let (name, helpers) = if let Some(def) = attrs.parse_proc_macro_decl(&makro.name) { ( def.name, match def.kind { @@ -404,6 +444,7 @@ impl ProcMacroData { stdx::never!("proc macro declaration is not a proc macro"); (makro.name.clone(), None) }; + Arc::new(ProcMacroData { name, helpers }) } } @@ -450,9 +491,16 @@ pub struct ConstData { pub name: Option, pub type_ref: TypeRefId, pub visibility: RawVisibility, - pub rustc_allow_incoherent_impl: bool, - pub has_body: bool, pub types_map: Arc, + pub flags: ConstFlags, +} + +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct ConstFlags: u8 { + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 0; + const HAS_BODY = 1 << 1; + } } impl ConstData { @@ -465,21 +513,38 @@ impl ConstData { } else { item_tree[konst.visibility].clone() }; + let attrs = item_tree.attrs( + db, + loc.container.module(db).krate(), + ModItem::from(loc.id.value).into(), + ); - let rustc_allow_incoherent_impl = item_tree - .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into()) - .by_key(&sym::rustc_allow_incoherent_impl) - .exists(); + let mut flags = ConstFlags::empty(); + if attrs.by_key(&sym::rustc_allow_incoherent_impl).exists() { + flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL; + } + if konst.has_body { + flags |= ConstFlags::HAS_BODY; + } Arc::new(ConstData { name: konst.name.clone(), type_ref: konst.type_ref, visibility, - rustc_allow_incoherent_impl, - has_body: konst.has_body, + flags, types_map: konst.types_map.clone(), }) } + + #[inline] + pub fn rustc_allow_incoherent_impl(&self) -> bool { + self.flags.contains(ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL) + } + + #[inline] + pub fn has_body(&self) -> bool { + self.flags.contains(ConstFlags::HAS_BODY) + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -487,11 +552,8 @@ pub struct StaticData { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibility, - pub mutable: bool, - pub is_extern: bool, - pub has_safe_kw: bool, - pub has_unsafe_kw: bool, pub types_map: Arc, + pub flags: StaticFlags, } impl StaticData { @@ -500,17 +562,39 @@ impl StaticData { let item_tree = loc.id.item_tree(db); let statik = &item_tree[loc.id.value]; + let mut flags = statik.flags; + if matches!(loc.container, ItemContainerId::ExternBlockId(_)) { + flags |= StaticFlags::IS_EXTERN; + } + Arc::new(StaticData { name: statik.name.clone(), type_ref: statik.type_ref, visibility: item_tree[statik.visibility].clone(), - mutable: statik.mutable, - is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), - has_safe_kw: statik.has_safe_kw, - has_unsafe_kw: statik.has_unsafe_kw, + flags, types_map: statik.types_map.clone(), }) } + + #[inline] + pub fn is_extern(&self) -> bool { + self.flags.contains(StaticFlags::IS_EXTERN) + } + + #[inline] + pub fn mutable(&self) -> bool { + self.flags.contains(StaticFlags::MUTABLE) + } + + #[inline] + pub fn has_safe_kw(&self) -> bool { + self.flags.contains(StaticFlags::HAS_SAFE_KW) + } + + #[inline] + pub fn has_unsafe_kw(&self) -> bool { + self.flags.contains(StaticFlags::HAS_UNSAFE_KW) + } } fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility { diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index e470fe689b91..a54d7663ccac 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -226,6 +226,7 @@ impl StructData { let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; } @@ -290,10 +291,10 @@ impl EnumData { let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); - let rustc_has_incoherent_inherent_impls = item_tree - .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) - .by_key(&sym::rustc_has_incoherent_inherent_impls) - .exists(); + let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + + let rustc_has_incoherent_inherent_impls = + attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); let enum_ = &item_tree[loc.id.value]; diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 717566f9d796..b1622eaf8373 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -9,11 +9,11 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; -use stdx::{TupleExt, format_to}; +use stdx::format_to; use triomphe::Arc; use crate::{ - AssocItemId, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, nameres::DefMap, @@ -31,6 +31,8 @@ pub struct ImportInfo { pub is_doc_hidden: bool, /// Whether this item is annotated with `#[unstable(..)]`. pub is_unstable: bool, + /// The value of `#[rust_analyzer::completions(...)]`, if exists. + pub complete: Complete, } /// A map from publicly exported items to its name. @@ -172,16 +174,22 @@ impl ImportMap { ItemInNs::Macros(id) => Some(id.into()), } }; - 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 (is_doc_hidden, is_unstable, do_not_complete) = match attr_id { + None => (false, false, Complete::Yes), + Some(attr_id) => { + let attrs = db.attrs(attr_id); + let do_not_complete = + Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), &attrs); + (attrs.has_doc_hidden(), attrs.is_unstable(), do_not_complete) + } + }; let import_info = ImportInfo { name: name.clone(), container: module, is_doc_hidden, is_unstable, + complete: do_not_complete, }; if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { @@ -235,12 +243,17 @@ impl ImportMap { ItemInNs::Values(module_def_id) }; - let attrs = &db.attrs(item.into()); + let attr_id = item.into(); + let attrs = &db.attrs(attr_id); + let item_do_not_complete = Complete::extract(false, attrs); + let do_not_complete = + Complete::for_trait_item(trait_import_info.complete, item_do_not_complete); let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), is_doc_hidden: attrs.has_doc_hidden(), is_unstable: attrs.is_unstable(), + complete: do_not_complete, }; let (infos, _) = @@ -398,7 +411,7 @@ pub fn search_dependencies( db: &dyn DefDatabase, krate: Crate, query: &Query, -) -> FxHashSet { +) -> FxHashSet<(ItemInNs, Complete)> { let _p = tracing::info_span!("search_dependencies", ?query).entered(); let import_maps: Vec<_> = @@ -439,7 +452,7 @@ fn search_maps( import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, -) -> FxHashSet { +) -> FxHashSet<(ItemInNs, Complete)> { let mut res = FxHashSet::default(); while let Some((_, indexed_values)) = stream.next() { for &IndexedValue { index: import_map_idx, value } in indexed_values { @@ -459,8 +472,9 @@ fn search_maps( }) .filter(|&(_, info)| { query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) - }); - res.extend(iter.map(TupleExt::head)); + }) + .map(|(item, import_info)| (item, import_info.complete)); + res.extend(iter); } } @@ -521,7 +535,7 @@ mod tests { let actual = search_dependencies(db.upcast(), krate, &query) .into_iter() - .filter_map(|dependency| { + .filter_map(|(dependency, _)| { let dependency_krate = dependency.krate(db.upcast())?; let dependency_imports = db.import_map(dependency_krate); diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 47ad020043ce..a299cfbdd19a 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -358,7 +358,7 @@ impl ItemScope { } /// Get a name from current module scope, legacy macros are not included - pub(crate) fn get(&self, name: &Name) -> PerNs { + pub fn get(&self, name: &Name) -> PerNs { PerNs { types: self.types.get(name).copied(), values: self.values.get(name).copied(), diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 80d666a0aba1..1cabb665d062 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -962,7 +962,7 @@ pub struct Param { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub(crate) struct FnFlags: u16 { + pub struct FnFlags: u16 { const HAS_SELF_PARAM = 1 << 0; const HAS_BODY = 1 << 1; const HAS_DEFAULT_KW = 1 << 2; @@ -977,6 +977,7 @@ bitflags::bitflags! { /// it if needed. const HAS_TARGET_FEATURE = 1 << 8; const DEPRECATED_SAFE_2024 = 1 << 9; + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 10; } } @@ -1050,15 +1051,22 @@ pub struct Const { pub struct Static { pub name: Name, pub visibility: RawVisibilityId, - // TODO: use bitflags when we have more flags - pub mutable: bool, - pub has_safe_kw: bool, - pub has_unsafe_kw: bool, + pub flags: StaticFlags, pub type_ref: TypeRefId, pub ast_id: FileAstId, pub types_map: Arc, } +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct StaticFlags: u8 { + const MUTABLE = 1 << 0; + const IS_EXTERN = 1 << 1; + const HAS_SAFE_KW = 1 << 2; + const HAS_UNSAFE_KW = 1 << 3; + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Trait { pub name: Name, diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 5389ba49c577..4a3deec5ffd2 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -29,8 +29,8 @@ use crate::{ GenericModItem, Idx, Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeSourceMaps, ItemTreeSourceMapsBuilder, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, Path, Range, RawAttrs, RawIdx, RawVisibilityId, - Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, - Variant, + Static, StaticFlags, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, + UseTreeKind, Variant, }, lower::LowerCtx, path::AssociatedTypeBinding, @@ -620,22 +620,23 @@ impl<'a> Ctx<'a> { let name = static_.name()?.as_name(); let type_ref = TypeRef::from_ast_opt(&mut body_ctx, static_.ty()); let visibility = self.lower_visibility(static_); - let mutable = static_.mut_token().is_some(); - let has_safe_kw = static_.safe_token().is_some(); - let has_unsafe_kw = static_.unsafe_token().is_some(); + + let mut flags = StaticFlags::empty(); + if static_.mut_token().is_some() { + flags |= StaticFlags::MUTABLE; + } + if static_.safe_token().is_some() { + flags |= StaticFlags::HAS_SAFE_KW; + } + if static_.unsafe_token().is_some() { + flags |= StaticFlags::HAS_UNSAFE_KW; + } + let ast_id = self.source_ast_id_map.ast_id(static_); types_map.shrink_to_fit(); types_source_map.shrink_to_fit(); - let res = Static { - name, - visibility, - mutable, - type_ref, - ast_id, - has_safe_kw, - has_unsafe_kw, - types_map: Arc::new(types_map), - }; + let res = + Static { name, visibility, type_ref, ast_id, flags, types_map: Arc::new(types_map) }; self.source_maps.statics.push(types_source_map); Some(id(self.data().statics.alloc(res))) } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index f44a000ac1ba..b79b8a28b3ed 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -11,8 +11,8 @@ use crate::{ AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent, FieldsShape, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path, RawAttrs, - RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, Union, Use, - UseTree, UseTreeKind, Variant, + RawVisibilityId, Static, StaticFlags, Struct, Trait, TraitAlias, TypeAlias, TypeBound, + Union, Use, UseTree, UseTreeKind, Variant, }, pretty::{print_path, print_type_bounds, print_type_ref}, type_ref::{TypeRefId, TypesMap}, @@ -418,26 +418,18 @@ impl Printer<'_> { wln!(self, " = _;"); } ModItem::Static(it) => { - let Static { - name, - visibility, - mutable, - type_ref, - ast_id, - has_safe_kw, - has_unsafe_kw, - types_map, - } = &self.tree[it]; + let Static { name, visibility, type_ref, ast_id, types_map, flags } = + &self.tree[it]; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); - if *has_safe_kw { + if flags.contains(StaticFlags::HAS_SAFE_KW) { w!(self, "safe "); } - if *has_unsafe_kw { + if flags.contains(StaticFlags::HAS_UNSAFE_KW) { w!(self, "unsafe "); } w!(self, "static "); - if *mutable { + if flags.contains(StaticFlags::MUTABLE) { w!(self, "mut "); } w!(self, "{}: ", name.display(self.db.upcast(), self.edition)); diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 4601960c7efb..ab897f4e3619 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -53,7 +53,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::Interned; +use intern::{Interned, sym}; pub use rustc_abi as layout; use triomphe::Arc; @@ -86,6 +86,7 @@ use syntax::{AstNode, ast}; pub use hir_expand::{Intern, Lookup, tt}; use crate::{ + attr::Attrs, builtin_type::BuiltinType, data::adt::VariantData, db::DefDatabase, @@ -1502,3 +1503,81 @@ pub struct UnresolvedMacro { #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] pub struct SyntheticSyntax; + +// Feature: Completions Attribute +// Crate authors can opt their type out of completions in some cases. +// This is done with the `#[rust_analyzer::completions(...)]` attribute. +// +// All completeable things support `#[rust_analyzer::completions(ignore_flyimport)]`, +// which causes the thing to get excluded from flyimport completion. It will still +// be completed when in scope. This is analogous to the setting `rust-analyzer.completion.autoimport.exclude` +// with `"type": "always"`. +// +// In addition, traits support two more modes: `#[rust_analyzer::completions(ignore_flyimport_methods)]`, +// which means the trait itself may still be flyimported but its methods won't, and +// `#[rust_analyzer::completions(ignore_methods)]`, which means the methods won't be completed even when +// the trait is in scope (but the trait itself may still be completed). The methods will still be completed +// on `dyn Trait`, `impl Trait` or where the trait is specified in bounds. These modes correspond to +// the settings `rust-analyzer.completion.autoimport.exclude` with `"type": "methods"` and +// `rust-analyzer.completion.excludeTraits`, respectively. +// +// Malformed attributes will be ignored without warnings. +// +// Note that users have no way to override this attribute, so be careful and only include things +// users definitely do not want to be completed! + +/// `#[rust_analyzer::completions(...)]` options. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Complete { + /// No `#[rust_analyzer::completions(...)]`. + Yes, + /// `#[rust_analyzer::completions(ignore_flyimport)]`. + IgnoreFlyimport, + /// `#[rust_analyzer::completions(ignore_flyimport_methods)]` (on a trait only). + IgnoreFlyimportMethods, + /// `#[rust_analyzer::completions(ignore_methods)]` (on a trait only). + IgnoreMethods, +} + +impl Complete { + pub fn extract(is_trait: bool, attrs: &Attrs) -> Complete { + let mut do_not_complete = Complete::Yes; + for ra_attr in attrs.rust_analyzer_tool() { + let segments = ra_attr.path.segments(); + if segments.len() != 2 { + continue; + } + let action = segments[1].symbol(); + if *action == sym::completions { + match ra_attr.token_tree_value().map(|tt| tt.token_trees().flat_tokens()) { + Some([tt::TokenTree::Leaf(tt::Leaf::Ident(ident))]) => { + if ident.sym == sym::ignore_flyimport { + do_not_complete = Complete::IgnoreFlyimport; + } else if is_trait { + if ident.sym == sym::ignore_methods { + do_not_complete = Complete::IgnoreMethods; + } else if ident.sym == sym::ignore_flyimport_methods { + do_not_complete = Complete::IgnoreFlyimportMethods; + } + } + } + _ => {} + } + } + } + do_not_complete + } + + #[inline] + pub fn for_trait_item(trait_attr: Complete, item_attr: Complete) -> Complete { + match (trait_attr, item_attr) { + ( + Complete::IgnoreFlyimportMethods + | Complete::IgnoreFlyimport + | Complete::IgnoreMethods, + _, + ) => Complete::IgnoreFlyimport, + _ => item_attr, + } + } +} diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index ce0ffb179264..5fb8e8e60a15 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -558,7 +558,7 @@ impl<'a> DeclValidator<'a> { fn validate_static(&mut self, static_id: StaticId) { let data = self.db.static_data(static_id); - if data.is_extern { + if data.is_extern() { cov_mark::hit!(extern_static_incorrect_case_ignored); return; } diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 562a9aa085a1..b4fe41714531 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -362,9 +362,9 @@ impl<'a> UnsafeVisitor<'a> { self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { let static_data = self.db.static_data(id); - if static_data.mutable { + if static_data.mutable() { self.on_unsafe_op(node, UnsafetyReason::MutableStatic); - } else if static_data.is_extern && !static_data.has_safe_kw { + } else if static_data.is_extern() && !static_data.has_safe_kw() { self.on_unsafe_op(node, UnsafetyReason::ExternStatic); } } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 9df8d9cebbe8..355a4031e9f2 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1577,7 +1577,7 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query( let mut ctx = TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, t.into()) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); - let inner = if type_alias_data.is_extern { + let inner = if type_alias_data.is_extern() { TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner) } else { type_alias_data diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index d887013b6ff3..895cc58147b1 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -401,7 +401,7 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option { let alias = from_foreign_def_id(id); - Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls { + Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls() { db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id)) } else { smallvec![alias.module(db.upcast()).krate()] @@ -843,9 +843,11 @@ fn is_inherent_impl_coherent( rustc_has_incoherent_inherent_impls && !items.items.is_empty() && items.items.iter().all(|&(_, assoc)| match assoc { - AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl, - AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl, - AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl, + AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl(), + AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl(), + AssocItemId::TypeAliasId(it) => { + db.type_alias_data(it).rustc_allow_incoherent_impl() + } }) } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index ee412dd00b3a..498d707bdafe 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -2756,7 +2756,7 @@ impl Evaluator<'_> { return Ok(*o); }; let static_data = self.db.static_data(st); - let result = if !static_data.is_extern { + let result = if !static_data.is_extern() { let konst = self.db.const_eval_static(st).map_err(|e| { MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e)) })?; diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index ec34fd80ad68..673c336cc364 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -739,7 +739,7 @@ impl HirDisplay for Static { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.static_data(self.id); f.write_str("static ")?; - if data.mutable { + if data.mutable() { f.write_str("mut ")?; } write!(f, "{}: ", data.name.display(f.db.upcast(), f.edition()))?; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 124cbd2ed1a3..c5ed0446839f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -114,6 +114,7 @@ pub use crate::{ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ + Complete, ImportPathConfig, attr::{AttrSourceMap, Attrs, AttrsWithOwner}, data::adt::StructKind, @@ -254,14 +255,17 @@ impl Crate { self, db: &dyn DefDatabase, query: import_map::Query, - ) -> impl Iterator> { + ) -> impl Iterator, Complete)> { let _p = tracing::info_span!("query_external_importables").entered(); - import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| { - match ItemInNs::from(item) { - ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id), - ItemInNs::Macros(mac_id) => Either::Right(mac_id), - } - }) + import_map::search_dependencies(db, self.into(), &query).into_iter().map( + |(item, do_not_complete)| { + let item = match ItemInNs::from(item) { + ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id), + ItemInNs::Macros(mac_id) => Either::Right(mac_id), + }; + (item, do_not_complete) + }, + ) } pub fn all(db: &dyn HirDatabase) -> Vec { @@ -811,7 +815,7 @@ impl Module { let items = &db.trait_items(trait_.into()).items; let required_items = items.iter().filter(|&(_, assoc)| match *assoc { AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), - AssocItemId::ConstId(id) => !db.const_data(id).has_body, + AssocItemId::ConstId(id) => !db.const_data(id).has_body(), AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), }); impl_assoc_items_scratch.extend(db.impl_items(impl_def.id).items.iter().cloned()); @@ -2812,7 +2816,7 @@ impl Static { } pub fn is_mut(self, db: &dyn HirDatabase) -> bool { - db.static_data(self.id).mutable + db.static_data(self.id).mutable() } pub fn value(self, db: &dyn HirDatabase) -> Option { @@ -2932,6 +2936,11 @@ impl Trait { .map(|it| it.as_ref().clone().into_boxed_slice()) .unwrap_or_default() } + + /// `#[rust_analyzer::completions(...)]` mode. + pub fn complete(self, db: &dyn HirDatabase) -> Complete { + Complete::extract(true, &self.attrs(db)) + } } impl HasVisibility for Trait { @@ -6359,3 +6368,48 @@ where self(item) } } + +pub fn resolve_absolute_path<'a, I: Iterator + Clone + 'a>( + db: &'a dyn HirDatabase, + mut segments: I, +) -> impl Iterator + use<'a, I> { + segments + .next() + .into_iter() + .flat_map(move |crate_name| { + db.all_crates() + .iter() + .filter(|&krate| { + krate + .extra_data(db) + .display_name + .as_ref() + .is_some_and(|name| *name.crate_name().symbol() == crate_name) + }) + .filter_map(|&krate| { + let segments = segments.clone(); + let mut def_map = db.crate_def_map(krate); + let mut module = &def_map[DefMap::ROOT]; + let mut segments = segments.with_position().peekable(); + while let Some((_, segment)) = segments.next_if(|&(position, _)| { + !matches!(position, itertools::Position::Last | itertools::Position::Only) + }) { + let res = module + .scope + .get(&Name::new_symbol_root(segment)) + .take_types() + .and_then(|res| match res { + ModuleDefId::ModuleId(it) => Some(it), + _ => None, + })?; + def_map = res.def_map(db.upcast()); + module = &def_map[res.local_id]; + } + let (_, item_name) = segments.next()?; + let res = module.scope.get(&Name::new_symbol_root(item_name)); + Some(res.iter_items().map(|(item, _)| item.into())) + }) + .collect::>() + }) + .flatten() +} diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index ae70f6f0ecc0..679f775e1bd9 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -2,7 +2,7 @@ use either::Either; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId, + AdtId, AssocItemId, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, @@ -34,6 +34,7 @@ pub struct FileSymbol { /// Whether this symbol is a doc alias for the original symbol. pub is_alias: bool, pub is_assoc: bool, + pub do_not_complete: Complete, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -122,35 +123,43 @@ impl<'a> SymbolCollector<'a> { match def { ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { - this.push_decl(id, name, false); + this.push_decl(id, name, false, None); this.collect_from_body(id, Some(name.clone())); } - ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), - ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), - ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), + ModuleDefId::AdtId(AdtId::StructId(id)) => { + this.push_decl(id, name, false, None); + } + ModuleDefId::AdtId(AdtId::EnumId(id)) => { + this.push_decl(id, name, false, None); + } + ModuleDefId::AdtId(AdtId::UnionId(id)) => { + this.push_decl(id, name, false, None); + } ModuleDefId::ConstId(id) => { - this.push_decl(id, name, false); + this.push_decl(id, name, false, None); this.collect_from_body(id, Some(name.clone())); } ModuleDefId::StaticId(id) => { - this.push_decl(id, name, false); + this.push_decl(id, name, false, None); this.collect_from_body(id, Some(name.clone())); } ModuleDefId::TraitId(id) => { - this.push_decl(id, name, false); - this.collect_from_trait(id); + let trait_do_not_complete = this.push_decl(id, name, false, None); + this.collect_from_trait(id, trait_do_not_complete); } ModuleDefId::TraitAliasId(id) => { - this.push_decl(id, name, false); + this.push_decl(id, name, false, None); } ModuleDefId::TypeAliasId(id) => { - this.push_decl(id, name, false); + this.push_decl(id, name, false, None); + } + ModuleDefId::MacroId(id) => { + match id { + MacroId::Macro2Id(id) => this.push_decl(id, name, false, None), + MacroId::MacroRulesId(id) => this.push_decl(id, name, false, None), + MacroId::ProcMacroId(id) => this.push_decl(id, name, false, None), + }; } - ModuleDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => this.push_decl(id, name, false), - MacroId::MacroRulesId(id) => this.push_decl(id, name, false), - MacroId::ProcMacroId(id) => this.push_decl(id, name, false), - }, // Don't index these. ModuleDefId::BuiltinType(_) => {} ModuleDefId::EnumVariantId(_) => {} @@ -194,6 +203,7 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc, is_alias: false, is_assoc: false, + do_not_complete: Complete::Yes, }); }; @@ -223,6 +233,7 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc, is_alias: false, is_assoc: false, + do_not_complete: Complete::Yes, }); }; @@ -281,10 +292,10 @@ impl<'a> SymbolCollector<'a> { for &id in id { if id.module(self.db.upcast()) == module_id { match id { - MacroId::Macro2Id(id) => self.push_decl(id, name, false), - MacroId::MacroRulesId(id) => self.push_decl(id, name, false), - MacroId::ProcMacroId(id) => self.push_decl(id, name, false), - } + MacroId::Macro2Id(id) => self.push_decl(id, name, false, None), + MacroId::MacroRulesId(id) => self.push_decl(id, name, false, None), + MacroId::ProcMacroId(id) => self.push_decl(id, name, false, None), + }; } } } @@ -314,16 +325,16 @@ impl<'a> SymbolCollector<'a> { ); self.with_container_name(impl_name, |s| { for &(ref name, assoc_item_id) in &self.db.impl_items(impl_id).items { - s.push_assoc_item(assoc_item_id, name) + s.push_assoc_item(assoc_item_id, name, None) } }) } - fn collect_from_trait(&mut self, trait_id: TraitId) { + fn collect_from_trait(&mut self, trait_id: TraitId, trait_do_not_complete: Complete) { let trait_data = self.db.trait_data(trait_id); self.with_container_name(Some(trait_data.name.as_str().into()), |s| { for &(ref name, assoc_item_id) in &self.db.trait_items(trait_id).items { - s.push_assoc_item(assoc_item_id, name); + s.push_assoc_item(assoc_item_id, name, Some(trait_do_not_complete)); } }); } @@ -338,15 +349,26 @@ impl<'a> SymbolCollector<'a> { } } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { + fn push_assoc_item( + &mut self, + assoc_item_id: AssocItemId, + name: &Name, + trait_do_not_complete: Option, + ) { match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl(id, name, true), - AssocItemId::ConstId(id) => self.push_decl(id, name, true), - AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true), - } + AssocItemId::FunctionId(id) => self.push_decl(id, name, true, trait_do_not_complete), + AssocItemId::ConstId(id) => self.push_decl(id, name, true, trait_do_not_complete), + AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true, trait_do_not_complete), + }; } - fn push_decl(&mut self, id: L, name: &Name, is_assoc: bool) + fn push_decl( + &mut self, + id: L, + name: &Name, + is_assoc: bool, + trait_do_not_complete: Option, + ) -> Complete where L: Lookup + Into, ::Data: HasSource, @@ -354,7 +376,7 @@ impl<'a> SymbolCollector<'a> { { let loc = id.lookup(self.db.upcast()); let source = loc.source(self.db.upcast()); - let Some(name_node) = source.value.name() else { return }; + let Some(name_node) = source.value.name() else { return Complete::Yes }; let def = ModuleDef::from(id.into()); let dec_loc = DeclarationLocation { hir_file_id: source.file_id, @@ -362,7 +384,14 @@ impl<'a> SymbolCollector<'a> { name_ptr: AstPtr::new(&name_node).wrap_left(), }; + let mut do_not_complete = Complete::Yes; + if let Some(attrs) = def.attrs(self.db) { + do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs); + if let Some(trait_do_not_complete) = trait_do_not_complete { + do_not_complete = Complete::for_trait_item(trait_do_not_complete, do_not_complete); + } + for alias in attrs.doc_aliases() { self.symbols.insert(FileSymbol { name: alias.clone(), @@ -371,6 +400,7 @@ impl<'a> SymbolCollector<'a> { container_name: self.current_container_name.clone(), is_alias: true, is_assoc, + do_not_complete, }); } } @@ -382,7 +412,10 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc, is_alias: false, is_assoc, + do_not_complete, }); + + do_not_complete } fn push_module(&mut self, module_id: ModuleId, name: &Name) { @@ -399,7 +432,10 @@ impl<'a> SymbolCollector<'a> { let def = ModuleDef::Module(module_id.into()); + let mut do_not_complete = Complete::Yes; if let Some(attrs) = def.attrs(self.db) { + do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs); + for alias in attrs.doc_aliases() { self.symbols.insert(FileSymbol { name: alias.clone(), @@ -408,6 +444,7 @@ impl<'a> SymbolCollector<'a> { container_name: self.current_container_name.clone(), is_alias: true, is_assoc: false, + do_not_complete, }); } } @@ -419,6 +456,7 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc, is_alias: false, is_assoc: false, + do_not_complete, }); } } 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 691ae931f8ad..feeea8872a44 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -78,7 +78,7 @@ pub(crate) fn replace_derive_with_manual_impl( NameToImport::exact_case_sensitive(path.segments().last()?.to_string()), items_locator::AssocSearchMode::Exclude, ) - .filter_map(|item| match item.into_module_def() { + .filter_map(|(item, _)| match item.into_module_def() { ModuleDef::Trait(trait_) => Some(trait_), _ => None, }) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index dea983b6ea81..4f21136d214e 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use hir::{HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use hir::{Complete, HasContainer, ItemContainer, MethodCandidateCallback, Name}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -259,7 +259,9 @@ fn complete_methods( // This needs to come before the `seen_methods` test, so that if we see the same method twice, // once as inherent and once not, we will include it. if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) { - if self.ctx.exclude_traits.contains(&trait_) { + if self.ctx.exclude_traits.contains(&trait_) + || trait_.complete(self.ctx.db) == Complete::IgnoreMethods + { return ControlFlow::Continue(()); } } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 7219a5f1b011..0494d4223d18 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use hir::{Name, PathCandidateCallback, ScopeDef, sym}; +use hir::{Complete, Name, PathCandidateCallback, ScopeDef, sym}; use ide_db::FxHashSet; use syntax::ast; @@ -33,10 +33,10 @@ where fn on_trait_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { // The excluded check needs to come before the `seen` test, so that if we see the same method twice, // once as inherent and once not, we will include it. - if item - .container_trait(self.ctx.db) - .is_none_or(|trait_| !self.ctx.exclude_traits.contains(&trait_)) - && self.seen.insert(item) + if item.container_trait(self.ctx.db).is_none_or(|trait_| { + !self.ctx.exclude_traits.contains(&trait_) + && trait_.complete(self.ctx.db) != Complete::IgnoreMethods + }) && self.seen.insert(item) { (self.add_assoc_item)(self.acc, item); } @@ -104,7 +104,9 @@ pub(crate) fn complete_expr_path( .iter() .copied() .map(hir::Trait::from) - .filter(|it| !ctx.exclude_traits.contains(it)) + .filter(|it| { + !ctx.exclude_traits.contains(it) && it.complete(ctx.db) != Complete::IgnoreMethods + }) .flat_map(|it| it.items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), Qualified::TypeAnchor { trait_: Some(trait_), .. } => { diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index b3ba07648930..a74756138090 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -268,19 +268,7 @@ fn import_on_the_fly( && !ctx.is_item_hidden(original_item) && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) }) - .filter(|import| { - let def = import.item_to_import.into_module_def(); - if let Some(&kind) = ctx.exclude_flyimport.get(&def) { - if kind == AutoImportExclusionType::Always { - return false; - } - let method_imported = import.item_to_import != import.original_item; - if method_imported { - return false; - } - } - true - }) + .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { let key = |import_path| { ( @@ -366,24 +354,7 @@ fn import_on_the_fly_method( !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(&import.original_item) }) - .filter(|import| { - let def = import.item_to_import.into_module_def(); - if let Some(&kind) = ctx.exclude_flyimport.get(&def) { - if kind == AutoImportExclusionType::Always { - return false; - } - let method_imported = import.item_to_import != import.original_item; - if method_imported { - return false; - } - } - - if let ModuleDef::Trait(_) = import.item_to_import.into_module_def() { - !ctx.exclude_flyimport.contains_key(&def) - } else { - true - } - }) + .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { let key = |import_path| { ( @@ -401,6 +372,28 @@ fn import_on_the_fly_method( Some(()) } +fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport) -> bool { + let def = import.item_to_import.into_module_def(); + let is_exclude_flyimport = ctx.exclude_flyimport.get(&def).copied(); + + if matches!(is_exclude_flyimport, Some(AutoImportExclusionType::Always)) + || !import.complete_in_flyimport.0 + { + return false; + } + let method_imported = import.item_to_import != import.original_item; + if method_imported + && (is_exclude_flyimport.is_some() + || ctx.exclude_flyimport.contains_key(&import.original_item.into_module_def())) + { + // If this is a method, exclude it either if it was excluded itself (which may not be caught above, + // because `item_to_import` is the trait), or if its trait was excluded. We don't need to check + // the attributes here, since they pass from trait to methods on import map construction. + return false; + } + true +} + fn import_name(ctx: &CompletionContext<'_>) -> String { let token_kind = ctx.token.kind(); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 304379603199..fd25ee05e039 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -8,8 +8,8 @@ use std::{iter, ops::ControlFlow}; use base_db::{RootQueryDb as _, salsa::AsDynDatabase}; use hir::{ - DisplayTarget, HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, - ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, + DisplayTarget, HasAttrs, Local, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, + Semantics, SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs, @@ -796,15 +796,12 @@ impl<'a> CompletionContext<'a> { .exclude_traits .iter() .filter_map(|path| { - scope - .resolve_mod_path(&ModPath::from_segments( - hir::PathKind::Plain, - path.split("::").map(Symbol::intern).map(Name::new_symbol_root), - )) - .find_map(|it| match it { + hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map( + |it| match it { hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t), _ => None, - }) + }, + ) }) .collect(); @@ -812,11 +809,7 @@ impl<'a> CompletionContext<'a> { .exclude_flyimport .iter() .flat_map(|(path, kind)| { - scope - .resolve_mod_path(&ModPath::from_segments( - hir::PathKind::Plain, - path.split("::").map(Symbol::intern).map(Name::new_symbol_root), - )) + hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)) .map(|it| (it.into_module_def(), *kind)) }) .collect(); diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 82c12662c433..b0adbad74ed1 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -334,7 +334,7 @@ pub(crate) fn render_expr( continue; }; - item.add_import(LocatedImport::new(path, trait_item, trait_item)); + item.add_import(LocatedImport::new_no_completion(path, trait_item, trait_item)); } Some(item) diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 07f33a826e4c..9dc0c0234dc5 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -174,7 +174,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option 1).then(|| LocatedImport::new(path.clone(), item, item))) + Some((path.len() > 1).then(|| LocatedImport::new_no_completion(path.clone(), item, item))) }; let mut res = Vec::with_capacity(requires.len()); for import in requires { diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 22d42ba750ed..98da2cbf0780 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -1555,7 +1555,10 @@ fn main() { #[test] fn excluded_trait_method_is_excluded() { check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} @@ -1575,23 +1578,20 @@ fn foo() { } "#, expect![[r#" - me bar() (as ExcludedTrait) fn(&self) - me baz() (as ExcludedTrait) fn(&self) - me foo() (as ExcludedTrait) fn(&self) - me inherent() fn(&self) - sn box Box::new(expr) - sn call function(expr) - sn const const {} - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn deref *expr - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr - sn return return expr - sn unsafe unsafe {} + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} "#]], ); } @@ -1599,7 +1599,10 @@ fn foo() { #[test] fn excluded_trait_not_excluded_when_inherent() { check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} @@ -1633,7 +1636,10 @@ fn foo(v: &dyn ExcludedTrait) { "#]], ); check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} @@ -1667,7 +1673,10 @@ fn foo(v: impl ExcludedTrait) { "#]], ); check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} @@ -1706,7 +1715,7 @@ fn foo(v: T) { fn excluded_trait_method_is_excluded_from_flyimport() { check_with_config( CompletionConfig { - exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + exclude_traits: &["ra_test_fixture::module2::ExcludedTrait".to_owned()], ..TEST_CONFIG }, r#" @@ -1730,23 +1739,20 @@ fn foo() { } "#, expect![[r#" - me bar() (use module2::ExcludedTrait) fn(&self) - me baz() (use module2::ExcludedTrait) fn(&self) - me foo() (use module2::ExcludedTrait) fn(&self) - me inherent() fn(&self) - sn box Box::new(expr) - sn call function(expr) - sn const const {} - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn deref *expr - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr - sn return return expr - sn unsafe unsafe {} + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} "#]], ); } @@ -1756,7 +1762,7 @@ fn flyimport_excluded_trait_method_is_excluded_from_flyimport() { check_with_config( CompletionConfig { exclude_flyimport: vec![( - "test::module2::ExcludedTrait".to_owned(), + "ra_test_fixture::module2::ExcludedTrait".to_owned(), AutoImportExclusionType::Methods, )], ..TEST_CONFIG @@ -1782,23 +1788,20 @@ fn foo() { } "#, expect![[r#" - me bar() (use module2::ExcludedTrait) fn(&self) - me baz() (use module2::ExcludedTrait) fn(&self) - me foo() (use module2::ExcludedTrait) fn(&self) - me inherent() fn(&self) - sn box Box::new(expr) - sn call function(expr) - sn const const {} - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn deref *expr - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr - sn return return expr - sn unsafe unsafe {} + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} "#]], ); } @@ -1806,7 +1809,10 @@ fn foo() { #[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" pub trait ExcludedTrait { fn foo(&self) {} @@ -1826,10 +1832,7 @@ fn foo() { } "#, expect![[r#" - me bar(…) (as ExcludedTrait) fn(&self) - me baz(…) (as ExcludedTrait) fn(&self) - me foo(…) (as ExcludedTrait) fn(&self) - me inherent(…) fn(&self) + me inherent(…) fn(&self) "#]], ); } @@ -1837,7 +1840,10 @@ fn foo() { #[test] fn excluded_trait_method_is_not_excluded_when_trait_is_specified() { check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" pub trait ExcludedTrait { fn foo(&self) {} @@ -1863,7 +1869,10 @@ fn foo() { "#]], ); check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" pub trait ExcludedTrait { fn foo(&self) {} @@ -1893,7 +1902,10 @@ fn foo() { #[test] fn excluded_trait_not_excluded_when_inherent_path() { check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} @@ -1914,7 +1926,10 @@ fn foo() { "#]], ); check_with_config( - CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + CompletionConfig { + exclude_traits: &["ra_test_fixture::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, r#" trait ExcludedTrait { fn foo(&self) {} diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 8bba44c12bf6..27c91bc7c455 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1810,9 +1810,10 @@ fn function() { #[test] fn excluded_trait_item_included_when_exact_match() { + // FIXME: This does not work, we need to change the code. check_with_config( CompletionConfig { - exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + exclude_traits: &["ra_test_fixture::module2::ExcludedTrait".to_owned()], ..TEST_CONFIG }, r#" @@ -1828,10 +1829,122 @@ mod module2 { fn foo() { true.foo$0 +} + "#, + expect![""], + ); +} + +#[test] +fn excluded_via_attr() { + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_flyimport)] + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + true.$0 +} + "#, + expect![""], + ); + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_flyimport_methods)] + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + true.$0 +} + "#, + expect![""], + ); + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_methods)] + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + true.$0 +} + "#, + expect![""], + ); + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_flyimport)] + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + ExcludedTrait$0 +} + "#, + expect![""], + ); + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_methods)] + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + ExcludedTrait$0 } "#, expect![[r#" - me foo() (use module2::ExcludedTrait) fn(&self) + tt ExcludedTrait (use module2::ExcludedTrait) "#]], ); + check( + r#" +mod module2 { + #[rust_analyzer::completions(ignore_flyimport)] + pub struct Foo {} +} + +fn foo() { + Foo$0 +} + "#, + expect![""], + ); } diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 650c957ca458..ac592dfe93cf 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -3,8 +3,8 @@ use std::ops::ControlFlow; use hir::{ - AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, ItemInNs, - ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, + AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, HasCrate, ImportPathConfig, + ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Trait, TyFingerprint, Type, db::HirDatabase, }; use itertools::Itertools; @@ -183,6 +183,9 @@ impl ImportAssets { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompleteInFlyimport(pub bool); + /// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`]. /// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details) #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -198,11 +201,31 @@ pub struct LocatedImport { /// the original item is the associated constant, but the import has to be a trait that /// defines this constant. pub original_item: ItemInNs, + /// The value of `#[rust_analyzer::completions(...)]`, if existing. + pub complete_in_flyimport: CompleteInFlyimport, } impl LocatedImport { - pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self { - Self { import_path, item_to_import, original_item } + pub fn new( + import_path: ModPath, + item_to_import: ItemInNs, + original_item: ItemInNs, + complete_in_flyimport: CompleteInFlyimport, + ) -> Self { + Self { import_path, item_to_import, original_item, complete_in_flyimport } + } + + pub fn new_no_completion( + import_path: ModPath, + item_to_import: ItemInNs, + original_item: ItemInNs, + ) -> Self { + Self { + import_path, + item_to_import, + original_item, + complete_in_flyimport: CompleteInFlyimport(true), + } } } @@ -351,12 +374,17 @@ fn path_applicable_imports( // see also an ignored test under FIXME comment in the qualify_path.rs module AssocSearchMode::Exclude, ) - .filter_map(|item| { + .filter_map(|(item, do_not_complete)| { if !scope_filter(item) { return None; } let mod_path = mod_path(item)?; - Some(LocatedImport::new(mod_path, item, item)) + Some(LocatedImport::new( + mod_path, + item, + item, + CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport), + )) }) .take(DEFAULT_QUERY_SEARCH_LIMIT) .collect() @@ -371,7 +399,7 @@ fn path_applicable_imports( NameToImport::Exact(first_qsegment.as_str().to_owned(), true), AssocSearchMode::Exclude, ) - .filter_map(|item| { + .filter_map(|(item, do_not_complete)| { // we found imports for `first_qsegment`, now we need to filter these imports by whether // they result in resolving the rest of the path successfully validate_resolvable( @@ -382,6 +410,7 @@ fn path_applicable_imports( &path_candidate.name, item, qualifier_rest, + CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport), ) }) .take(DEFAULT_QUERY_SEARCH_LIMIT) @@ -399,6 +428,7 @@ fn validate_resolvable( candidate: &NameToImport, resolved_qualifier: ItemInNs, unresolved_qualifier: &[Name], + complete_in_flyimport: CompleteInFlyimport, ) -> Option { let _p = tracing::info_span!("ImportAssets::import_for_item").entered(); @@ -434,7 +464,14 @@ fn validate_resolvable( false => ControlFlow::Continue(()), }, ) - .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item)); + .map(|item| { + LocatedImport::new( + import_path_candidate, + resolved_qualifier, + item, + complete_in_flyimport, + ) + }); } // FIXME ModuleDef::Trait(_) => return None, @@ -472,6 +509,7 @@ fn validate_resolvable( import_path_candidate.clone(), resolved_qualifier, assoc_to_item(assoc), + complete_in_flyimport, )) }) } @@ -510,15 +548,15 @@ fn trait_applicable_items( let env_traits = trait_candidate.receiver_ty.env_traits(db); let related_traits = inherent_traits.chain(env_traits).collect::>(); - let mut required_assoc_items = FxHashSet::default(); + let mut required_assoc_items = FxHashMap::default(); let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name( db, current_crate, trait_candidate.assoc_item_name.clone(), AssocSearchMode::AssocItemsOnly, ) - .filter_map(|input| item_as_assoc(db, input)) - .filter_map(|assoc| { + .filter_map(|(input, do_not_complete)| Some((item_as_assoc(db, input)?, do_not_complete))) + .filter_map(|(assoc, do_not_complete)| { if !trait_assoc_item && matches!(assoc, AssocItem::Const(_) | AssocItem::TypeAlias(_)) { return None; } @@ -527,7 +565,8 @@ fn trait_applicable_items( if related_traits.contains(&assoc_item_trait) { return None; } - required_assoc_items.insert(assoc); + required_assoc_items + .insert(assoc, CompleteInFlyimport(do_not_complete != Complete::IgnoreFlyimport)); Some(assoc_item_trait.into()) }) .collect(); @@ -599,7 +638,7 @@ fn trait_applicable_items( None, None, |assoc| { - if required_assoc_items.contains(&assoc) { + if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) { let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let import_path = trait_import_paths @@ -610,6 +649,7 @@ fn trait_applicable_items( import_path, trait_item, assoc_to_item(assoc), + complete_in_flyimport, )); } None::<()> @@ -624,7 +664,7 @@ fn trait_applicable_items( None, |function| { let assoc = function.as_assoc_item(db)?; - if required_assoc_items.contains(&assoc) { + if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) { let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let import_path = trait_import_paths @@ -635,6 +675,7 @@ fn trait_applicable_items( import_path, trait_item, assoc_to_item(assoc), + complete_in_flyimport, )); } None::<()> diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 7a543d64e27f..e9385253250a 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -5,7 +5,7 @@ use std::ops::ControlFlow; use either::Either; -use hir::{Crate, ItemInNs, Module, import_map}; +use hir::{Complete, Crate, ItemInNs, Module, import_map}; use crate::{ RootDatabase, @@ -25,7 +25,7 @@ pub fn items_with_name( krate: Crate, name: NameToImport, assoc_item_search: AssocSearchMode, -) -> impl Iterator { +) -> impl Iterator { let _p = tracing::info_span!("items_with_name", name = name.text(), assoc_item_search = ?assoc_item_search, crate = ?krate.display_name(db).map(|name| name.to_string())) .entered(); @@ -123,26 +123,29 @@ fn find_items( krate: Crate, local_query: symbol_index::Query, external_query: import_map::Query, -) -> impl Iterator { +) -> impl Iterator { let _p = tracing::info_span!("find_items").entered(); // 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 { + let external_importables = krate.query_external_importables(db, external_query).map( + |(external_importable, do_not_complete)| { + let external_importable = match external_importable { Either::Left(module_def) => ItemInNs::from(module_def), Either::Right(macro_def) => ItemInNs::from(macro_def), - } - }); + }; + (external_importable, do_not_complete) + }, + ); // Query the local crate using the symbol index. let mut local_results = Vec::new(); local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| { - local_results.push(match local_candidate.def { + let def = match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), - }); + }; + local_results.push((def, local_candidate.do_not_complete)); ControlFlow::<()>::Continue(()) }); local_results.into_iter().chain(external_importables) diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt index ea50745d6793..8e342ec553c2 100644 --- a/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -42,6 +42,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Struct", @@ -75,6 +76,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "mul1", @@ -108,6 +110,7 @@ container_name: None, is_alias: true, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "mul2", @@ -141,6 +144,7 @@ container_name: None, is_alias: true, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "s1", @@ -174,6 +178,7 @@ container_name: None, is_alias: true, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "s1", @@ -207,6 +212,7 @@ container_name: None, is_alias: true, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "s2", @@ -240,6 +246,7 @@ container_name: None, is_alias: true, is_assoc: false, + do_not_complete: Yes, }, ], ), diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index d2d24262f098..6de25c000e52 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -40,6 +40,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "CONST", @@ -71,6 +72,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "CONST_WITH_INNER", @@ -102,6 +104,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Enum", @@ -135,6 +138,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "ItemLikeMacro", @@ -168,6 +172,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Macro", @@ -201,6 +206,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "STATIC", @@ -232,6 +238,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Struct", @@ -265,6 +272,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructFromMacro", @@ -295,6 +303,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructInFn", @@ -330,6 +339,7 @@ ), is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructInNamedConst", @@ -365,6 +375,7 @@ ), is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructInUnnamedConst", @@ -398,6 +409,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructT", @@ -431,6 +443,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Trait", @@ -462,6 +475,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Trait", @@ -495,6 +509,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "Union", @@ -528,6 +543,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "a_mod", @@ -563,6 +579,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "b_mod", @@ -598,6 +615,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "define_struct", @@ -631,6 +649,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "generic_impl_fn", @@ -664,6 +683,7 @@ ), is_alias: false, is_assoc: true, + do_not_complete: Yes, }, FileSymbol { name: "impl_fn", @@ -697,6 +717,7 @@ ), is_alias: false, is_assoc: true, + do_not_complete: Yes, }, FileSymbol { name: "macro_rules_macro", @@ -730,6 +751,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "main", @@ -761,6 +783,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "really_define_struct", @@ -794,6 +817,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "trait_fn", @@ -827,6 +851,7 @@ ), is_alias: false, is_assoc: true, + do_not_complete: Yes, }, ], ), @@ -873,6 +898,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, ], ), @@ -917,6 +943,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "IsThisJustATrait", @@ -950,6 +977,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "StructInModB", @@ -983,6 +1011,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "SuperItemLikeMacro", @@ -1016,6 +1045,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, FileSymbol { name: "ThisStruct", @@ -1049,6 +1079,7 @@ container_name: None, is_alias: false, is_assoc: false, + do_not_complete: Yes, }, ], ), diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index cc9b3ef45736..4841f4865915 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -521,4 +521,8 @@ define_symbols! { win64, array, boxed_slice, + completions, + ignore_flyimport, + ignore_flyimport_methods, + ignore_methods, }