about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-01-10 08:55:53 +0000
committerbors <bors@rust-lang.org>2022-01-10 08:55:53 +0000
commitdf035a33b228daa700ac50712d9e16509d373e41 (patch)
treee98f02a220de459f6b5da0d52985cef02f624af7
parentd63a8d965e76f29a2b65c1f22a32613df1fe5c2c (diff)
parent3a77bb86ff33bd4633aa831f2580aa3792a9c279 (diff)
downloadrust-df035a33b228daa700ac50712d9e16509d373e41.tar.gz
rust-df035a33b228daa700ac50712d9e16509d373e41.zip
Auto merge of #87487 - lambinoo:I-64762_unreachable_pub_lint, r=petrochenkov
Fixes wrong unreachable_pub lints on nested and glob public reexport

Linked issues: #64762 & #82064
-rw-r--r--compiler/rustc_errors/src/snippet.rs3
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_middle/src/middle/privacy.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs220
-rw-r--r--compiler/rustc_resolve/src/access_levels.rs237
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs6
-rw-r--r--compiler/rustc_resolve/src/imports.rs4
-rw-r--r--compiler/rustc_resolve/src/lib.rs13
-rw-r--r--library/alloc/src/collections/btree/map.rs3
-rw-r--r--library/core/src/iter/adapters/mod.rs1
-rw-r--r--library/core/src/iter/sources.rs1
-rw-r--r--library/core/src/iter/traits/mod.rs16
-rw-r--r--library/std/src/io/buffered/mod.rs8
-rw-r--r--library/std/src/io/mod.rs27
-rw-r--r--library/std/src/os/linux/raw.rs1
-rw-r--r--library/std/src/os/unix/io/raw.rs1
-rw-r--r--src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs28
-rw-r--r--src/tools/rustfmt/src/lib.rs1
19 files changed, 359 insertions, 218 deletions
diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs
index 64353461e90..e4cc44c41dd 100644
--- a/compiler/rustc_errors/src/snippet.rs
+++ b/compiler/rustc_errors/src/snippet.rs
@@ -69,9 +69,6 @@ pub enum AnnotationType {
     /// Annotation under a single line of code
     Singleline,
 
-    /// Annotation enclosing the first and last character of a multiline span
-    Multiline(MultilineAnnotation),
-
     // The Multiline type above is replaced with the following three in order
     // to reuse the current label drawing code.
     //
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index c7823032b0c..c6145ae0d51 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -92,7 +92,8 @@ use unused::*;
 
 /// Useful for other parts of the compiler / Clippy.
 pub use builtin::SoftLints;
-pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
+pub use context::{CheckLintNameResult, FindLintError, LintStore};
+pub use context::{EarlyContext, LateContext, LintContext};
 pub use early::check_ast_crate;
 pub use late::check_crate;
 pub use passes::{EarlyLintPass, LateLintPass};
diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs
index f33bd3438b9..ff993ac392c 100644
--- a/compiler/rustc_middle/src/middle/privacy.rs
+++ b/compiler/rustc_middle/src/middle/privacy.rs
@@ -28,7 +28,7 @@ pub enum AccessLevel {
 }
 
 /// Holds a map of accessibility levels for reachable HIR nodes.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct AccessLevels<Id = LocalDefId> {
     pub map: FxHashMap<Id, AccessLevel>,
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f5f55dcf38c..16adf93b69a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -20,6 +20,7 @@ pub use generics::*;
 pub use vtable::*;
 
 use crate::metadata::ModChild;
+use crate::middle::privacy::AccessLevels;
 use crate::mir::{Body, GeneratorLayout};
 use crate::traits::{self, Reveal};
 use crate::ty;
@@ -123,6 +124,7 @@ pub struct ResolverOutputs {
     pub definitions: rustc_hir::definitions::Definitions,
     pub cstore: Box<CrateStoreDyn>,
     pub visibilities: FxHashMap<LocalDefId, Visibility>,
+    pub access_levels: AccessLevels,
     pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
     pub maybe_unused_trait_imports: FxHashSet<LocalDefId>,
     pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 16418e627f2..e93cd813db5 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -11,8 +11,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet};
-use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
 use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor};
 use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind};
 use rustc_middle::bug;
@@ -26,7 +25,7 @@ use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable};
 use rustc_session::lint;
 use rustc_span::hygiene::Transparency;
-use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::Span;
 use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst};
 
@@ -436,6 +435,15 @@ impl<'tcx> EmbargoVisitor<'tcx> {
         self.access_levels.map.get(&def_id).copied()
     }
 
+    fn update_with_hir_id(
+        &mut self,
+        hir_id: hir::HirId,
+        level: Option<AccessLevel>,
+    ) -> Option<AccessLevel> {
+        let def_id = self.tcx.hir().local_def_id(hir_id);
+        self.update(def_id, level)
+    }
+
     /// Updates node level and returns the updated level.
     fn update(&mut self, def_id: LocalDefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
         let old_level = self.get(def_id);
@@ -623,54 +631,6 @@ impl<'tcx> EmbargoVisitor<'tcx> {
             | DefKind::Generator => (),
         }
     }
-
-    /// Given the path segments of an `ItemKind::Use`, then we need
-    /// to update the visibility of the intermediate use so that it isn't linted
-    /// by `unreachable_pub`.
-    ///
-    /// This isn't trivial as `path.res` has the `DefId` of the eventual target
-    /// of the use statement not of the next intermediate use statement.
-    ///
-    /// To do this, consider the last two segments of the path to our intermediate
-    /// use statement. We expect the penultimate segment to be a module and the
-    /// last segment to be the name of the item we are exporting. We can then
-    /// look at the items contained in the module for the use statement with that
-    /// name and update that item's visibility.
-    ///
-    /// FIXME: This solution won't work with glob imports and doesn't respect
-    /// namespaces. See <https://github.com/rust-lang/rust/pull/57922#discussion_r251234202>.
-    fn update_visibility_of_intermediate_use_statements(
-        &mut self,
-        segments: &[hir::PathSegment<'_>],
-    ) {
-        if let [.., module, segment] = segments {
-            if let Some(item) = module
-                .res
-                .and_then(|res| res.mod_def_id())
-                // If the module is `self`, i.e. the current crate,
-                // there will be no corresponding item.
-                .filter(|def_id| def_id.index != CRATE_DEF_INDEX || def_id.krate != LOCAL_CRATE)
-                .and_then(|def_id| def_id.as_local())
-                .map(|module_hir_id| self.tcx.hir().expect_item(module_hir_id))
-            {
-                if let hir::ItemKind::Mod(m) = &item.kind {
-                    for &item_id in m.item_ids {
-                        let item = self.tcx.hir().item(item_id);
-                        if !self.tcx.hygienic_eq(
-                            segment.ident,
-                            item.ident,
-                            item_id.def_id.to_def_id(),
-                        ) {
-                            continue;
-                        }
-                        if let hir::ItemKind::Use(..) = item.kind {
-                            self.update(item.def_id, Some(AccessLevel::Exported));
-                        }
-                    }
-                }
-            }
-        }
-    }
 }
 
 impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
@@ -683,120 +643,22 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
     }
 
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
-        let inherited_item_level = match item.kind {
+        let item_level = match item.kind {
             hir::ItemKind::Impl { .. } => {
-                Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels)
-            }
-            // Only exported `macro_rules!` items are public, but they always are.
-            hir::ItemKind::Macro(MacroDef { macro_rules: true, .. }) => {
-                let def_id = item.def_id.to_def_id();
-                let is_macro_export = self.tcx.has_attr(def_id, sym::macro_export);
-                if is_macro_export { Some(AccessLevel::Public) } else { None }
-            }
-            // Foreign modules inherit level from parents.
-            hir::ItemKind::ForeignMod { .. } => self.prev_level,
-            // Other `pub` items inherit levels from parents.
-            hir::ItemKind::Const(..)
-            | hir::ItemKind::Enum(..)
-            | hir::ItemKind::ExternCrate(..)
-            | hir::ItemKind::GlobalAsm(..)
-            | hir::ItemKind::Fn(..)
-            | hir::ItemKind::Macro(..)
-            | hir::ItemKind::Mod(..)
-            | hir::ItemKind::Static(..)
-            | hir::ItemKind::Struct(..)
-            | hir::ItemKind::Trait(..)
-            | hir::ItemKind::TraitAlias(..)
-            | hir::ItemKind::OpaqueTy(..)
-            | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::Union(..)
-            | hir::ItemKind::Use(..) => {
-                if item.vis.node.is_pub() {
-                    self.prev_level
-                } else {
-                    None
-                }
+                let impl_level =
+                    Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels);
+                self.update(item.def_id, impl_level)
             }
+            _ => self.get(item.def_id),
         };
 
-        // Update level of the item itself.
-        let item_level = self.update(item.def_id, inherited_item_level);
-
-        // Update levels of nested things.
-        match item.kind {
-            hir::ItemKind::Enum(ref def, _) => {
-                for variant in def.variants {
-                    let variant_level =
-                        self.update(self.tcx.hir().local_def_id(variant.id), item_level);
-                    if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
-                        self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level);
-                    }
-                    for field in variant.data.fields() {
-                        self.update(self.tcx.hir().local_def_id(field.hir_id), variant_level);
-                    }
-                }
-            }
-            hir::ItemKind::Impl(ref impl_) => {
-                for impl_item_ref in impl_.items {
-                    if impl_.of_trait.is_some()
-                        || self.tcx.visibility(impl_item_ref.id.def_id) == ty::Visibility::Public
-                    {
-                        self.update(impl_item_ref.id.def_id, item_level);
-                    }
-                }
-            }
-            hir::ItemKind::Trait(.., trait_item_refs) => {
-                for trait_item_ref in trait_item_refs {
-                    self.update(trait_item_ref.id.def_id, item_level);
-                }
-            }
-            hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
-                if let Some(ctor_hir_id) = def.ctor_hir_id() {
-                    self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level);
-                }
-                for field in def.fields() {
-                    if field.vis.node.is_pub() {
-                        self.update(self.tcx.hir().local_def_id(field.hir_id), item_level);
-                    }
-                }
-            }
-            hir::ItemKind::Macro(ref macro_def) => {
-                self.update_reachability_from_macro(item.def_id, macro_def);
-            }
-            hir::ItemKind::ForeignMod { items, .. } => {
-                for foreign_item in items {
-                    if self.tcx.visibility(foreign_item.id.def_id) == ty::Visibility::Public {
-                        self.update(foreign_item.id.def_id, item_level);
-                    }
-                }
-            }
-
-            hir::ItemKind::OpaqueTy(..)
-            | hir::ItemKind::Use(..)
-            | hir::ItemKind::Static(..)
-            | hir::ItemKind::Const(..)
-            | hir::ItemKind::GlobalAsm(..)
-            | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::Mod(..)
-            | hir::ItemKind::TraitAlias(..)
-            | hir::ItemKind::Fn(..)
-            | hir::ItemKind::ExternCrate(..) => {}
-        }
-
         // Mark all items in interfaces of reachable items as reachable.
         match item.kind {
             // The interface is empty.
-            hir::ItemKind::Macro(..) | hir::ItemKind::ExternCrate(..) => {}
+            hir::ItemKind::ExternCrate(..) => {}
             // All nested items are checked by `visit_item`.
             hir::ItemKind::Mod(..) => {}
-            // Re-exports are handled in `visit_mod`. However, in order to avoid looping over
-            // all of the items of a mod in `visit_mod` looking for use statements, we handle
-            // making sure that intermediate use statements have their visibilities updated here.
-            hir::ItemKind::Use(path, _) => {
-                if item_level.is_some() {
-                    self.update_visibility_of_intermediate_use_statements(path.segments.as_ref());
-                }
-            }
+            hir::ItemKind::Use(..) => {}
             // The interface is empty.
             hir::ItemKind::GlobalAsm(..) => {}
             hir::ItemKind::OpaqueTy(..) => {
@@ -847,6 +709,14 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
             }
             // Visit everything except for private impl items.
             hir::ItemKind::Impl(ref impl_) => {
+                for impl_item_ref in impl_.items {
+                    if impl_.of_trait.is_some()
+                        || self.tcx.visibility(impl_item_ref.id.def_id) == ty::Visibility::Public
+                    {
+                        self.update(impl_item_ref.id.def_id, item_level);
+                    }
+                }
+
                 if item_level.is_some() {
                     self.reach(item.def_id, item_level).generics().predicates().ty().trait_ref();
 
@@ -861,15 +731,21 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     }
                 }
             }
-
             // Visit everything, but enum variants have their own levels.
             hir::ItemKind::Enum(ref def, _) => {
                 if item_level.is_some() {
                     self.reach(item.def_id, item_level).generics().predicates();
                 }
+
+                let enum_level = self.get(item.def_id);
                 for variant in def.variants {
-                    let variant_level = self.get(self.tcx.hir().local_def_id(variant.id));
+                    let variant_level = self.update_with_hir_id(variant.id, enum_level);
+
                     if variant_level.is_some() {
+                        if let Some(ctor_id) = variant.data.ctor_hir_id() {
+                            self.update_with_hir_id(ctor_id, variant_level);
+                        }
+
                         for field in variant.data.fields() {
                             self.reach(self.tcx.hir().local_def_id(field.hir_id), variant_level)
                                 .ty();
@@ -880,6 +756,9 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     }
                 }
             }
+            hir::ItemKind::Macro(ref macro_def) => {
+                self.update_reachability_from_macro(item.def_id, macro_def);
+            }
             // Visit everything, but foreign items have their own levels.
             hir::ItemKind::ForeignMod { items, .. } => {
                 for foreign_item in items {
@@ -920,27 +799,6 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
         intravisit::walk_block(self, b);
         self.prev_level = orig_level;
     }
-
-    fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _sp: Span, id: hir::HirId) {
-        // This code is here instead of in visit_item so that the
-        // crate module gets processed as well.
-        if self.prev_level.is_some() {
-            let def_id = self.tcx.hir().local_def_id(id);
-            if let Some(exports) = self.tcx.module_reexports(def_id) {
-                for export in exports.iter() {
-                    if export.vis.is_public() {
-                        if let Some(def_id) = export.res.opt_def_id() {
-                            if let Some(def_id) = def_id.as_local() {
-                                self.update(def_id, Some(AccessLevel::Exported));
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        intravisit::walk_mod(self, m, id);
-    }
 }
 
 impl ReachEverythingInTheInterfaceVisitor<'_, '_> {
@@ -2166,11 +2024,12 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels {
     // items which are reachable from external crates based on visibility.
     let mut visitor = EmbargoVisitor {
         tcx,
-        access_levels: Default::default(),
+        access_levels: tcx.resolutions(()).access_levels.clone(),
         macro_reachable: Default::default(),
         prev_level: Some(AccessLevel::Public),
         changed: false,
     };
+
     loop {
         tcx.hir().walk_toplevel_module(&mut visitor);
         if visitor.changed {
@@ -2179,7 +2038,6 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels {
             break;
         }
     }
-    visitor.update(CRATE_DEF_ID, Some(AccessLevel::Public));
 
     tcx.arena.alloc(visitor.access_levels)
 }
diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs
new file mode 100644
index 00000000000..60cc4248edc
--- /dev/null
+++ b/compiler/rustc_resolve/src/access_levels.rs
@@ -0,0 +1,237 @@
+use rustc_ast::ast;
+use rustc_ast::visit;
+use rustc_ast::visit::Visitor;
+use rustc_ast::Crate;
+use rustc_ast::EnumDef;
+use rustc_ast::ForeignMod;
+use rustc_ast::NodeId;
+use rustc_ast_lowering::ResolverAstLowering;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::CRATE_DEF_ID;
+use rustc_middle::middle::privacy::AccessLevel;
+use rustc_middle::ty::Visibility;
+use rustc_span::sym;
+
+use crate::imports::ImportKind;
+use crate::BindingKey;
+use crate::NameBinding;
+use crate::NameBindingKind;
+use crate::Resolver;
+
+pub struct AccessLevelsVisitor<'r, 'a> {
+    r: &'r mut Resolver<'a>,
+    prev_level: Option<AccessLevel>,
+    changed: bool,
+}
+
+impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
+    /// Fills the `Resolver::access_levels` table with public & exported items
+    /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
+    /// need access to a TyCtxt for that.
+    pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
+        let mut visitor =
+            AccessLevelsVisitor { r, changed: false, prev_level: Some(AccessLevel::Public) };
+
+        visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public));
+        visitor.set_exports_access_level(CRATE_DEF_ID);
+
+        while visitor.changed {
+            visitor.reset();
+            visit::walk_crate(&mut visitor, krate);
+        }
+
+        tracing::info!("resolve::access_levels: {:#?}", r.access_levels);
+    }
+
+    fn reset(&mut self) {
+        self.changed = false;
+        self.prev_level = Some(AccessLevel::Public);
+    }
+
+    /// Update the access level of the exports of the given module accordingly. The module access
+    /// level has to be Exported or Public.
+    /// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level).
+    fn set_exports_access_level(&mut self, module_id: LocalDefId) {
+        assert!(self.r.module_map.contains_key(&&module_id.to_def_id()));
+
+        // Set the given binding access level to `AccessLevel::Public` and
+        // sets the rest of the `use` chain to `AccessLevel::Exported` until
+        // we hit the actual exported item.
+        let set_import_binding_access_level =
+            |this: &mut Self, mut binding: &NameBinding<'a>, mut access_level| {
+                while let NameBindingKind::Import { binding: nested_binding, import, .. } =
+                    binding.kind
+                {
+                    this.set_access_level(import.id, access_level);
+                    if let ImportKind::Single { additional_ids, .. } = import.kind {
+                        this.set_access_level(additional_ids.0, access_level);
+                        this.set_access_level(additional_ids.1, access_level);
+                    }
+
+                    access_level = Some(AccessLevel::Exported);
+                    binding = nested_binding;
+                }
+            };
+
+        let module_level = self.r.access_levels.map.get(&module_id).copied();
+        assert!(module_level >= Some(AccessLevel::Exported));
+
+        if let Some(exports) = self.r.reexport_map.get(&module_id) {
+            let pub_exports = exports
+                .iter()
+                .filter(|ex| ex.vis == Visibility::Public)
+                .cloned()
+                .collect::<Vec<_>>();
+
+            let module = self.r.get_module(module_id.to_def_id()).unwrap();
+            for export in pub_exports.into_iter() {
+                if let Some(export_def_id) = export.res.opt_def_id().and_then(|id| id.as_local()) {
+                    self.set_access_level_def_id(export_def_id, Some(AccessLevel::Exported));
+                }
+
+                if let Some(ns) = export.res.ns() {
+                    let key = BindingKey { ident: export.ident, ns, disambiguator: 0 };
+                    let name_res = self.r.resolution(module, key);
+                    if let Some(binding) = name_res.borrow().binding() {
+                        set_import_binding_access_level(self, binding, module_level)
+                    }
+                }
+            }
+        }
+    }
+
+    /// Sets the access level of the `LocalDefId` corresponding to the given `NodeId`.
+    /// This function will panic if the `NodeId` does not have a `LocalDefId`
+    fn set_access_level(
+        &mut self,
+        node_id: NodeId,
+        access_level: Option<AccessLevel>,
+    ) -> Option<AccessLevel> {
+        self.set_access_level_def_id(self.r.local_def_id(node_id), access_level)
+    }
+
+    fn set_access_level_def_id(
+        &mut self,
+        def_id: LocalDefId,
+        access_level: Option<AccessLevel>,
+    ) -> Option<AccessLevel> {
+        let old_level = self.r.access_levels.map.get(&def_id).copied();
+        if old_level < access_level {
+            self.r.access_levels.map.insert(def_id, access_level.unwrap());
+            self.changed = true;
+            access_level
+        } else {
+            old_level
+        }
+    }
+}
+
+impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
+    fn visit_item(&mut self, item: &'ast ast::Item) {
+        let inherited_item_level = match item.kind {
+            // Resolved in rustc_privacy when types are available
+            ast::ItemKind::Impl(..) => return,
+
+            // Only exported `macro_rules!` items are public, but they always are
+            ast::ItemKind::MacroDef(..) => {
+                let is_macro_export =
+                    item.attrs.iter().any(|attr| attr.has_name(sym::macro_export));
+                if is_macro_export { Some(AccessLevel::Public) } else { None }
+            }
+
+            // Foreign modules inherit level from parents.
+            ast::ItemKind::ForeignMod(..) => self.prev_level,
+
+            // Other `pub` items inherit levels from parents.
+            ast::ItemKind::ExternCrate(..)
+            | ast::ItemKind::Use(..)
+            | ast::ItemKind::Static(..)
+            | ast::ItemKind::Const(..)
+            | ast::ItemKind::Fn(..)
+            | ast::ItemKind::Mod(..)
+            | ast::ItemKind::GlobalAsm(..)
+            | ast::ItemKind::TyAlias(..)
+            | ast::ItemKind::Enum(..)
+            | ast::ItemKind::Struct(..)
+            | ast::ItemKind::Union(..)
+            | ast::ItemKind::Trait(..)
+            | ast::ItemKind::TraitAlias(..) => {
+                if item.vis.kind.is_pub() {
+                    self.prev_level
+                } else {
+                    None
+                }
+            }
+
+            // Should be unreachable at this stage
+            ast::ItemKind::MacCall(..) => panic!(
+                "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
+            ),
+        };
+
+        let access_level = self.set_access_level(item.id, inherited_item_level);
+
+        // Set access level of nested items.
+        // If it's a mod, also make the visitor walk all of its items
+        match item.kind {
+            ast::ItemKind::Mod(..) => {
+                if access_level.is_some() {
+                    self.set_exports_access_level(self.r.local_def_id(item.id));
+                }
+
+                let orig_level = std::mem::replace(&mut self.prev_level, access_level);
+                visit::walk_item(self, item);
+                self.prev_level = orig_level;
+            }
+
+            ast::ItemKind::ForeignMod(ForeignMod { ref items, .. }) => {
+                for nested in items {
+                    if nested.vis.kind.is_pub() {
+                        self.set_access_level(nested.id, access_level);
+                    }
+                }
+            }
+            ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
+                for variant in variants {
+                    let variant_level = self.set_access_level(variant.id, access_level);
+                    if let Some(ctor_id) = variant.data.ctor_id() {
+                        self.set_access_level(ctor_id, access_level);
+                    }
+
+                    for field in variant.data.fields() {
+                        self.set_access_level(field.id, variant_level);
+                    }
+                }
+            }
+            ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
+                if let Some(ctor_id) = def.ctor_id() {
+                    self.set_access_level(ctor_id, access_level);
+                }
+
+                for field in def.fields() {
+                    if field.vis.kind.is_pub() {
+                        self.set_access_level(field.id, access_level);
+                    }
+                }
+            }
+            ast::ItemKind::Trait(ref trait_kind) => {
+                for nested in trait_kind.items.iter() {
+                    self.set_access_level(nested.id, access_level);
+                }
+            }
+
+            ast::ItemKind::ExternCrate(..)
+            | ast::ItemKind::Use(..)
+            | ast::ItemKind::Static(..)
+            | ast::ItemKind::Const(..)
+            | ast::ItemKind::GlobalAsm(..)
+            | ast::ItemKind::TyAlias(..)
+            | ast::ItemKind::TraitAlias(..)
+            | ast::ItemKind::MacroDef(..)
+            | ast::ItemKind::Fn(..) => return,
+
+            // Unreachable kinds
+            ast::ItemKind::Impl(..) | ast::ItemKind::MacCall(..) => unreachable!(),
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 944e7185184..052770b201a 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -383,8 +383,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             used: Cell::new(false),
         });
 
-        debug!("add_import({:?})", import);
-
         self.r.indeterminate_imports.push(import);
         match import.kind {
             // Don't add unresolved underscore imports to modules
@@ -455,7 +453,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot
         };
         match use_tree.kind {
-            ast::UseTreeKind::Simple(rename, ..) => {
+            ast::UseTreeKind::Simple(rename, id1, id2) => {
                 let mut ident = use_tree.ident();
                 let mut module_path = prefix;
                 let mut source = module_path.pop().unwrap();
@@ -565,7 +563,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     },
                     type_ns_only,
                     nested,
+                    additional_ids: (id1, id2),
                 };
+
                 self.add_import(
                     module_path,
                     kind,
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 2832f59a5ef..e7f76a18ad3 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -48,6 +48,9 @@ pub enum ImportKind<'a> {
         type_ns_only: bool,
         /// Did this import result from a nested import? ie. `use foo::{bar, baz};`
         nested: bool,
+        /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement
+        /// (eg. implicit struct constructors)
+        additional_ids: (NodeId, NodeId),
     },
     Glob {
         is_prelude: bool,
@@ -834,7 +837,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         import.span,
                     );
                     import.vis.set(orig_vis);
-
                     source_bindings[ns].set(binding);
                 } else {
                     return;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 37be0e228d2..28437742758 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -50,6 +50,7 @@ use rustc_hir::TraitCandidate;
 use rustc_index::vec::IndexVec;
 use rustc_metadata::creader::{CStore, CrateLoader};
 use rustc_middle::metadata::ModChild;
+use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::span_bug;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs};
@@ -77,8 +78,11 @@ use imports::{Import, ImportKind, ImportResolver, NameResolution};
 use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*};
 use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 
+use crate::access_levels::AccessLevelsVisitor;
+
 type Res = def::Res<NodeId>;
 
+mod access_levels;
 mod build_reduced_graph;
 mod check_unused;
 mod def_collector;
@@ -1052,6 +1056,8 @@ pub struct Resolver<'a> {
     /// they are declared in the static array generated by proc_macro_harness.
     proc_macros: Vec<NodeId>,
     confused_type_with_std_module: FxHashMap<Span, Span>,
+
+    access_levels: AccessLevels,
 }
 
 /// Nothing really interesting here; it just provides memory for the rest of the crate.
@@ -1407,6 +1413,7 @@ impl<'a> Resolver<'a> {
             trait_impls: Default::default(),
             proc_macros: Default::default(),
             confused_type_with_std_module: Default::default(),
+            access_levels: Default::default(),
         };
 
         let root_parent_scope = ParentScope::module(graph_root, &resolver);
@@ -1452,10 +1459,12 @@ impl<'a> Resolver<'a> {
         let glob_map = self.glob_map;
         let main_def = self.main_def;
         let confused_type_with_std_module = self.confused_type_with_std_module;
+        let access_levels = self.access_levels;
         ResolverOutputs {
             definitions,
             cstore: Box::new(self.crate_loader.into_cstore()),
             visibilities,
+            access_levels,
             extern_crate_map,
             reexport_map,
             glob_map,
@@ -1477,6 +1486,7 @@ impl<'a> Resolver<'a> {
         let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
         ResolverOutputs {
             definitions: self.definitions.clone(),
+            access_levels: self.access_levels.clone(),
             cstore: Box::new(self.cstore().clone()),
             visibilities: self.visibilities.clone(),
             extern_crate_map: self.extern_crate_map.clone(),
@@ -1532,6 +1542,9 @@ impl<'a> Resolver<'a> {
     pub fn resolve_crate(&mut self, krate: &Crate) {
         self.session.time("resolve_crate", || {
             self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports());
+            self.session.time("resolve_access_levels", || {
+                AccessLevelsVisitor::compute_access_levels(self, krate)
+            });
             self.session.time("finalize_macro_resolutions", || self.finalize_macro_resolutions());
             self.session.time("late_resolve_crate", || self.late_resolve_crate(krate));
             self.session.time("resolve_main", || self.resolve_main());
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 199c05dc5df..62153efbb39 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -16,7 +16,10 @@ use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root};
 use super::search::SearchResult::*;
 
 mod entry;
+
+#[stable(feature = "rust1", since = "1.0.0")]
 pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry};
+
 use Entry::*;
 
 /// Minimum number of elements in a node that is not a root.
diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs
index b1b917775c3..db8776ac741 100644
--- a/library/core/src/iter/adapters/mod.rs
+++ b/library/core/src/iter/adapters/mod.rs
@@ -24,6 +24,7 @@ mod take;
 mod take_while;
 mod zip;
 
+#[stable(feature = "rust1", since = "1.0.0")]
 pub use self::{
     chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap,
     flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev,
diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs
index de0663141e2..37b6f2e2565 100644
--- a/library/core/src/iter/sources.rs
+++ b/library/core/src/iter/sources.rs
@@ -6,6 +6,7 @@ mod repeat;
 mod repeat_with;
 mod successors;
 
+#[stable(feature = "rust1", since = "1.0.0")]
 pub use self::repeat::{repeat, Repeat};
 
 #[stable(feature = "iter_empty", since = "1.2.0")]
diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs
index ffd745a46b1..ed0fb634dbf 100644
--- a/library/core/src/iter/traits/mod.rs
+++ b/library/core/src/iter/traits/mod.rs
@@ -5,15 +5,17 @@ mod exact_size;
 mod iterator;
 mod marker;
 
-pub use self::accum::{Product, Sum};
-pub use self::collect::{Extend, FromIterator, IntoIterator};
-pub use self::double_ended::DoubleEndedIterator;
-pub use self::exact_size::ExactSizeIterator;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::iterator::Iterator;
+pub use self::{
+    accum::{Product, Sum},
+    collect::{Extend, FromIterator, IntoIterator},
+    double_ended::DoubleEndedIterator,
+    exact_size::ExactSizeIterator,
+    iterator::Iterator,
+    marker::{FusedIterator, TrustedLen},
+};
+
 #[unstable(issue = "none", feature = "inplace_iteration")]
 pub use self::marker::InPlaceIterable;
 #[unstable(feature = "trusted_step", issue = "85731")]
 pub use self::marker::TrustedStep;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::marker::{FusedIterator, TrustedLen};
diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs
index 179bdf7fe55..100dab1e249 100644
--- a/library/std/src/io/buffered/mod.rs
+++ b/library/std/src/io/buffered/mod.rs
@@ -12,12 +12,12 @@ use crate::error;
 use crate::fmt;
 use crate::io::Error;
 
-pub use bufreader::BufReader;
-pub use bufwriter::BufWriter;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter};
+use linewritershim::LineWriterShim;
+
 #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
 pub use bufwriter::WriterPanicked;
-pub use linewriter::LineWriter;
-use linewritershim::LineWriterShim;
 
 /// An error returned by [`BufWriter::into_inner`] which combines an error that
 /// happened while writing out the buffer, and the buffered writer object
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index ecc9e91b6bd..358ef22e708 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -261,31 +261,24 @@ use crate::str;
 use crate::sys;
 use crate::sys_common::memchr;
 
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::buffered::IntoInnerError;
 #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
 pub use self::buffered::WriterPanicked;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::buffered::{BufReader, BufWriter, LineWriter};
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::copy::copy;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::cursor::Cursor;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::error::{Error, ErrorKind, Result};
 #[unstable(feature = "internal_output_capture", issue = "none")]
 #[doc(no_inline, hidden)]
 pub use self::stdio::set_output_capture;
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
-#[unstable(feature = "stdio_locked", issue = "86845")]
-pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked};
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
 #[unstable(feature = "print_internals", issue = "none")]
 pub use self::stdio::{_eprint, _print};
+#[unstable(feature = "stdio_locked", issue = "86845")]
+pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink};
+pub use self::{
+    buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},
+    copy::copy,
+    cursor::Cursor,
+    error::{Error, ErrorKind, Result},
+    stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock},
+    util::{empty, repeat, sink, Empty, Repeat, Sink},
+};
 
 #[unstable(feature = "read_buf", issue = "78485")]
 pub use self::readbuf::ReadBuf;
diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs
index cd92dcabdf5..d78049bce24 100644
--- a/library/std/src/os/linux/raw.rs
+++ b/library/std/src/os/linux/raw.rs
@@ -239,6 +239,7 @@ mod arch {
     target_arch = "riscv32"
 ))]
 mod arch {
+    #[stable(feature = "raw_ext", since = "1.1.0")]
     pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t};
 }
 
diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs
index 6317e317471..a4d2ba797d9 100644
--- a/library/std/src/os/unix/io/raw.rs
+++ b/library/std/src/os/unix/io/raw.rs
@@ -2,4 +2,5 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+#[stable(feature = "rust1", since = "1.0.0")]
 pub use crate::os::fd::raw::*;
diff --git a/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs b/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs
new file mode 100644
index 00000000000..2df6d08e7ae
--- /dev/null
+++ b/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs
@@ -0,0 +1,28 @@
+// check-pass
+
+#![deny(unreachable_pub)]
+
+pub use self::m1::*;
+
+mod m1 {
+    pub use self::m2::*;
+
+    mod m2 {
+        pub struct Item1;
+        pub struct Item2;
+    }
+}
+
+
+pub use self::o1::{ Item42, Item24 };
+
+mod o1 {
+    pub use self::o2::{ Item42, Item24 };
+
+    mod o2 {
+        pub struct Item42;
+        pub struct Item24;
+    }
+}
+
+fn main() {}
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index ad23b16e02e..fae8080c02e 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -3,6 +3,7 @@
 #![warn(unreachable_pub)]
 #![recursion_limit = "256"]
 #![allow(clippy::match_like_matches_macro)]
+#![allow(unreachable_pub)]
 
 #[macro_use]
 extern crate derive_new;