Repository: rust-analyzer/rust-analyzer
Pull request #7801 (2021-02-28 03:49:26)

Restrict visibilities to the containing DefMap
Visibilities must always point into the DefMap where they are used, but in a block expression `self` resolves to the *containing* non-block module, which is in a different DefMap. Restrict visibilities accordingly, turning them into basically `pub(block)`, which Rust has no syntax for.

bors r+


Patch:
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index fdcdc23ae7..dd1db0094b 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -77,7 +77,7 @@ impl DefMap {
         original_module: LocalModuleId,
         visibility: &RawVisibility,
     ) -> Option<Visibility> {
-        match visibility {
+        let mut vis = match visibility {
             RawVisibility::Module(path) => {
                 let (result, remaining) =
                     self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
@@ -86,15 +86,28 @@ impl DefMap {
                 }
                 let types = result.take_types()?;
                 match types {
-                    ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
+                    ModuleDefId::ModuleId(m) => Visibility::Module(m),
                     _ => {
                         // error: visibility needs to refer to module
-                        None
+                        return None;
                     }
                 }
             }
-            RawVisibility::Public => Some(Visibility::Public),
+            RawVisibility::Public => Visibility::Public,
+        };
+
+        // In block expressions, `self` normally refers to the containing non-block module, and
+        // `super` to its parent (etc.). However, visibilities must only refer to a module in the
+        // DefMap they're written in, so we restrict them when that happens.
+        if let Visibility::Module(m) = vis {
+            if self.block_id() != m.block {
+                mark::hit!(adjust_vis_in_block_def_map);
+                vis = Visibility::Module(self.module_id(self.root()));
+                log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
+            }
         }
+
+        Some(vis)
     }
 
     // Returns Yes if we are sure that additions to `ItemMap` wouldn't change


Test patch:
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index a5ec0883fb..8bca72a175 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -259,3 +259,32 @@ fn main() {
     "#]],
     );
 }
+
+#[test]
+fn underscore_import() {
+    // This used to panic, because the default (private) visibility inside block expressions would
+    // point into the containing `DefMap`, which visibilities should never be able to do.
+    mark::check!(adjust_vis_in_block_def_map);
+    check_at(
+        r#"
+mod m {
+    fn main() {
+        use Tr as _;
+        trait Tr {}
+        $0
+    }
+}
+    "#,
+        expect![[r#"
+        block scope
+        _: t
+        Tr: t
+
+        crate
+        m: t
+
+        crate::m
+        main: v
+    "#]],
+    );
+}