diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 175c7facfdbeb..b3b89e6e673a2 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -41,6 +41,7 @@ type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>;
 crate fn try_inline(
     cx: &mut DocContext<'_>,
     parent_module: DefId,
+    import_def_id: Option<DefId>,
     res: Res,
     name: Symbol,
     attrs: Option<Attrs<'_>>,
@@ -108,7 +109,7 @@ crate fn try_inline(
             clean::ConstantItem(build_const(cx, did))
         }
         Res::Def(DefKind::Macro(kind), did) => {
-            let mac = build_macro(cx, did, name);
+            let mac = build_macro(cx, did, name, import_def_id);
 
             let type_kind = match kind {
                 MacroKind::Bang => ItemType::Macro,
@@ -123,14 +124,13 @@ crate fn try_inline(
 
     let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone);
     cx.inlined.insert(did.into());
-    ret.push(clean::Item::from_def_id_and_attrs_and_parts(
-        did,
-        Some(name),
-        kind,
-        box attrs,
-        cx,
-        cfg,
-    ));
+    let mut item =
+        clean::Item::from_def_id_and_attrs_and_parts(did, Some(name), kind, box attrs, cx, cfg);
+    if let Some(import_def_id) = import_def_id {
+        // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
+        item.visibility = cx.tcx.visibility(import_def_id).clean(cx);
+    }
+    ret.push(item);
     Some(ret)
 }
 
@@ -509,7 +509,9 @@ fn build_module(
                     )),
                     cfg: None,
                 });
-            } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) {
+            } else if let Some(i) =
+                try_inline(cx, did, None, item.res, item.ident.name, None, visited)
+            {
                 items.extend(i)
             }
         }
@@ -543,21 +545,26 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St
     }
 }
 
-fn build_macro(cx: &mut DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind {
-    let imported_from = cx.tcx.crate_name(did.krate);
-    match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
-        LoadedMacro::MacroDef(def, _) => {
-            if let ast::ItemKind::MacroDef(ref def) = def.kind {
-                let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
-                let matchers = tts.chunks(4).map(|arm| &arm[0]);
-
-                let source = format!(
-                    "macro_rules! {} {{\n{}}}",
-                    name,
-                    utils::render_macro_arms(matchers, ";")
-                );
-
-                clean::MacroItem(clean::Macro { source, imported_from: Some(imported_from) })
+fn build_macro(
+    cx: &mut DocContext<'_>,
+    def_id: DefId,
+    name: Symbol,
+    import_def_id: Option<DefId>,
+) -> clean::ItemKind {
+    let imported_from = cx.tcx.crate_name(def_id.krate);
+    match cx.enter_resolver(|r| r.cstore().load_macro_untracked(def_id, cx.sess())) {
+        LoadedMacro::MacroDef(item_def, _) => {
+            if let ast::ItemKind::MacroDef(ref def) = item_def.kind {
+                clean::MacroItem(clean::Macro {
+                    source: utils::display_macro_source(
+                        cx,
+                        name,
+                        def,
+                        def_id,
+                        cx.tcx.visibility(import_def_id.unwrap_or(def_id)),
+                    ),
+                    imported_from: Some(imported_from),
+                })
             } else {
                 unreachable!()
             }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index cc086427dd0cd..0ff708d01f5e7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1997,6 +1997,7 @@ fn clean_extern_crate(
         if let Some(items) = inline::try_inline(
             cx,
             cx.tcx.parent_module(krate.hir_id()).to_def_id(),
+            Some(krate.def_id.to_def_id()),
             res,
             name,
             Some(attrs),
@@ -2052,7 +2053,8 @@ fn clean_use_statement(
     // forcefully don't inline if this is not public or if the
     // #[doc(no_inline)] attribute is present.
     // Don't inline doc(hidden) imports so they can be stripped at a later stage.
-    let mut denied = !import.vis.node.is_pub()
+    let mut denied = !(import.vis.node.is_pub()
+        || (cx.render_options.document_private && import.vis.node.is_pub_restricted()))
         || pub_underscore
         || attrs.iter().any(|a| {
             a.has_name(sym::doc)
@@ -2088,17 +2090,19 @@ fn clean_use_statement(
         }
         if !denied {
             let mut visited = FxHashSet::default();
+            let import_def_id = import.def_id.to_def_id();
 
             if let Some(mut items) = inline::try_inline(
                 cx,
                 cx.tcx.parent_module(import.hir_id()).to_def_id(),
+                Some(import_def_id),
                 path.res,
                 name,
                 Some(attrs),
                 &mut visited,
             ) {
                 items.push(Item::from_def_id_and_parts(
-                    import.def_id.to_def_id(),
+                    import_def_id,
                     None,
                     ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)),
                     cx,
@@ -2157,37 +2161,15 @@ impl Clean<Item> for (&hir::MacroDef<'_>, Option<Symbol>) {
     fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         let (item, renamed) = self;
         let name = renamed.unwrap_or(item.ident.name);
-        let tts = item.ast.body.inner_tokens().trees().collect::<Vec<_>>();
-        // Extract the macro's matchers. They represent the "interface" of the macro.
-        let matchers = tts.chunks(4).map(|arm| &arm[0]);
-
-        let source = if item.ast.macro_rules {
-            format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(matchers, ";"))
-        } else {
-            let vis = item.vis.clean(cx);
-            let def_id = item.def_id.to_def_id();
-
-            if matchers.len() <= 1 {
-                format!(
-                    "{}macro {}{} {{\n    ...\n}}",
-                    vis.to_src_with_space(cx.tcx, def_id),
-                    name,
-                    matchers.map(render_macro_matcher).collect::<String>(),
-                )
-            } else {
-                format!(
-                    "{}macro {} {{\n{}}}",
-                    vis.to_src_with_space(cx.tcx, def_id),
-                    name,
-                    render_macro_arms(matchers, ","),
-                )
-            }
-        };
+        let def_id = item.def_id.to_def_id();
 
         Item::from_hir_id_and_parts(
             item.hir_id(),
             Some(name),
-            MacroItem(Macro { source, imported_from: None }),
+            MacroItem(Macro {
+                source: display_macro_source(cx, name, &item.ast, def_id, &item.vis),
+                imported_from: None,
+            }),
             cx,
         )
     }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index fb46b81102caa..7ae602c8033f8 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -3,10 +3,12 @@ use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
     inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
     Path, PathSegment, PolyTrait, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
+    Visibility,
 };
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
 
+use rustc_ast as ast;
 use rustc_ast::tokenstream::TokenTree;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -577,3 +579,37 @@ pub(super) fn render_macro_arms<'a>(
 pub(super) fn render_macro_matcher(matcher: &TokenTree) -> String {
     rustc_ast_pretty::pprust::tt_to_string(matcher)
 }
+
+pub(super) fn display_macro_source(
+    cx: &mut DocContext<'_>,
+    name: Symbol,
+    def: &ast::MacroDef,
+    def_id: DefId,
+    vis: impl Clean<Visibility>,
+) -> String {
+    let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
+    // Extract the spans of all matchers. They represent the "interface" of the macro.
+    let matchers = tts.chunks(4).map(|arm| &arm[0]);
+
+    if def.macro_rules {
+        format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(matchers, ";"))
+    } else {
+        let vis = vis.clean(cx);
+
+        if matchers.len() <= 1 {
+            format!(
+                "{}macro {}{} {{\n    ...\n}}",
+                vis.to_src_with_space(cx.tcx, def_id),
+                name,
+                matchers.map(render_macro_matcher).collect::<String>(),
+            )
+        } else {
+            format!(
+                "{}macro {} {{\n{}}}",
+                vis.to_src_with_space(cx.tcx, def_id),
+                name,
+                render_macro_arms(matchers, ","),
+            )
+        }
+    }
+}
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index eeac9d1a9db76..f7073a8751fa1 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -133,7 +133,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer,
         clean::StructItem(ref s) => item_struct(buf, cx, item, s),
         clean::UnionItem(ref s) => item_union(buf, cx, item, s),
         clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
-        clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t),
+        clean::TypedefItem(ref t, is_associated) => item_typedef(buf, cx, item, t, is_associated),
         clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
         clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
         clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
@@ -837,9 +837,18 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:
     render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
 }
 
-fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+fn item_typedef(
+    w: &mut Buffer,
+    cx: &Context<'_>,
+    it: &clean::Item,
+    t: &clean::Typedef,
+    is_associated: bool,
+) {
     w.write_str("<pre class=\"rust typedef\">");
     render_attributes_in_pre(w, it, "");
+    if !is_associated {
+        write!(w, "{}", it.visibility.print_with_space(it.def_id, cx));
+    }
     write!(
         w,
         "type {}{}{where_clause} = {type_};</pre>",
diff --git a/src/test/rustdoc/auxiliary/reexports.rs b/src/test/rustdoc/auxiliary/reexports.rs
new file mode 100644
index 0000000000000..e04b786a864a1
--- /dev/null
+++ b/src/test/rustdoc/auxiliary/reexports.rs
@@ -0,0 +1,42 @@
+#![feature(decl_macro)]
+
+pub macro addr_of($place:expr) {
+    &raw const $place
+}
+
+pub macro addr_of_self($place:expr) {
+    &raw const $place
+}
+
+pub macro addr_of_crate($place:expr) {
+    &raw const $place
+}
+
+pub struct Foo;
+pub struct FooSelf;
+pub struct FooCrate;
+
+pub enum Bar { Foo, }
+pub enum BarSelf { Foo, }
+pub enum BarCrate { Foo, }
+
+pub fn foo() {}
+pub fn foo_self() {}
+pub fn foo_crate() {}
+
+pub type Type = i32;
+pub type TypeSelf = i32;
+pub type TypeCrate = i32;
+
+pub union Union {
+    a: i8,
+    b: i8,
+}
+pub union UnionSelf {
+    a: i8,
+    b: i8,
+}
+pub union UnionCrate {
+    a: i8,
+    b: i8,
+}
diff --git a/src/test/rustdoc/reexports-priv.rs b/src/test/rustdoc/reexports-priv.rs
new file mode 100644
index 0000000000000..ff7424033aa49
--- /dev/null
+++ b/src/test/rustdoc/reexports-priv.rs
@@ -0,0 +1,53 @@
+// aux-build: reexports.rs
+// compile-flags: --document-private-items
+
+#![crate_name = "foo"]
+
+extern crate reexports;
+
+// @has 'foo/macro.addr_of.html' '//*[@class="docblock type-decl"]' 'pub macro addr_of($place : expr) {'
+pub use reexports::addr_of;
+// @has 'foo/macro.addr_of_crate.html' '//*[@class="docblock type-decl"]' 'pub(crate) macro addr_of_crate($place : expr) {'
+pub(crate) use reexports::addr_of_crate;
+// @has 'foo/macro.addr_of_self.html' '//*[@class="docblock type-decl"]' 'pub(crate) macro addr_of_self($place : expr) {'
+pub(self) use reexports::addr_of_self;
+
+// @has 'foo/struct.Foo.html' '//*[@class="docblock type-decl"]' 'pub struct Foo;'
+pub use reexports::Foo;
+// @has 'foo/struct.FooCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) struct FooCrate;'
+pub(crate) use reexports::FooCrate;
+// @has 'foo/struct.FooSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) struct FooSelf;'
+pub(self) use reexports::FooSelf;
+
+// @has 'foo/enum.Bar.html' '//*[@class="docblock type-decl"]' 'pub enum Bar {'
+pub use reexports::Bar;
+// @has 'foo/enum.BarCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) enum BarCrate {'
+pub(crate) use reexports::BarCrate;
+// @has 'foo/enum.BarSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) enum BarSelf {'
+pub(self) use reexports::BarSelf;
+
+// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+pub use reexports::foo;
+// @has 'foo/fn.foo_crate.html' '//*[@class="rust fn"]' 'pub(crate) fn foo_crate()'
+pub(crate) use reexports::foo_crate;
+// @has 'foo/fn.foo_self.html' '//*[@class="rust fn"]' 'pub(crate) fn foo_self()'
+pub(self) use reexports::foo_self;
+
+// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+pub use reexports::Type;
+// @has 'foo/type.TypeCrate.html' '//*[@class="rust typedef"]' 'pub(crate) type TypeCrate ='
+pub(crate) use reexports::TypeCrate;
+// @has 'foo/type.TypeSelf.html' '//*[@class="rust typedef"]' 'pub(crate) type TypeSelf ='
+pub(self) use reexports::TypeSelf;
+
+// @has 'foo/union.Union.html' '//*[@class="docblock type-decl"]' 'pub union Union {'
+pub use reexports::Union;
+// @has 'foo/union.UnionCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) union UnionCrate {'
+pub(crate) use reexports::UnionCrate;
+// @has 'foo/union.UnionSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) union UnionSelf {'
+pub(self) use reexports::UnionSelf;
+
+pub mod foo {
+    // @!has 'foo/foo/union.Union.html'
+    use crate::reexports::Union;
+}
diff --git a/src/test/rustdoc/reexports.rs b/src/test/rustdoc/reexports.rs
new file mode 100644
index 0000000000000..ab4c5bc743907
--- /dev/null
+++ b/src/test/rustdoc/reexports.rs
@@ -0,0 +1,47 @@
+// aux-build: reexports.rs
+
+#![crate_name = "foo"]
+
+extern crate reexports;
+
+// @has 'foo/macro.addr_of.html' '//*[@class="docblock type-decl"]' 'pub macro addr_of($place : expr) {'
+pub use reexports::addr_of;
+// @!has 'foo/macro.addr_of_crate.html'
+pub(crate) use reexports::addr_of_crate;
+// @!has 'foo/macro.addr_of_self.html'
+pub(self) use reexports::addr_of_self;
+
+// @has 'foo/struct.Foo.html' '//*[@class="docblock type-decl"]' 'pub struct Foo;'
+pub use reexports::Foo;
+// @!has 'foo/struct.FooCrate.html'
+pub(crate) use reexports::FooCrate;
+// @!has 'foo/struct.FooSelf.html'
+pub(self) use reexports::FooSelf;
+
+// @has 'foo/enum.Bar.html' '//*[@class="docblock type-decl"]' 'pub enum Bar {'
+pub use reexports::Bar;
+// @!has 'foo/enum.BarCrate.html'
+pub(crate) use reexports::BarCrate;
+// @!has 'foo/enum.BarSelf.html'
+pub(self) use reexports::BarSelf;
+
+// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+pub use reexports::foo;
+// @!has 'foo/fn.foo_crate.html'
+pub(crate) use reexports::foo_crate;
+// @!has 'foo/fn.foo_self.html'
+pub(self) use reexports::foo_self;
+
+// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+pub use reexports::Type;
+// @!has 'foo/type.TypeCrate.html'
+pub(crate) use reexports::TypeCrate;
+// @!has 'foo/type.TypeSelf.html'
+pub(self) use reexports::TypeSelf;
+
+// @has 'foo/union.Union.html' '//*[@class="docblock type-decl"]' 'pub union Union {'
+pub use reexports::Union;
+// @!has 'foo/union.UnionCrate.html'
+pub(crate) use reexports::UnionCrate;
+// @!has 'foo/union.UnionSelf.html'
+pub(self) use reexports::UnionSelf;