about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-02-18 03:35:21 +0000
committerbors <bors@rust-lang.org>2016-02-18 03:35:21 +0000
commita9430a359f36e8d19505398129223a60d3f3b972 (patch)
tree548ca210a87add7ea76984e11d63cee05e4c79f1
parente18f7a1c5af91f8630f3c47f73f490c49b61acde (diff)
parentfadc95e60a7d9eb4f886169b1abbfb9e96274927 (diff)
downloadrust-a9430a359f36e8d19505398129223a60d3f3b972.tar.gz
rust-a9430a359f36e8d19505398129223a60d3f3b972.zip
Auto merge of #31641 - petrochenkov:reach, r=alexcrichton
Fixes https://github.com/rust-lang/rust/issues/16734 and probably some other issues

This is a continuation of https://github.com/rust-lang/rust/pull/29822, but the algorithm is mostly a copy of https://github.com/rust-lang/rust/pull/29973, so
r? @alexcrichton or @nikomatsakis
-rw-r--r--src/librustc_privacy/lib.rs145
-rw-r--r--src/test/auxiliary/reachable-unnameable-items.rs116
-rw-r--r--src/test/run-pass/reachable-unnameable-items.rs42
-rw-r--r--src/test/run-pass/reachable-unnameable-type-alias.rs24
4 files changed, 316 insertions, 11 deletions
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 1424616e792..366089645bf 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -169,6 +169,10 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
     changed: bool,
 }
 
+struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> {
+    ev: &'b mut EmbargoVisitor<'a, 'tcx>,
+}
+
 impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
     fn ty_level(&self, ty: &hir::Ty) -> Option<AccessLevel> {
         if let hir::TyPath(..) = ty.node {
@@ -214,6 +218,10 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
             old_level
         }
     }
+
+    fn reach<'b>(&'b mut self) -> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
+        ReachEverythingInTheInterfaceVisitor { ev: self }
+    }
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
@@ -245,10 +253,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
             }
         };
 
-        // Update id of the item itself
+        // Update level of the item itself
         let item_level = self.update(item.id, inherited_item_level);
 
-        // Update ids of nested things
+        // Update levels of nested things
         match item.node {
             hir::ItemEnum(ref def, _) => {
                 for variant in &def.variants {
@@ -292,19 +300,72 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
                     }
                 }
             }
-            hir::ItemTy(ref ty, _) if item_level.is_some() => {
-                if let hir::TyPath(..) = ty.node {
-                    match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
-                        Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => {},
-                        def => {
-                            if let Some(node_id) = self.tcx.map.as_local_node_id(def.def_id()) {
-                                self.update(node_id, Some(AccessLevel::Reachable));
-                            }
+            _ => {}
+        }
+
+        // Mark all items in interfaces of reachable items as reachable
+        match item.node {
+            // The interface is empty
+            hir::ItemExternCrate(..) => {}
+            // All nested items are checked by visit_item
+            hir::ItemMod(..) => {}
+            // Reexports are handled in visit_mod
+            hir::ItemUse(..) => {}
+            // Visit everything
+            hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
+            hir::ItemTrait(..) | hir::ItemTy(..) | hir::ItemImpl(_, _, _, Some(..), _, _) => {
+                if item_level.is_some() {
+                    self.reach().visit_item(item);
+                }
+            }
+            // Visit everything, but enum variants have their own levels
+            hir::ItemEnum(ref def, ref generics) => {
+                if item_level.is_some() {
+                    self.reach().visit_generics(generics);
+                }
+                for variant in &def.variants {
+                    if self.get(variant.node.data.id()).is_some() {
+                        for field in variant.node.data.fields() {
+                            self.reach().visit_struct_field(field);
+                        }
+                        // Corner case: if the variant is reachable, but its
+                        // enum is not, make the enum reachable as well.
+                        self.update(item.id, Some(AccessLevel::Reachable));
+                    }
+                }
+            }
+            // Visit everything, but foreign items have their own levels
+            hir::ItemForeignMod(ref foreign_mod) => {
+                for foreign_item in &foreign_mod.items {
+                    if self.get(foreign_item.id).is_some() {
+                        self.reach().visit_foreign_item(foreign_item);
+                    }
+                }
+            }
+            // Visit everything except for private fields
+            hir::ItemStruct(ref struct_def, ref generics) => {
+                if item_level.is_some() {
+                    self.reach().visit_generics(generics);
+                    for field in struct_def.fields() {
+                        if self.get(field.node.id).is_some() {
+                            self.reach().visit_struct_field(field);
+                        }
+                    }
+                }
+            }
+            // The interface is empty
+            hir::ItemDefaultImpl(..) => {}
+            // Visit everything except for private impl items
+            hir::ItemImpl(_, _, ref generics, None, _, ref impl_items) => {
+                if item_level.is_some() {
+                    self.reach().visit_generics(generics);
+                    for impl_item in impl_items {
+                        if self.get(impl_item.id).is_some() {
+                            self.reach().visit_impl_item(impl_item);
                         }
                     }
                 }
             }
-            _ => {}
         }
 
         let orig_level = self.prev_level;
@@ -347,6 +408,68 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
     }
 }
 
+impl<'b, 'a, 'tcx: 'a> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
+    // Make the type hidden under a type alias reachable
+    fn reach_aliased_type(&mut self, item: &hir::Item, path: &hir::Path) {
+        if let hir::ItemTy(ref ty, ref generics) = item.node {
+            // See `fn is_public_type_alias` for details
+            self.visit_ty(ty);
+            let provided_params = path.segments.last().unwrap().parameters.types().len();
+            for ty_param in &generics.ty_params[provided_params..] {
+                if let Some(ref default_ty) = ty_param.default {
+                    self.visit_ty(default_ty);
+                }
+            }
+        }
+    }
+}
+
+impl<'b, 'a, 'tcx: 'a, 'v> Visitor<'v> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
+    fn visit_ty(&mut self, ty: &hir::Ty) {
+        if let hir::TyPath(_, ref path) = ty.node {
+            let def = self.ev.tcx.def_map.borrow().get(&ty.id).unwrap().full_def();
+            match def {
+                Def::Struct(def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) |
+                Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => {
+                    if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
+                        let item = self.ev.tcx.map.expect_item(node_id);
+                        if let Def::TyAlias(..) = def {
+                            // Type aliases are substituted. Associated type aliases are not
+                            // substituted yet, but ideally they should be.
+                            if self.ev.get(item.id).is_none() {
+                                self.reach_aliased_type(item, path);
+                            }
+                        } else {
+                            self.ev.update(item.id, Some(AccessLevel::Reachable));
+                        }
+                    }
+                }
+
+                _ => {}
+            }
+        }
+
+        intravisit::walk_ty(self, ty);
+    }
+
+    fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) {
+        let def_id = self.ev.tcx.trait_ref_to_def_id(trait_ref);
+        if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
+            let item = self.ev.tcx.map.expect_item(node_id);
+            self.ev.update(item.id, Some(AccessLevel::Reachable));
+        }
+
+        intravisit::walk_trait_ref(self, trait_ref);
+    }
+
+    // Don't recurse into function bodies
+    fn visit_block(&mut self, _: &hir::Block) {}
+    // Don't recurse into expressions in array sizes or const initializers
+    fn visit_expr(&mut self, _: &hir::Expr) {}
+    // Don't recurse into patterns in function arguments
+    fn visit_pat(&mut self, _: &hir::Pat) {}
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// The privacy visitor, where privacy checks take place (violations reported)
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/test/auxiliary/reachable-unnameable-items.rs b/src/test/auxiliary/reachable-unnameable-items.rs
new file mode 100644
index 00000000000..7ec2bb9394c
--- /dev/null
+++ b/src/test/auxiliary/reachable-unnameable-items.rs
@@ -0,0 +1,116 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use inner_private_module::*;
+
+mod inner_private_module {
+    pub struct Unnameable1;
+    pub struct Unnameable2;
+    #[derive(Clone, Copy)]
+    pub struct Unnameable3;
+    pub struct Unnameable4;
+    pub struct Unnameable5;
+    pub struct Unnameable6;
+    pub struct Unnameable7;
+    #[derive(Default)]
+    pub struct Unnameable8;
+    pub enum UnnameableEnum {
+        NameableVariant
+    }
+    pub trait UnnameableTrait {
+        type Alias: Default;
+    }
+
+    impl Unnameable1 {
+        pub fn method_of_unnameable_type1(&self) -> &'static str {
+            "Hello1"
+        }
+    }
+    impl Unnameable2 {
+        pub fn method_of_unnameable_type2(&self) -> &'static str {
+            "Hello2"
+        }
+    }
+    impl Unnameable3 {
+        pub fn method_of_unnameable_type3(&self) -> &'static str {
+            "Hello3"
+        }
+    }
+    impl Unnameable4 {
+        pub fn method_of_unnameable_type4(&self) -> &'static str {
+            "Hello4"
+        }
+    }
+    impl Unnameable5 {
+        pub fn method_of_unnameable_type5(&self) -> &'static str {
+            "Hello5"
+        }
+    }
+    impl Unnameable6 {
+        pub fn method_of_unnameable_type6(&self) -> &'static str {
+            "Hello6"
+        }
+    }
+    impl Unnameable7 {
+        pub fn method_of_unnameable_type7(&self) -> &'static str {
+            "Hello7"
+        }
+    }
+    impl Unnameable8 {
+        pub fn method_of_unnameable_type8(&self) -> &'static str {
+            "Hello8"
+        }
+    }
+    impl UnnameableEnum {
+        pub fn method_of_unnameable_enum(&self) -> &'static str {
+            "HelloEnum"
+        }
+    }
+}
+
+pub fn function_returning_unnameable_type() -> Unnameable1 {
+    Unnameable1
+}
+
+pub const CONSTANT_OF_UNNAMEABLE_TYPE: Unnameable2 =
+                                            Unnameable2;
+
+pub fn function_accepting_unnameable_type(_: Option<Unnameable3>) {}
+
+pub type AliasOfUnnameableType = Unnameable4;
+
+impl Unnameable1 {
+    pub fn inherent_method_returning_unnameable_type(&self) -> Unnameable5 {
+        Unnameable5
+    }
+}
+
+pub trait Tr {
+    fn trait_method_returning_unnameable_type(&self) -> Unnameable6 {
+        Unnameable6
+    }
+}
+impl Tr for Unnameable1 {}
+
+pub use inner_private_module::UnnameableEnum::NameableVariant;
+
+pub struct Struct {
+    pub field_of_unnameable_type: Unnameable7
+}
+
+pub static STATIC: Struct = Struct { field_of_unnameable_type: Unnameable7 } ;
+
+impl UnnameableTrait for AliasOfUnnameableType {
+    type Alias = Unnameable8;
+}
+
+pub fn generic_function<T: UnnameableTrait>() -> T::Alias {
+    Default::default()
+}
diff --git a/src/test/run-pass/reachable-unnameable-items.rs b/src/test/run-pass/reachable-unnameable-items.rs
new file mode 100644
index 00000000000..88d3f160c81
--- /dev/null
+++ b/src/test/run-pass/reachable-unnameable-items.rs
@@ -0,0 +1,42 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:reachable-unnameable-items.rs
+
+#![feature(braced_empty_structs)]
+#![feature(recover)]
+
+extern crate reachable_unnameable_items;
+use reachable_unnameable_items::*;
+
+fn main() {
+    let res1 = function_returning_unnameable_type().method_of_unnameable_type1();
+    let res2 = CONSTANT_OF_UNNAMEABLE_TYPE.method_of_unnameable_type2();
+    let res4 = AliasOfUnnameableType{}.method_of_unnameable_type4();
+    let res5 = function_returning_unnameable_type().inherent_method_returning_unnameable_type().
+                                                    method_of_unnameable_type5();
+    let res6 = function_returning_unnameable_type().trait_method_returning_unnameable_type().
+                                                    method_of_unnameable_type6();
+    let res7 = STATIC.field_of_unnameable_type.method_of_unnameable_type7();
+    let res8 = generic_function::<AliasOfUnnameableType>().method_of_unnameable_type8();
+    let res_enum = NameableVariant.method_of_unnameable_enum();
+    assert_eq!(res1, "Hello1");
+    assert_eq!(res2, "Hello2");
+    assert_eq!(res4, "Hello4");
+    assert_eq!(res5, "Hello5");
+    assert_eq!(res6, "Hello6");
+    assert_eq!(res7, "Hello7");
+    assert_eq!(res8, "Hello8");
+    assert_eq!(res_enum, "HelloEnum");
+
+    let none = None;
+    function_accepting_unnameable_type(none);
+    let _guard = std::panic::recover(|| none.unwrap().method_of_unnameable_type3());
+}
diff --git a/src/test/run-pass/reachable-unnameable-type-alias.rs b/src/test/run-pass/reachable-unnameable-type-alias.rs
new file mode 100644
index 00000000000..5d0c6df3d58
--- /dev/null
+++ b/src/test/run-pass/reachable-unnameable-type-alias.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(staged_api)]
+#![stable(feature = "a", since = "b")]
+
+mod inner_private_module {
+    // UnnameableTypeAlias isn't marked as reachable, so no stability annotation is required here
+    pub type UnnameableTypeAlias = u8;
+}
+
+#[stable(feature = "a", since = "b")]
+pub fn f() -> inner_private_module::UnnameableTypeAlias {
+    0
+}
+
+fn main() {}