diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-04-18 23:08:44 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-18 23:08:44 +0000 |
| commit | 7570212a544b8e973a7d57be3657aae6465028a7 (patch) | |
| tree | 9f01d4563413e95e0b743b991f2a6ff522db889d | |
| parent | d39873e88b12b6c6c56bed530500baf07bf3391f (diff) | |
| parent | 20c27dbdbe3116be205d66af88e6f5ac88b862d3 (diff) | |
| download | rust-7570212a544b8e973a7d57be3657aae6465028a7.tar.gz rust-7570212a544b8e973a7d57be3657aae6465028a7.zip | |
Merge #8569
8569: Support inherent impls in unnamed consts r=jonas-schievink a=jonas-schievink
It turns out that some proc. macros not only generate *trait* impls wrapped in `const _: () = { ... };`, but inherent impls too. Even though it is questionable whether *custom derives* should produce non-trait impls, this is useful for procedural attribute macros once we support them.
bors r+
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
| -rw-r--r-- | crates/hir_def/src/lib.rs | 12 | ||||
| -rw-r--r-- | crates/hir_def/src/visibility.rs | 10 | ||||
| -rw-r--r-- | crates/hir_ty/src/method_resolution.rs | 42 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/method_resolution.rs | 37 |
4 files changed, 83 insertions, 18 deletions
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 000567d990f..5ac1670b55e 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs @@ -108,6 +108,18 @@ impl ModuleId { pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { self.def_map(db).containing_module(self.local_id) } + + /// Returns `true` if this module represents a block expression. + /// + /// Returns `false` if this module is a submodule *inside* a block expression + /// (eg. `m` in `{ mod m {} }`). + pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool { + if self.block.is_none() { + return false; + } + + self.def_map(db)[self.local_id].parent.is_none() + } } /// An ID of a module, **local** to a specific crate diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 9908cd92654..d4b7c9970ce 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs @@ -123,11 +123,19 @@ impl Visibility { def_map: &DefMap, mut from_module: crate::LocalModuleId, ) -> bool { - let to_module = match self { + let mut to_module = match self { Visibility::Module(m) => m, Visibility::Public => return true, }; + // `to_module` might be the root module of a block expression. Those have the same + // visibility as the containing module (even though no items are directly nameable from + // there, getting this right is important for method resolution). + // In that case, we adjust the visibility of `to_module` to point to the containing module. + if to_module.is_block_root(db) { + to_module = to_module.containing_module(db).unwrap(); + } + // from_module needs to be a descendant of to_module let mut def_map = def_map; let mut parent_arc; diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 3693e328451..48bbcfd9ffa 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -246,29 +246,39 @@ pub struct InherentImpls { impl InherentImpls { pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { - let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); + 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 data = db.impl_data(impl_id); - if data.target_trait.is_some() { - continue; + 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 InherentImpls) { + for (_module_id, module_data) in def_map.modules() { + for impl_id in module_data.scope.impls() { + let data = db.impl_data(impl_id); + if data.target_trait.is_some() { + continue; + } + + let self_ty = db.impl_self_ty(impl_id); + let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); + if let Some(fp) = fp { + impls.map.entry(fp).or_default().push(impl_id); + } + // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) } - let self_ty = db.impl_self_ty(impl_id); - let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); - if let Some(fp) = fp { - map.entry(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); + } } - // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) } } - - // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only - // support trait impls there. - - Arc::new(Self { map }) } pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 4b2c82b417f..a4c132bc5f2 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1294,7 +1294,7 @@ mod b { } #[test] -fn impl_in_unnamed_const() { +fn trait_impl_in_unnamed_const() { check_types( r#" struct S; @@ -1314,3 +1314,38 @@ fn f() { "#, ); } + +#[test] +fn inherent_impl_in_unnamed_const() { + check_types( + r#" +struct S; + +const _: () = { + impl S { + fn method(&self) -> u16 { 0 } + + pub(super) fn super_method(&self) -> u16 { 0 } + + pub(crate) fn crate_method(&self) -> u16 { 0 } + + pub fn pub_method(&self) -> u16 { 0 } + } +}; + +fn f() { + S.method(); + //^^^^^^^^^^ u16 + + S.super_method(); + //^^^^^^^^^^^^^^^^ u16 + + S.crate_method(); + //^^^^^^^^^^^^^^^^ u16 + + S.pub_method(); + //^^^^^^^^^^^^^^ u16 +} + "#, + ); +} |
