about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-04-18 23:08:44 +0000
committerGitHub <noreply@github.com>2021-04-18 23:08:44 +0000
commit7570212a544b8e973a7d57be3657aae6465028a7 (patch)
tree9f01d4563413e95e0b743b991f2a6ff522db889d
parentd39873e88b12b6c6c56bed530500baf07bf3391f (diff)
parent20c27dbdbe3116be205d66af88e6f5ac88b862d3 (diff)
downloadrust-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.rs12
-rw-r--r--crates/hir_def/src/visibility.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs42
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs37
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
+}
+    "#,
+    );
+}