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>
This commit is contained in:
bors[bot] 2021-04-18 23:08:44 +00:00 committed by GitHub
commit 7570212a54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 18 deletions

View File

@ -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

View File

@ -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;

View File

@ -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] {

View File

@ -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
}
"#,
);
}