diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index a8ee5eeac9f5..9014468ea04f 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs @@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::format_to; use crate::{ - db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, + db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId, }; @@ -37,6 +37,7 @@ pub struct ItemScope { defs: Vec, impls: Vec, + unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. unnamed_trait_imports: FxHashMap, /// Macros visible in current module in legacy textual scope @@ -106,6 +107,10 @@ impl ItemScope { .map(|(_, v)| v) } + pub fn unnamed_consts(&self) -> impl Iterator + '_ { + self.unnamed_consts.iter().copied() + } + /// Iterate over all module scoped macros pub(crate) fn macros<'a>(&'a self) -> impl Iterator + 'a { self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) @@ -156,6 +161,10 @@ impl ItemScope { self.impls.push(imp) } + pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) { + self.unnamed_consts.push(konst); + } + pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { self.legacy_macros.insert(name, mac); } @@ -295,6 +304,7 @@ impl ItemScope { unresolved, defs, impls, + unnamed_consts, unnamed_trait_imports, legacy_macros, } = self; @@ -304,6 +314,7 @@ impl ItemScope { unresolved.shrink_to_fit(); defs.shrink_to_fit(); impls.shrink_to_fit(); + unnamed_consts.shrink_to_fit(); unnamed_trait_imports.shrink_to_fit(); legacy_macros.shrink_to_fit(); } diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index f42f9270230b..492d8c71ff83 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -1163,19 +1163,27 @@ impl ModCollector<'_, '_> { } ModItem::Const(id) => { let it = &self.item_tree[id]; - - if let Some(name) = &it.name { - def = Some(DefData { - id: ConstLoc { - container: module.into(), - id: ItemTreeId::new(self.file_id, id), - } - .intern(self.def_collector.db) - .into(), - name, - visibility: &self.item_tree[it.visibility], - has_constructor: false, - }); + let const_id = ConstLoc { + container: module.into(), + id: ItemTreeId::new(self.file_id, id), + } + .intern(self.def_collector.db); + + match &it.name { + Some(name) => { + def = Some(DefData { + id: const_id.into(), + name, + visibility: &self.item_tree[it.visibility], + has_constructor: false, + }); + } + None => { + // const _: T = ...; + self.def_collector.def_map.modules[self.module_id] + .scope + .define_unnamed_const(const_id); + } } } ModItem::Static(id) => { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index ee725fd468bb..f29319f2065a 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -8,8 +8,8 @@ use arrayvec::ArrayVec; use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ - lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule, - ImplId, Lookup, ModuleId, TraitId, + lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, + GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -100,25 +100,38 @@ impl TraitImpls { let mut impls = Self { map: FxHashMap::default() }; let crate_def_map = db.crate_def_map(krate); - for (_module_id, module_data) in crate_def_map.modules() { - for impl_id in module_data.scope.impls() { - let target_trait = match db.impl_trait(impl_id) { - Some(tr) => tr.skip_binders().hir_trait_id(), - None => continue, - }; - let self_ty = db.impl_self_ty(impl_id); - let self_ty_fp = TyFingerprint::for_impl(self_ty.skip_binders()); - impls - .map - .entry(target_trait) - .or_default() - .entry(self_ty_fp) - .or_default() - .push(impl_id); + collect_def_map(db, &crate_def_map, &mut impls); + + return Arc::new(impls); + + fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { + for (_module_id, module_data) in def_map.modules() { + for impl_id in module_data.scope.impls() { + let target_trait = match db.impl_trait(impl_id) { + Some(tr) => tr.skip_binders().hir_trait_id(), + None => continue, + }; + let self_ty = db.impl_self_ty(impl_id); + let self_ty_fp = TyFingerprint::for_impl(self_ty.skip_binders()); + impls + .map + .entry(target_trait) + .or_default() + .entry(self_ty_fp) + .or_default() + .push(impl_id); + } + + // To better support custom derives, collect impls in all unnamed const items. + // const _: () = { ... }; + for konst in module_data.scope.unnamed_consts() { + let body = db.body(konst.into()); + for (_, block_def_map) in body.blocks(db.upcast()) { + collect_def_map(db, &block_def_map, impls); + } + } } } - - Arc::new(impls) } pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { @@ -208,6 +221,9 @@ impl InherentImpls { } } + // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only + // support trait impls there. + Arc::new(Self { map }) } diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 61f18b0d2069..4b2c82b417fe 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1292,3 +1292,25 @@ mod b { "#]], ) } + +#[test] +fn impl_in_unnamed_const() { + check_types( + r#" +struct S; + +trait Tr { + fn method(&self) -> u16; +} + +const _: () = { + impl Tr for S {} +}; + +fn f() { + S.method(); + //^^^^^^^^^^ u16 +} + "#, + ); +}