about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-08-28 10:45:28 +0000
committerbors <bors@rust-lang.org>2021-08-28 10:45:28 +0000
commit05cccdc9b321e6565b3e62e8b52aec53d106ef2f (patch)
treed6d123da95741a6b5bad6f4e6288805fa500f174
parent2031fd6e46fbe4da271bb23d55c211b2e16dd91f (diff)
parentb5a41418f84b4d54b9137fe9c83c50f015161063 (diff)
downloadrust-05cccdc9b321e6565b3e62e8b52aec53d106ef2f.tar.gz
rust-05cccdc9b321e6565b3e62e8b52aec53d106ef2f.zip
Auto merge of #88019 - inquisitivecrystal:macro-def, r=cjgillot
Treat macros as HIR items

Macros have historically been treated differently from other items at the HIR level. This PR makes them just like normal items. There are a few special cases left over, which I've attempted to lay out below. By normalizing the treatment of macro items, this PR simplifies a fair bit of code and fixes some bugs at the same time. For more information, see #87406.

r? `@cjgillot`

## Backwards incompatibility

This is backwards incompatible in one small way. Due to a mistake, it was previously possible to apply stability attributes to an exported macro, even without enabling the `staged_api` feature. This never should have worked. After this PR, it will error, as it should. We could make it a warning instead, but that would require a special case for a feature that shouldn't ever have worked and is likely used by no or very few crates, so I'm not thrilled about the idea.

## Notes for reviewers
### Commit seperation

I'd recommend reviewing this PR commit by commit. The commit chunking wasn't perfect, but it's better than looking at the combined diff, which is quite overwhelming. The compiler and standard library build after each commit, although tests do not necessarily pass and tools do not necessarily build till the end of the series.

### Special cases
There are a few special cases that remain after this change. Here are the notable ones I remember:

1. Visibility works a bit differently for `macro_rules!` macros than other items, since they aren't generally marked with `pub` but instead with `#[macro_export]`.
2. Since `#[macro_export]` macros always have paths at the top level of the crate, some additional handling needs to be done on the reexport to top level.
### Performance impact

I don't know for sure, but theses changes may slightly hurt performance. They create more work for the compiler in a few places. For instance, some operations that were previously run only for exported macros are now run for all macros. A perf run is probably advisable. For all I know we'll see performance improvements instead. :)

## Issues resolved

This resolves #87406 (the tracking issue for this change). It also fixes several bugs:

Fixes #59306.
Fixes #73754.
Fixes #87257.
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs30
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs10
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs48
-rw-r--r--compiler/rustc_hir/src/arena.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs66
-rw-r--r--compiler/rustc_hir/src/intravisit.rs12
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs17
-rw-r--r--compiler/rustc_hir/src/target.rs1
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs6
-rw-r--r--compiler/rustc_lint/src/builtin.rs19
-rw-r--r--compiler/rustc_lint/src/late.rs4
-rw-r--r--compiler/rustc_lint/src/levels.rs3
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs17
-rw-r--r--compiler/rustc_middle/src/hir/map/collector.rs14
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs26
-rw-r--r--compiler/rustc_mir/src/monomorphize/collector.rs1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs21
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs4
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs5
-rw-r--r--compiler/rustc_passes/src/lib_features.rs4
-rw-r--r--compiler/rustc_passes/src/reachable.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs18
-rw-r--r--compiler/rustc_privacy/Cargo.toml1
-rw-r--r--compiler/rustc_privacy/src/lib.rs114
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs1
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs8
-rw-r--r--compiler/rustc_typeck/src/collect.rs1
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs1
-rw-r--r--src/librustdoc/clean/mod.rs23
-rw-r--r--src/librustdoc/doctest.rs28
-rw-r--r--src/librustdoc/doctree.rs12
-rw-r--r--src/librustdoc/visit_ast.rs91
-rw-r--r--src/test/rustdoc-ui/deny-missing-docs-macro.stderr2
-rw-r--r--src/test/rustdoc/macro-document-private-duplicate.rs23
-rw-r--r--src/test/rustdoc/macro-document-private.rs19
-rw-r--r--src/test/rustdoc/macro-indirect-use.rs16
-rw-r--r--src/test/ui/lint/lint-level-macro-def-mod.rs17
-rw-r--r--src/test/ui/lint/lint-level-macro-def.rs17
-rw-r--r--src/test/ui/lint/missing-doc-private-macro.rs4
-rw-r--r--src/test/ui/lint/missing-doc-private-macro.stderr4
-rw-r--r--src/test/ui/macros/macro-stability-rpass.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/inspector.rs7
46 files changed, 338 insertions, 396 deletions
diff --git a/Cargo.lock b/Cargo.lock
index cef4f11da80..af23324cbef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4146,6 +4146,7 @@ dependencies = [
 name = "rustc_privacy"
 version = "0.0.0"
 dependencies = [
+ "rustc_ast",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 3acf69ec2b7..cc87078d54b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -170,7 +170,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self.lower_item_id_use_tree(use_tree, i.id, &mut vec);
                 vec
             }
-            ItemKind::MacroDef(..) => SmallVec::new(),
             ItemKind::Fn(..) | ItemKind::Impl(box ImplKind { of_trait: None, .. }) => {
                 smallvec![i.id]
             }
@@ -212,28 +211,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
     pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item<'hir>> {
         let mut ident = i.ident;
         let mut vis = self.lower_visibility(&i.vis, None);
-
-        if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind {
-            if !macro_rules || self.sess.contains_name(&i.attrs, sym::macro_export) {
-                let hir_id = self.lower_node_id(i.id);
-                self.lower_attrs(hir_id, &i.attrs);
-                let body = P(self.lower_mac_args(body));
-                self.insert_macro_def(hir::MacroDef {
-                    ident,
-                    vis,
-                    def_id: hir_id.expect_owner(),
-                    span: i.span,
-                    ast: MacroDef { body, macro_rules },
-                });
-            } else {
-                for a in i.attrs.iter() {
-                    let a = self.lower_attr(a);
-                    self.non_exported_macro_attrs.push(a);
-                }
-            }
-            return None;
-        }
-
         let hir_id = self.lower_node_id(i.id);
         let attrs = self.lower_attrs(hir_id, &i.attrs);
         let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, &mut vis, &i.kind);
@@ -465,7 +442,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self.lower_generics(generics, ImplTraitContext::disallowed()),
                 self.lower_param_bounds(bounds, ImplTraitContext::disallowed()),
             ),
-            ItemKind::MacroDef(..) | ItemKind::MacCall(..) => {
+            ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => {
+                let body = P(self.lower_mac_args(body));
+
+                hir::ItemKind::Macro(ast::MacroDef { body, macro_rules })
+            }
+            ItemKind::MacCall(..) => {
                 panic!("`TyMac` should have been expanded by now")
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 948d74e3bf8..bd2c9f41a53 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -103,7 +103,6 @@ struct LoweringContext<'a, 'hir: 'a> {
     /// The items being lowered are collected here.
     owners: IndexVec<LocalDefId, Option<hir::OwnerNode<'hir>>>,
     bodies: BTreeMap<hir::BodyId, hir::Body<'hir>>,
-    non_exported_macro_attrs: Vec<ast::Attribute>,
 
     trait_impls: BTreeMap<DefId, Vec<LocalDefId>>,
 
@@ -330,7 +329,6 @@ pub fn lower_crate<'a, 'hir>(
         trait_impls: BTreeMap::new(),
         modules: BTreeMap::new(),
         attrs: BTreeMap::default(),
-        non_exported_macro_attrs: Vec::new(),
         catch_scopes: Vec::new(),
         loop_scopes: Vec::new(),
         is_in_loop_condition: false,
@@ -551,7 +549,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
 
         let krate = hir::Crate {
-            non_exported_macro_attrs: self.arena.alloc_from_iter(self.non_exported_macro_attrs),
             owners: self.owners,
             bodies: self.bodies,
             body_ids,
@@ -600,13 +597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         id
     }
 
-    fn insert_macro_def(&mut self, item: hir::MacroDef<'hir>) {
-        let def_id = item.def_id;
-        let item = self.arena.alloc(item);
-        self.owners.ensure_contains_elem(def_id, || None);
-        self.owners[def_id] = Some(hir::OwnerNode::MacroDef(item));
-    }
-
     fn allocate_hir_id_counter(&mut self, owner: NodeId) -> hir::HirId {
         // Set up the counter if needed.
         self.item_local_id_counters.entry(owner).or_insert(0);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index b09c668273a..f729973ddc6 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -578,6 +578,33 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
+    fn print_mac_def(
+        &mut self,
+        macro_def: &ast::MacroDef,
+        ident: &Ident,
+        sp: &Span,
+        print_visibility: impl FnOnce(&mut Self),
+    ) {
+        let (kw, has_bang) = if macro_def.macro_rules {
+            ("macro_rules", true)
+        } else {
+            print_visibility(self);
+            ("macro", false)
+        };
+        self.print_mac_common(
+            Some(MacHeader::Keyword(kw)),
+            has_bang,
+            Some(*ident),
+            macro_def.body.delim(),
+            &macro_def.body.inner_tokens(),
+            true,
+            *sp,
+        );
+        if macro_def.body.need_semicolon() {
+            self.word(";");
+        }
+    }
+
     fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) {
         self.maybe_print_comment(path.span.lo());
 
@@ -1305,24 +1332,9 @@ impl<'a> State<'a> {
                 }
             }
             ast::ItemKind::MacroDef(ref macro_def) => {
-                let (kw, has_bang) = if macro_def.macro_rules {
-                    ("macro_rules", true)
-                } else {
-                    self.print_visibility(&item.vis);
-                    ("macro", false)
-                };
-                self.print_mac_common(
-                    Some(MacHeader::Keyword(kw)),
-                    has_bang,
-                    Some(item.ident),
-                    macro_def.body.delim(),
-                    &macro_def.body.inner_tokens(),
-                    true,
-                    item.span,
-                );
-                if macro_def.body.need_semicolon() {
-                    self.word(";");
-                }
+                self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
+                    state.print_visibility(&item.vis)
+                });
             }
         }
         self.ann.post(self, AnnNode::Item(item))
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs
index c4cff79f6c5..0801a1bde22 100644
--- a/compiler/rustc_hir/src/arena.rs
+++ b/compiler/rustc_hir/src/arena.rs
@@ -35,7 +35,6 @@ macro_rules! arena_types {
             [few] inline_asm: rustc_hir::InlineAsm<$tcx>,
             [few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>,
             [] local: rustc_hir::Local<$tcx>,
-            [few] macro_def: rustc_hir::MacroDef<$tcx>,
             [few] mod_: rustc_hir::Mod<$tcx>,
             [] param: rustc_hir::Param<$tcx>,
             [] pat: rustc_hir::Pat<$tcx>,
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 551e6a57b32..a9bd83a67c9 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -670,9 +670,6 @@ pub struct ModuleItems {
 /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
 #[derive(Debug)]
 pub struct Crate<'hir> {
-    // Attributes from non-exported macros, kept only for collecting the library feature list.
-    pub non_exported_macro_attrs: &'hir [Attribute],
-
     pub owners: IndexVec<LocalDefId, Option<OwnerNode<'hir>>>,
     pub bodies: BTreeMap<BodyId, Body<'hir>>,
     pub trait_impls: BTreeMap<DefId, Vec<LocalDefId>>,
@@ -743,7 +740,7 @@ impl Crate<'_> {
                 OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item),
                 OwnerNode::ImplItem(item) => visitor.visit_impl_item(item),
                 OwnerNode::TraitItem(item) => visitor.visit_trait_item(item),
-                OwnerNode::MacroDef(_) | OwnerNode::Crate(_) => {}
+                OwnerNode::Crate(_) => {}
             }
         }
     }
@@ -758,7 +755,7 @@ impl Crate<'_> {
             Some(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item),
             Some(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item),
             Some(OwnerNode::TraitItem(item)) => visitor.visit_trait_item(item),
-            Some(OwnerNode::MacroDef(_)) | Some(OwnerNode::Crate(_)) | None => {}
+            Some(OwnerNode::Crate(_)) | None => {}
         })
     }
 
@@ -768,32 +765,6 @@ impl Crate<'_> {
             _ => None,
         })
     }
-
-    pub fn exported_macros<'hir>(&'hir self) -> impl Iterator<Item = &'hir MacroDef<'hir>> + 'hir {
-        self.owners.iter().filter_map(|owner| match owner {
-            Some(OwnerNode::MacroDef(macro_def)) => Some(*macro_def),
-            _ => None,
-        })
-    }
-}
-
-/// A macro definition, in this crate or imported from another.
-///
-/// Not parsed directly, but created on macro import or `macro_rules!` expansion.
-#[derive(Debug)]
-pub struct MacroDef<'hir> {
-    pub ident: Ident,
-    pub vis: Visibility<'hir>,
-    pub def_id: LocalDefId,
-    pub span: Span,
-    pub ast: ast::MacroDef,
-}
-
-impl MacroDef<'_> {
-    #[inline]
-    pub fn hir_id(&self) -> HirId {
-        HirId::make_owner(self.def_id)
-    }
 }
 
 /// A block of statements `{ .. }`, which may have a label (in this case the
@@ -2602,7 +2573,7 @@ pub struct PolyTraitRef<'hir> {
 
 pub type Visibility<'hir> = Spanned<VisibilityKind<'hir>>;
 
-#[derive(Debug)]
+#[derive(Copy, Clone, Debug)]
 pub enum VisibilityKind<'hir> {
     Public,
     Crate(CrateSugar),
@@ -2791,6 +2762,8 @@ pub enum ItemKind<'hir> {
     Const(&'hir Ty<'hir>, BodyId),
     /// A function declaration.
     Fn(FnSig<'hir>, Generics<'hir>, BodyId),
+    /// A MBE macro definition (`macro_rules!` or `macro`).
+    Macro(ast::MacroDef),
     /// A module.
     Mod(Mod<'hir>),
     /// An external module, e.g. `extern { .. }`.
@@ -2856,6 +2829,7 @@ impl ItemKind<'_> {
             ItemKind::Static(..) => "static item",
             ItemKind::Const(..) => "constant item",
             ItemKind::Fn(..) => "function",
+            ItemKind::Macro(..) => "macro",
             ItemKind::Mod(..) => "module",
             ItemKind::ForeignMod { .. } => "extern block",
             ItemKind::GlobalAsm(..) => "global asm item",
@@ -2996,7 +2970,6 @@ pub enum OwnerNode<'hir> {
     ForeignItem(&'hir ForeignItem<'hir>),
     TraitItem(&'hir TraitItem<'hir>),
     ImplItem(&'hir ImplItem<'hir>),
-    MacroDef(&'hir MacroDef<'hir>),
     Crate(&'hir Mod<'hir>),
 }
 
@@ -3006,8 +2979,7 @@ impl<'hir> OwnerNode<'hir> {
             OwnerNode::Item(Item { ident, .. })
             | OwnerNode::ForeignItem(ForeignItem { ident, .. })
             | OwnerNode::ImplItem(ImplItem { ident, .. })
-            | OwnerNode::TraitItem(TraitItem { ident, .. })
-            | OwnerNode::MacroDef(MacroDef { ident, .. }) => Some(*ident),
+            | OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident),
             OwnerNode::Crate(..) => None,
         }
     }
@@ -3018,7 +2990,6 @@ impl<'hir> OwnerNode<'hir> {
             | OwnerNode::ForeignItem(ForeignItem { span, .. })
             | OwnerNode::ImplItem(ImplItem { span, .. })
             | OwnerNode::TraitItem(TraitItem { span, .. })
-            | OwnerNode::MacroDef(MacroDef { span, .. })
             | OwnerNode::Crate(Mod { inner: span, .. }) => *span,
         }
     }
@@ -3062,8 +3033,7 @@ impl<'hir> OwnerNode<'hir> {
             OwnerNode::Item(Item { def_id, .. })
             | OwnerNode::TraitItem(TraitItem { def_id, .. })
             | OwnerNode::ImplItem(ImplItem { def_id, .. })
-            | OwnerNode::ForeignItem(ForeignItem { def_id, .. })
-            | OwnerNode::MacroDef(MacroDef { def_id, .. }) => *def_id,
+            | OwnerNode::ForeignItem(ForeignItem { def_id, .. }) => *def_id,
             OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner,
         }
     }
@@ -3095,13 +3065,6 @@ impl<'hir> OwnerNode<'hir> {
             _ => panic!(),
         }
     }
-
-    pub fn expect_macro_def(self) -> &'hir MacroDef<'hir> {
-        match self {
-            OwnerNode::MacroDef(n) => n,
-            _ => panic!(),
-        }
-    }
 }
 
 impl<'hir> Into<OwnerNode<'hir>> for &'hir Item<'hir> {
@@ -3128,12 +3091,6 @@ impl<'hir> Into<OwnerNode<'hir>> for &'hir TraitItem<'hir> {
     }
 }
 
-impl<'hir> Into<OwnerNode<'hir>> for &'hir MacroDef<'hir> {
-    fn into(self) -> OwnerNode<'hir> {
-        OwnerNode::MacroDef(self)
-    }
-}
-
 impl<'hir> Into<Node<'hir>> for OwnerNode<'hir> {
     fn into(self) -> Node<'hir> {
         match self {
@@ -3141,7 +3098,6 @@ impl<'hir> Into<Node<'hir>> for OwnerNode<'hir> {
             OwnerNode::ForeignItem(n) => Node::ForeignItem(n),
             OwnerNode::ImplItem(n) => Node::ImplItem(n),
             OwnerNode::TraitItem(n) => Node::TraitItem(n),
-            OwnerNode::MacroDef(n) => Node::MacroDef(n),
             OwnerNode::Crate(n) => Node::Crate(n),
         }
     }
@@ -3167,7 +3123,6 @@ pub enum Node<'hir> {
     Arm(&'hir Arm<'hir>),
     Block(&'hir Block<'hir>),
     Local(&'hir Local<'hir>),
-    MacroDef(&'hir MacroDef<'hir>),
 
     /// `Ctor` refers to the constructor of an enum variant or struct. Only tuple or unit variants
     /// with synthesized constructors.
@@ -3204,7 +3159,6 @@ impl<'hir> Node<'hir> {
             | Node::ForeignItem(ForeignItem { ident, .. })
             | Node::Field(FieldDef { ident, .. })
             | Node::Variant(Variant { ident, .. })
-            | Node::MacroDef(MacroDef { ident, .. })
             | Node::Item(Item { ident, .. })
             | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident),
             Node::Lifetime(lt) => Some(lt.name.ident()),
@@ -3265,8 +3219,7 @@ impl<'hir> Node<'hir> {
             Node::Item(Item { def_id, .. })
             | Node::TraitItem(TraitItem { def_id, .. })
             | Node::ImplItem(ImplItem { def_id, .. })
-            | Node::ForeignItem(ForeignItem { def_id, .. })
-            | Node::MacroDef(MacroDef { def_id, .. }) => Some(HirId::make_owner(*def_id)),
+            | Node::ForeignItem(ForeignItem { def_id, .. }) => Some(HirId::make_owner(*def_id)),
             Node::Field(FieldDef { hir_id, .. })
             | Node::AnonConst(AnonConst { hir_id, .. })
             | Node::Expr(Expr { hir_id, .. })
@@ -3326,7 +3279,6 @@ impl<'hir> Node<'hir> {
             Node::ForeignItem(i) => Some(OwnerNode::ForeignItem(i)),
             Node::TraitItem(i) => Some(OwnerNode::TraitItem(i)),
             Node::ImplItem(i) => Some(OwnerNode::ImplItem(i)),
-            Node::MacroDef(i) => Some(OwnerNode::MacroDef(i)),
             Node::Crate(i) => Some(OwnerNode::Crate(i)),
             _ => None,
         }
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 7f153867596..f4fbfd2692c 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -466,9 +466,6 @@ pub trait Visitor<'v>: Sized {
         walk_assoc_type_binding(self, type_binding)
     }
     fn visit_attribute(&mut self, _id: HirId, _attr: &'v Attribute) {}
-    fn visit_macro_def(&mut self, macro_def: &'v MacroDef<'v>) {
-        walk_macro_def(self, macro_def)
-    }
     fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
         walk_vis(self, vis)
     }
@@ -484,7 +481,6 @@ pub trait Visitor<'v>: Sized {
 pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
     let top_mod = krate.module();
     visitor.visit_mod(top_mod, top_mod.inner, CRATE_HIR_ID);
-    walk_list!(visitor, visit_macro_def, krate.exported_macros());
     for (&id, attrs) in krate.attrs.iter() {
         for a in *attrs {
             visitor.visit_attribute(id, a)
@@ -492,11 +488,6 @@ pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) {
     }
 }
 
-pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef<'v>) {
-    visitor.visit_id(macro_def.hir_id());
-    visitor.visit_ident(macro_def.ident);
-}
-
 pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) {
     visitor.visit_id(mod_hir_id);
     for &item_id in module.item_ids {
@@ -586,6 +577,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
             item.span,
             item.hir_id(),
         ),
+        ItemKind::Macro(_) => {
+            visitor.visit_id(item.hir_id());
+        }
         ItemKind::Mod(ref module) => {
             // `visit_mod()` takes care of visiting the `Item`'s `HirId`.
             visitor.visit_mod(module, item.span, item.hir_id())
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index 56060752833..422a1064874 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -1,8 +1,8 @@
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
 
 use crate::hir::{
-    BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, MacroDef, Mod,
-    TraitItem, TraitItemId, Ty, VisibilityKind,
+    BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, Mod, TraitItem,
+    TraitItemId, Ty, VisibilityKind,
 };
 use crate::hir_id::{HirId, ItemLocalId};
 use rustc_span::def_id::DefPathHash;
@@ -190,16 +190,3 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Item<'_> {
         });
     }
 }
-
-impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for MacroDef<'_> {
-    fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
-        let MacroDef { ident, def_id: _, ref ast, ref vis, span } = *self;
-
-        hcx.hash_hir_item_like(|hcx| {
-            ident.name.hash_stable(hcx, hasher);
-            ast.hash_stable(hcx, hasher);
-            vis.hash_stable(hcx, hasher);
-            span.hash_stable(hcx, hasher);
-        });
-    }
-}
diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs
index 473477bf22d..29c948fe318 100644
--- a/compiler/rustc_hir/src/target.rs
+++ b/compiler/rustc_hir/src/target.rs
@@ -111,6 +111,7 @@ impl Target {
             ItemKind::Static(..) => Target::Static,
             ItemKind::Const(..) => Target::Const,
             ItemKind::Fn(..) => Target::Fn,
+            ItemKind::Macro(..) => Target::MacroDef,
             ItemKind::Mod(..) => Target::Mod,
             ItemKind::ForeignMod { .. } => Target::ForeignMod,
             ItemKind::GlobalAsm(..) => Target::GlobalAsm,
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 9c286ad5ccf..42e51f4bb48 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -120,7 +120,6 @@ impl<'a> State<'a> {
             // printing.
             Node::Ctor(..) => panic!("cannot print isolated Ctor"),
             Node::Local(a) => self.print_local_decl(&a),
-            Node::MacroDef(_) => panic!("cannot print MacroDef"),
             Node::Crate(..) => panic!("cannot print Crate"),
         }
     }
@@ -642,6 +641,11 @@ impl<'a> State<'a> {
                 self.end(); // need to close a box
                 self.ann.nested(self, Nested::Body(body));
             }
+            hir::ItemKind::Macro(ref macro_def) => {
+                self.print_mac_def(macro_def, &item.ident, &item.span, |state| {
+                    state.print_visibility(&item.vis)
+                });
+            }
             hir::ItemKind::Mod(ref _mod) => {
                 self.head(visibility_qualified(&item.vis, "mod"));
                 self.print_ident(item.ident);
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8f848f54aad..a5b4fa15921 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -585,24 +585,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
     fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) {
         self.check_missing_docs_attrs(cx, CRATE_DEF_ID, krate.module().inner, "the", "crate");
-
-        for macro_def in krate.exported_macros() {
-            // Non exported macros should be skipped, since `missing_docs` only
-            // applies to externally visible items.
-            if !cx.access_levels.is_exported(macro_def.def_id) {
-                continue;
-            }
-
-            let attrs = cx.tcx.hir().attrs(macro_def.hir_id());
-            let has_doc = attrs.iter().any(has_doc);
-            if !has_doc {
-                cx.struct_span_lint(
-                    MISSING_DOCS,
-                    cx.tcx.sess.source_map().guess_head_span(macro_def.span),
-                    |lint| lint.build("missing documentation for macro").emit(),
-                );
-            }
-        }
     }
 
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
@@ -636,6 +618,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
             hir::ItemKind::TyAlias(..)
             | hir::ItemKind::Fn(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::Enum(..)
             | hir::ItemKind::Struct(..)
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 052efa851f7..30400da86b4 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -453,10 +453,6 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T)
         lint_callback!(cx, check_crate, krate);
 
         hir_visit::walk_crate(cx, krate);
-        for attr in krate.non_exported_macro_attrs {
-            // This HIR ID is a lie, since the macro ID isn't available.
-            cx.visit_attribute(hir::CRATE_HIR_ID, attr);
-        }
 
         lint_callback!(cx, check_crate_post, krate);
     })
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index e6cbfa0c9e2..90bf34ee863 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -37,9 +37,6 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
 
     let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true);
     builder.levels.register_id(hir::CRATE_HIR_ID);
-    for macro_def in krate.exported_macros() {
-        builder.levels.register_id(macro_def.hir_id());
-    }
     intravisit::walk_crate(&mut builder, krate);
     builder.levels.pop(push);
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 4e591b28f60..dd44e0cb1fa 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1100,7 +1100,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     let vis = self.get_visibility(child_index);
                     let def_id = self.local_def_id(child_index);
                     let res = Res::Def(kind, def_id);
-                    callback(Export { res, ident, vis, span });
+
+                    // FIXME: Macros are currently encoded twice, once as items and once as
+                    // reexports. We ignore the items here and only use the reexports.
+                    if !matches!(kind, DefKind::Macro(..)) {
+                        callback(Export { res, ident, vis, span });
+                    }
+
                     // For non-re-export structs and variants add their constructors to children.
                     // Re-export lists automatically contain constructors when necessary.
                     match kind {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 1c243f8bd51..2cd4fe3b706 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -448,9 +448,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
 
         krate.visit_all_item_likes(&mut self.as_deep_visitor());
-        for macro_def in krate.exported_macros() {
-            self.visit_macro_def(macro_def);
-        }
     }
 
     fn encode_def_path_table(&mut self) {
@@ -1385,6 +1382,9 @@ impl EncodeContext<'a, 'tcx> {
 
                 EntryKind::Fn(self.lazy(data))
             }
+            hir::ItemKind::Macro(ref macro_def) => {
+                EntryKind::MacroDef(self.lazy(macro_def.clone()))
+            }
             hir::ItemKind::Mod(ref m) => {
                 return self.encode_info_for_mod(item.def_id, m);
             }
@@ -1539,13 +1539,6 @@ impl EncodeContext<'a, 'tcx> {
         }
     }
 
-    /// Serialize the text of exported macros
-    fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) {
-        let def_id = macro_def.def_id.to_def_id();
-        record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone())));
-        self.encode_ident_span(def_id, macro_def.ident);
-    }
-
     fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) {
         record!(self.tables.kind[def_id] <- kind);
         if encode_type {
@@ -1915,9 +1908,6 @@ impl Visitor<'tcx> for EncodeContext<'a, 'tcx> {
         intravisit::walk_generics(self, generics);
         self.encode_info_for_generics(generics);
     }
-    fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
-        self.encode_info_for_macro_def(macro_def);
-    }
 }
 
 impl EncodeContext<'a, 'tcx> {
@@ -1972,6 +1962,7 @@ impl EncodeContext<'a, 'tcx> {
             hir::ItemKind::Static(..)
             | hir::ItemKind::Const(..)
             | hir::ItemKind::Fn(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::GlobalAsm(..)
diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs
index 5c166c74004..1351b4950f1 100644
--- a/compiler/rustc_middle/src/hir/map/collector.rs
+++ b/compiler/rustc_middle/src/hir/map/collector.rs
@@ -394,20 +394,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         }
     }
 
-    fn visit_macro_def(&mut self, macro_def: &'hir MacroDef<'hir>) {
-        // Exported macros are visited directly from the crate root,
-        // so they do not have `parent_node` set.
-        // Find the correct enclosing module from their DefKey.
-        let def_key = self.definitions.def_key(macro_def.def_id);
-        let parent = def_key.parent.map_or(hir::CRATE_HIR_ID, |local_def_index| {
-            self.definitions.local_def_id_to_hir_id(LocalDefId { local_def_index })
-        });
-        self.insert_owner(macro_def.def_id, OwnerNode::MacroDef(macro_def));
-        self.with_parent(parent, |this| {
-            this.insert_nested(macro_def.def_id);
-        });
-    }
-
     fn visit_variant(&mut self, v: &'hir Variant<'hir>, g: &'hir Generics<'hir>, item_id: HirId) {
         self.insert(v.span, v.id, Node::Variant(v));
         self.with_parent(v.id, |this| {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index c313146b072..62d0374fb52 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -10,7 +10,6 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
 use rustc_hir::intravisit;
-use rustc_hir::intravisit::Visitor;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::*;
 use rustc_index::vec::Idx;
@@ -218,6 +217,7 @@ impl<'hir> Map<'hir> {
                 ItemKind::Static(..) => DefKind::Static,
                 ItemKind::Const(..) => DefKind::Const,
                 ItemKind::Fn(..) => DefKind::Fn,
+                ItemKind::Macro(..) => DefKind::Macro(MacroKind::Bang),
                 ItemKind::Mod(..) => DefKind::Mod,
                 ItemKind::OpaqueTy(..) => DefKind::OpaqueTy,
                 ItemKind::TyAlias(..) => DefKind::TyAlias,
@@ -266,7 +266,6 @@ impl<'hir> Map<'hir> {
                 ExprKind::Closure(.., Some(_)) => DefKind::Generator,
                 _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)),
             },
-            Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang),
             Node::GenericParam(param) => match param.kind {
                 GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam,
                 GenericParamKind::Type { .. } => DefKind::TyParam,
@@ -543,15 +542,6 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn visit_exported_macros_in_krate<V>(&self, visitor: &mut V)
-    where
-        V: Visitor<'hir>,
-    {
-        for macro_def in self.krate().exported_macros() {
-            visitor.visit_macro_def(macro_def);
-        }
-    }
-
     /// Returns an iterator for the nodes in the ancestor tree of the `current_id`
     /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`.
     pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> {
@@ -645,8 +635,6 @@ impl<'hir> Map<'hir> {
     /// in a module, trait, or impl.
     pub fn get_parent_item(&self, hir_id: HirId) -> HirId {
         if let Some((hir_id, _node)) = self.parent_owner_iter(hir_id).next() {
-            // A MacroDef does not have children.
-            debug_assert!(!matches!(_node, OwnerNode::MacroDef(_)));
             hir_id
         } else {
             CRATE_HIR_ID
@@ -774,13 +762,6 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn expect_macro_def(&self, id: HirId) -> &'hir MacroDef<'hir> {
-        match self.tcx.hir_owner(id.expect_owner()) {
-            Some(Owner { node: OwnerNode::MacroDef(macro_def) }) => macro_def,
-            _ => bug!("expected macro def, found {}", self.node_to_string(id)),
-        }
-    }
-
     pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> {
         match self.find(id) {
             Some(Node::Expr(expr)) => expr,
@@ -800,7 +781,6 @@ impl<'hir> Map<'hir> {
             Node::GenericParam(param) => param.name.ident().name,
             Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name,
             Node::Ctor(..) => self.name(self.get_parent_item(id)),
-            Node::MacroDef(md) => md.ident.name,
             _ => return None,
         })
     }
@@ -867,7 +847,6 @@ impl<'hir> Map<'hir> {
             Node::Infer(i) => i.span,
             Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
             Node::Local(local) => local.span,
-            Node::MacroDef(macro_def) => macro_def.span,
             Node::Crate(item) => item.inner,
         };
         Some(span)
@@ -1013,7 +992,6 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
     source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
     tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
     tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher);
-    tcx.untracked_crate.non_exported_macro_attrs.hash_stable(&mut hcx, &mut stable_hasher);
 
     let crate_hash: Fingerprint = stable_hasher.finish();
     Svh::new(crate_hash.to_smaller_hash())
@@ -1062,6 +1040,7 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
                 ItemKind::Static(..) => "static",
                 ItemKind::Const(..) => "const",
                 ItemKind::Fn(..) => "fn",
+                ItemKind::Macro(..) => "macro",
                 ItemKind::Mod(..) => "mod",
                 ItemKind::ForeignMod { .. } => "foreign mod",
                 ItemKind::GlobalAsm(..) => "global asm",
@@ -1118,7 +1097,6 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
         Some(Node::Lifetime(_)) => node_str("lifetime"),
         Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
         Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str),
-        Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str),
         Some(Node::Crate(..)) => String::from("root_crate"),
         None => format!("unknown node{}", id_str),
     }
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index f03036267ac..4cb362238c1 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -1149,6 +1149,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
         match item.kind {
             hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::Use(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::TyAlias(..)
             | hir::ItemKind::Trait(..)
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d0ddad1c1a3..d3dac35d2c9 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1723,6 +1723,16 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
     }
 
     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
+        // Historically we've run more checks on non-exported than exported macros,
+        // so this lets us continue to run them while maintaining backwards compatibility.
+        // In the long run, the checks should be harmonized.
+        if let ItemKind::Macro(ref macro_def) = item.kind {
+            let def_id = item.def_id.to_def_id();
+            if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
+                check_non_exported_macro_for_invalid_attrs(self.tcx, item);
+            }
+        }
+
         let target = Target::from_item(item);
         self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
         intravisit::walk_item(self, item)
@@ -1795,11 +1805,6 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
         intravisit::walk_variant(self, variant, generics, item_id)
     }
 
-    fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
-        self.check_attributes(macro_def.hir_id(), &macro_def.span, Target::MacroDef, None);
-        intravisit::walk_macro_def(self, macro_def);
-    }
-
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
         self.check_attributes(param.hir_id, &param.span, Target::Param, None);
 
@@ -1848,7 +1853,9 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
     }
 }
 
-fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
+fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
+    let attrs = tcx.hir().attrs(item.hir_id());
+
     for attr in attrs {
         if attr.has_name(sym::inline) {
             struct_span_err!(
@@ -1869,8 +1876,6 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
     if module_def_id.is_top_level_module() {
         check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
-        tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
-        check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
     }
 }
 
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index e43abda7133..3f12a744be0 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -107,10 +107,6 @@ fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> FxHashMap<Symbol
     // Collect diagnostic items in this crate.
     tcx.hir().krate().visit_all_item_likes(&mut collector);
 
-    for m in tcx.hir().krate().exported_macros() {
-        collector.observe_item(m.def_id);
-    }
-
     collector.items
 }
 
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 2bed8cadeb9..b8ce973185c 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -244,11 +244,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_attribute(&mut self, _: hir::HirId, attr: &'v ast::Attribute) {
         self.record("Attribute", Id::Attr(attr.id), attr);
     }
-
-    fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef<'v>) {
-        self.record("MacroDef", Id::Node(macro_def.hir_id()), macro_def);
-        hir_visit::walk_macro_def(self, macro_def)
-    }
 }
 
 impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 605b52f1891..7d15ca1e8f7 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -127,9 +127,7 @@ impl Visitor<'tcx> for LibFeatureCollector<'tcx> {
 fn get_lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures {
     let mut collector = LibFeatureCollector::new(tcx);
     let krate = tcx.hir().krate();
-    for attr in krate.non_exported_macro_attrs {
-        collector.visit_attribute(rustc_hir::CRATE_HIR_ID, attr);
-    }
+
     intravisit::walk_crate(&mut collector, krate);
     collector.lib_features
 }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 5ca098c2287..23f43233b79 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -263,6 +263,7 @@ impl<'tcx> ReachableContext<'tcx> {
                     | hir::ItemKind::Use(..)
                     | hir::ItemKind::OpaqueTy(..)
                     | hir::ItemKind::TyAlias(..)
+                    | hir::ItemKind::Macro(..)
                     | hir::ItemKind::Mod(..)
                     | hir::ItemKind::ForeignMod { .. }
                     | hir::ItemKind::Impl { .. }
@@ -309,8 +310,7 @@ impl<'tcx> ReachableContext<'tcx> {
             | Node::Ctor(..)
             | Node::Field(_)
             | Node::Ty(_)
-            | Node::Crate(_)
-            | Node::MacroDef(_) => {}
+            | Node::Crate(_) => {}
             _ => {
                 bug!(
                     "found unexpected node kind in worklist: {} ({:?})",
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index b64dcb0bbf0..a88393cea82 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -538,19 +538,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
         );
     }
 
-    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        self.annotate(
-            md.def_id,
-            md.span,
-            None,
-            AnnotationKind::Required,
-            InheritDeprecation::Yes,
-            InheritConstStability::No,
-            InheritStability::No,
-            |_| {},
-        );
-    }
-
     fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
         let kind = match &p.kind {
             // Allow stability attributes on default generic arguments.
@@ -662,11 +649,6 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
         self.check_missing_stability(i.def_id, i.span);
         intravisit::walk_foreign_item(self, i);
     }
-
-    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        self.check_missing_stability(md.def_id, md.span);
-    }
-
     // Note that we don't need to `check_missing_stability` for default generic parameters,
     // as we assume that any default generic parameters without attributes are automatically
     // stable (assuming they have not inherited instability from their parent).
diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml
index 85a53b1bde2..6ac2915c345 100644
--- a/compiler/rustc_privacy/Cargo.toml
+++ b/compiler/rustc_privacy/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2018"
 
 [dependencies]
 rustc_middle = { path = "../rustc_middle" }
+rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_hir = { path = "../rustc_hir" }
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 6fe68a0c17a..079a9ed878a 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(associated_type_defaults)]
 #![recursion_limit = "256"]
 
+use rustc_ast::MacroDef;
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
@@ -26,7 +27,7 @@ use rustc_middle::ty::subst::{InternalSubsts, Subst};
 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, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
 use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst};
 
@@ -462,6 +463,43 @@ impl EmbargoVisitor<'tcx> {
         }
     }
 
+    // We have to make sure that the items that macros might reference
+    // are reachable, since they might be exported transitively.
+    fn update_reachability_from_macro(&mut self, local_def_id: LocalDefId, md: &MacroDef) {
+        // Non-opaque macros cannot make other items more accessible than they already are.
+
+        let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
+        let attrs = self.tcx.hir().attrs(hir_id);
+        if attr::find_transparency(&attrs, md.macro_rules).0 != Transparency::Opaque {
+            return;
+        }
+
+        let item_def_id = local_def_id.to_def_id();
+        let macro_module_def_id =
+            ty::DefIdTree::parent(self.tcx, item_def_id).unwrap().expect_local();
+        if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
+            // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
+            return;
+        }
+
+        if self.get(local_def_id).is_none() {
+            return;
+        }
+
+        // Since we are starting from an externally visible module,
+        // all the parents in the loop below are also guaranteed to be modules.
+        let mut module_def_id = macro_module_def_id;
+        loop {
+            let changed_reachability =
+                self.update_macro_reachable(module_def_id, macro_module_def_id);
+            if changed_reachability || module_def_id == CRATE_DEF_ID {
+                break;
+            }
+            module_def_id =
+                ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
+        }
+    }
+
     /// Updates the item as being reachable through a macro defined in the given
     /// module. Returns `true` if the level has changed.
     fn update_macro_reachable(
@@ -511,16 +549,26 @@ impl EmbargoVisitor<'tcx> {
         }
         match def_kind {
             // No type privacy, so can be directly marked as reachable.
-            DefKind::Const
-            | DefKind::Macro(_)
-            | DefKind::Static
-            | DefKind::TraitAlias
-            | DefKind::TyAlias => {
+            DefKind::Const | DefKind::Static | DefKind::TraitAlias | DefKind::TyAlias => {
                 if vis.is_accessible_from(module.to_def_id(), self.tcx) {
                     self.update(def_id, level);
                 }
             }
 
+            // Hygine isn't really implemented for `macro_rules!` macros at the
+            // moment. Accordingly, marking them as reachable is unwise. `macro` macros
+            // have normal  hygine, so we can treat them like other items without type
+            // privacy and mark them reachable.
+            DefKind::Macro(_) => {
+                let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+                let item = self.tcx.hir().expect_item(hir_id);
+                if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }) = item.kind {
+                    if vis.is_accessible_from(module.to_def_id(), self.tcx) {
+                        self.update(def_id, level);
+                    }
+                }
+            }
+
             // We can't use a module name as the final segment of a path, except
             // in use statements. Since re-export checking doesn't consider
             // hygiene these don't need to be marked reachable. The contents of
@@ -644,6 +692,12 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
             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.
@@ -652,6 +706,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
             | hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::GlobalAsm(..)
             | hir::ItemKind::Fn(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::Static(..)
             | hir::ItemKind::Struct(..)
@@ -708,6 +763,9 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     }
                 }
             }
+            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 foreign_item.vis.node.is_pub() {
@@ -715,6 +773,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     }
                 }
             }
+
             hir::ItemKind::OpaqueTy(..)
             | hir::ItemKind::Use(..)
             | hir::ItemKind::Static(..)
@@ -730,7 +789,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
         // Mark all items in interfaces of reachable items as reachable.
         match item.kind {
             // The interface is empty.
-            hir::ItemKind::ExternCrate(..) => {}
+            hir::ItemKind::Macro(..) | 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
@@ -885,45 +944,6 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
 
         intravisit::walk_mod(self, m, id);
     }
-
-    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        // Non-opaque macros cannot make other items more accessible than they already are.
-        let attrs = self.tcx.hir().attrs(md.hir_id());
-        if attr::find_transparency(&attrs, md.ast.macro_rules).0 != Transparency::Opaque {
-            // `#[macro_export]`-ed `macro_rules!` are `Public` since they
-            // ignore their containing path to always appear at the crate root.
-            if md.ast.macro_rules {
-                self.update(md.def_id, Some(AccessLevel::Public));
-            }
-            return;
-        }
-
-        let macro_module_def_id =
-            ty::DefIdTree::parent(self.tcx, md.def_id.to_def_id()).unwrap().expect_local();
-        if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
-            // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
-            return;
-        }
-
-        let level = if md.vis.node.is_pub() { self.get(macro_module_def_id) } else { None };
-        let new_level = self.update(md.def_id, level);
-        if new_level.is_none() {
-            return;
-        }
-
-        // Since we are starting from an externally visible module,
-        // all the parents in the loop below are also guaranteed to be modules.
-        let mut module_def_id = macro_module_def_id;
-        loop {
-            let changed_reachability =
-                self.update_macro_reachable(module_def_id, macro_module_def_id);
-            if changed_reachability || module_def_id == CRATE_DEF_ID {
-                break;
-            }
-            module_def_id =
-                ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
-        }
-    }
 }
 
 impl ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
@@ -1981,7 +2001,7 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> {
             // Checked in resolve.
             hir::ItemKind::Use(..) => {}
             // No subitems.
-            hir::ItemKind::GlobalAsm(..) => {}
+            hir::ItemKind::Macro(..) | hir::ItemKind::GlobalAsm(..) => {}
             // Subitems of these items have inherited publicity.
             hir::ItemKind::Const(..)
             | hir::ItemKind::Static(..)
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 882d15cf892..bc2c46ec0aa 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -740,6 +740,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
 
             hir::ItemKind::ExternCrate(_)
             | hir::ItemKind::Use(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::GlobalAsm(..) => {
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index c3bc1c191ff..7864b47ab0a 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -416,6 +416,14 @@ impl<'hir> Sig for hir::Item<'hir> {
 
                 Ok(sig)
             }
+            hir::ItemKind::Macro(_) => {
+                let mut text = "macro".to_owned();
+                let name = self.ident.to_string();
+                text.push_str(&name);
+                text.push_str(&"! {}");
+
+                Ok(text_sig(text))
+            }
             hir::ItemKind::Mod(ref _mod) => {
                 let mut text = "mod ".to_owned();
                 let name = self.ident.to_string();
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index b514176ad52..145a0c5413b 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -746,6 +746,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
         // These don't define types.
         hir::ItemKind::ExternCrate(_)
         | hir::ItemKind::Use(..)
+        | hir::ItemKind::Macro(_)
         | hir::ItemKind::Mod(_)
         | hir::ItemKind::GlobalAsm(_) => {}
         hir::ItemKind::ForeignMod { items, .. } => {
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 9bce5ee0da2..41277b22da0 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -427,6 +427,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                 }
                 ItemKind::Trait(..)
                 | ItemKind::TraitAlias(..)
+                | ItemKind::Macro(..)
                 | ItemKind::Mod(..)
                 | ItemKind::ForeignMod { .. }
                 | ItemKind::GlobalAsm(..)
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b6ff3890c58..640acffb114 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -91,7 +91,6 @@ impl Clean<Item> for doctree::Module<'_> {
         items.extend(self.foreigns.iter().map(|x| x.clean(cx)));
         items.extend(self.mods.iter().map(|x| x.clean(cx)));
         items.extend(self.items.iter().map(|x| x.clean(cx)).flatten());
-        items.extend(self.macros.iter().map(|x| x.clean(cx)));
 
         // determine if we should display the inner contents or
         // the outer `mod` item for the source code.
@@ -1861,6 +1860,10 @@ impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Symbol>) {
                 ItemKind::Fn(ref sig, ref generics, body_id) => {
                     clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
                 }
+                ItemKind::Macro(ref macro_def) => MacroItem(Macro {
+                    source: display_macro_source(cx, name, &macro_def, def_id, &item.vis),
+                    imported_from: None,
+                }),
                 ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => {
                     let items = item_ids
                         .iter()
@@ -2138,24 +2141,6 @@ impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Symbol>) {
     }
 }
 
-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 def_id = item.def_id.to_def_id();
-
-        Item::from_hir_id_and_parts(
-            item.hir_id(),
-            Some(name),
-            MacroItem(Macro {
-                source: display_macro_source(cx, name, &item.ast, def_id, &item.vis),
-                imported_from: None,
-            }),
-            cx,
-        )
-    }
-}
-
 impl Clean<TypeBinding> for hir::TypeBinding<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
         TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 083d82cb414..bf14a17c076 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1171,10 +1171,21 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
     }
 
     fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
-        let name = if let hir::ItemKind::Impl(impl_) = &item.kind {
-            rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
-        } else {
-            item.ident.to_string()
+        let name = match &item.kind {
+            hir::ItemKind::Macro(ref macro_def) => {
+                // FIXME(#88038): Non exported macros have historically not been tested,
+                // but we really ought to start testing them.
+                let def_id = item.def_id.to_def_id();
+                if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
+                    intravisit::walk_item(self, item);
+                    return;
+                }
+                item.ident.to_string()
+            }
+            hir::ItemKind::Impl(impl_) => {
+                rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
+            }
+            _ => item.ident.to_string(),
         };
 
         self.visit_testable(name, item.hir_id(), item.span, |this| {
@@ -1216,15 +1227,6 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
             intravisit::walk_field_def(this, f);
         });
     }
-
-    fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) {
-        self.visit_testable(
-            macro_def.ident.to_string(),
-            macro_def.hir_id(),
-            macro_def.span,
-            |_| (),
-        );
-    }
 }
 
 #[cfg(test)]
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index eadac89f79e..8f1e8f277c5 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -5,6 +5,7 @@ use rustc_span::{self, Span, Symbol};
 
 use rustc_hir as hir;
 
+#[derive(Debug)]
 crate struct Module<'hir> {
     crate name: Symbol,
     crate where_inner: Span,
@@ -13,20 +14,11 @@ crate struct Module<'hir> {
     // (item, renamed)
     crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
     crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
-    crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
 }
 
 impl Module<'hir> {
     crate fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Module<'hir> {
-        Module {
-            name,
-            id,
-            where_inner,
-            mods: Vec::new(),
-            items: Vec::new(),
-            foreigns: Vec::new(),
-            macros: Vec::new(),
-        }
+        Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
     }
 
     crate fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e2891035535..897b9140fc8 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -9,7 +9,7 @@ use rustc_hir::Node;
 use rustc_middle::middle::privacy::AccessLevel;
 use rustc_middle::ty::TyCtxt;
 use rustc_span;
-use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Symbol};
 
@@ -79,49 +79,23 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             &krate.module(),
             self.cx.tcx.crate_name(LOCAL_CRATE),
         );
-        // Attach the crate's exported macros to the top-level module.
-        // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
-        // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
-        // moving them back to their correct locations.
-        'exported_macros: for def in krate.exported_macros() {
-            // The `def` of a macro in `exported_macros` should correspond to either:
-            //  - a `#[macro_export] macro_rules!` macro,
-            //  - a built-in `derive` (or attribute) macro such as the ones in `::core`,
-            //  - a `pub macro`.
-            // Only the last two need to be fixed, thus:
-            if def.ast.macro_rules {
-                top_level_module.macros.push((def, None));
-                continue 'exported_macros;
-            }
-            let tcx = self.cx.tcx;
-            // Note: this is not the same as `.parent_module()`. Indeed, the latter looks
-            // for the closest module _ancestor_, which is not necessarily a direct parent
-            // (since a direct parent isn't necessarily a module, c.f. #77828).
-            let macro_parent_def_id = {
-                use rustc_middle::ty::DefIdTree;
-                tcx.parent(def.def_id.to_def_id()).unwrap()
-            };
-            let macro_parent_path = tcx.def_path(macro_parent_def_id);
-            // HACK: rustdoc has no way to lookup `doctree::Module`s by their HirId. Instead,
-            // lookup the module by its name, by looking at each path segment one at a time.
-            let mut cur_mod = &mut top_level_module;
-            for path_segment in macro_parent_path.data {
-                // Path segments may refer to a module (in which case they belong to the type
-                // namespace), which is _necessary_ for the macro to be accessible outside it
-                // (no "associated macros" as of yet). Else we bail with an outer `continue`.
-                let path_segment_ty_ns = match path_segment.data {
-                    rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol,
-                    _ => continue 'exported_macros,
-                };
-                // Descend into the child module that matches this path segment (if any).
-                match cur_mod.mods.iter_mut().find(|child| child.name == path_segment_ty_ns) {
-                    Some(child_mod) => cur_mod = &mut *child_mod,
-                    None => continue 'exported_macros,
+
+        // `#[macro_export] macro_rules!` items are reexported at the top level of the
+        // crate, regardless of where they're defined. We want to document the
+        // top level rexport of the macro, not its original definition, since
+        // the rexport defines the path that a user will actually see. Accordingly,
+        // we add the rexport as an item here, and then skip over the original
+        // definition in `visit_item()` below.
+        for export in self.cx.tcx.module_exports(CRATE_DEF_ID).unwrap_or(&[]) {
+            if let Res::Def(DefKind::Macro(_), def_id) = export.res {
+                if let Some(local_def_id) = def_id.as_local() {
+                    if self.cx.tcx.has_attr(def_id, sym::macro_export) {
+                        let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+                        let item = self.cx.tcx.hir().expect_item(hir_id);
+                        top_level_module.items.push((item, None));
+                    }
                 }
             }
-            let cur_mod_def_id = tcx.hir().local_def_id(cur_mod.id).to_def_id();
-            assert_eq!(cur_mod_def_id, macro_parent_def_id);
-            cur_mod.macros.push((def, None));
         }
         self.cx.cache.exact_paths = self.exact_paths;
         top_level_module
@@ -238,10 +212,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 self.inlining = prev;
                 true
             }
-            Node::MacroDef(def) if !glob => {
-                om.macros.push((def, renamed));
-                true
-            }
             _ => false,
         };
         self.view_item_stack.remove(&res_hir_id);
@@ -257,7 +227,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         debug!("visiting item {:?}", item);
         let name = renamed.unwrap_or(item.ident.name);
 
-        if item.vis.node.is_pub() {
+        let def_id = item.def_id.to_def_id();
+        let is_pub = item.vis.node.is_pub() || self.cx.tcx.has_attr(def_id, sym::macro_export);
+
+        if is_pub {
             self.store_path(item.def_id.to_def_id());
         }
 
@@ -269,7 +242,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 }
             }
             // If we're inlining, skip private items.
-            _ if self.inlining && !item.vis.node.is_pub() => {}
+            _ if self.inlining && !is_pub => {}
             hir::ItemKind::GlobalAsm(..) => {}
             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
             hir::ItemKind::Use(ref path, kind) => {
@@ -285,7 +258,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
                 // If there was a private module in the current path then don't bother inlining
                 // anything as it will probably be stripped anyway.
-                if item.vis.node.is_pub() && self.inside_public_path {
+                if is_pub && self.inside_public_path {
                     let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
                         Some(ref list) if item.has_name(sym::doc) => {
                             list.iter().any(|i| i.has_name(sym::inline))
@@ -307,6 +280,26 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
                 om.items.push((item, renamed))
             }
+            hir::ItemKind::Macro(ref macro_def) => {
+                // `#[macro_export] macro_rules!` items are handled seperately in `visit()`,
+                // above, since they need to be documented at the module top level. Accordingly,
+                // we only want to handle macros if one of three conditions holds:
+                //
+                // 1. This macro was defined by `macro`, and thus isn't covered by the case
+                //    above.
+                // 2. This macro isn't marked with `#[macro_export]`, and thus isn't covered
+                //    by the case above.
+                // 3. We're inlining, since a reexport where inlining has been requested
+                //    should be inlined even if it is also documented at the top level.
+
+                let def_id = item.def_id.to_def_id();
+                let is_macro_2_0 = !macro_def.macro_rules;
+                let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
+
+                if is_macro_2_0 || nonexported || self.inlining {
+                    om.items.push((item, renamed));
+                }
+            }
             hir::ItemKind::Mod(ref m) => {
                 om.mods.push(self.visit_mod_contents(&item.vis, item.hir_id(), m, name));
             }
diff --git a/src/test/rustdoc-ui/deny-missing-docs-macro.stderr b/src/test/rustdoc-ui/deny-missing-docs-macro.stderr
index a564006e74f..0867b08183e 100644
--- a/src/test/rustdoc-ui/deny-missing-docs-macro.stderr
+++ b/src/test/rustdoc-ui/deny-missing-docs-macro.stderr
@@ -1,4 +1,4 @@
-error: missing documentation for macro
+error: missing documentation for a macro
   --> $DIR/deny-missing-docs-macro.rs:6:1
    |
 LL | macro_rules! foo {
diff --git a/src/test/rustdoc/macro-document-private-duplicate.rs b/src/test/rustdoc/macro-document-private-duplicate.rs
new file mode 100644
index 00000000000..460785ed979
--- /dev/null
+++ b/src/test/rustdoc/macro-document-private-duplicate.rs
@@ -0,0 +1,23 @@
+// FIXME: If two macros in the same module have the same name
+// (yes, that's a thing), rustdoc lists both of them on the index page,
+// but only documents the first one on the page for the macro.
+// Fortunately, this can only happen in document private items mode,
+// but it still isn't ideal beahvior.
+//
+// See https://github.com/rust-lang/rust/pull/88019#discussion_r693920453
+//
+// compile-flags: --document-private-items
+
+// @has macro_document_private_duplicate/index.html 'Doc 1.'
+// @has macro_document_private_duplicate/macro.a_macro.html 'Doc 1.'
+/// Doc 1.
+macro_rules! a_macro {
+    () => ()
+}
+
+// @has macro_document_private_duplicate/index.html 'Doc 2.'
+// @!has macro_document_private_duplicate/macro.a_macro.html 'Doc 2.'
+/// Doc 2.
+macro_rules! a_macro {
+    () => ()
+}
diff --git a/src/test/rustdoc/macro-document-private.rs b/src/test/rustdoc/macro-document-private.rs
new file mode 100644
index 00000000000..d2496913ffc
--- /dev/null
+++ b/src/test/rustdoc/macro-document-private.rs
@@ -0,0 +1,19 @@
+// Checks that private macros are documented when `--document-private-items`
+// is present.
+//
+// This is a regression test for issue #73754.
+//
+// compile-flags: --document-private-items
+
+#![feature(decl_macro)]
+
+
+// @has macro_document_private/macro.some_macro.html
+macro some_macro {
+    (a: tt) => {}
+}
+
+// @has macro_document_private/macro.another_macro.html
+macro_rules! another_macro {
+    (a: tt) => {}
+}
diff --git a/src/test/rustdoc/macro-indirect-use.rs b/src/test/rustdoc/macro-indirect-use.rs
new file mode 100644
index 00000000000..b2d9336cffc
--- /dev/null
+++ b/src/test/rustdoc/macro-indirect-use.rs
@@ -0,0 +1,16 @@
+// Checks that it is possible to make a macro public through a `pub use` of its
+// parent module.
+//
+// This is a regression test for issue #87257.
+
+#![feature(decl_macro)]
+
+mod outer {
+    pub mod inner {
+        pub macro some_macro() {}
+    }
+}
+
+// @has macro_indirect_use/inner/index.html
+// @has macro_indirect_use/inner/macro.some_macro.html
+pub use outer::inner;
diff --git a/src/test/ui/lint/lint-level-macro-def-mod.rs b/src/test/ui/lint/lint-level-macro-def-mod.rs
new file mode 100644
index 00000000000..79f7d1206df
--- /dev/null
+++ b/src/test/ui/lint/lint-level-macro-def-mod.rs
@@ -0,0 +1,17 @@
+// This checks that exported macros lint as part of their module of origin, not
+// the root module.
+//
+// check-pass
+
+//! Top level documentation
+#![deny(missing_docs)]
+
+#[allow(missing_docs)]
+mod module {
+    #[macro_export]
+    macro_rules! hello {
+        () => ()
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/lint-level-macro-def.rs b/src/test/ui/lint/lint-level-macro-def.rs
new file mode 100644
index 00000000000..720f4b453ab
--- /dev/null
+++ b/src/test/ui/lint/lint-level-macro-def.rs
@@ -0,0 +1,17 @@
+// Checks that you can set a lint level specficially for a macro definition.
+//
+// This is a regression test for issue #59306.
+//
+// check-pass
+
+
+#[deny(missing_docs)]
+mod module {
+    #[allow(missing_docs)]
+    #[macro_export]
+    macro_rules! hello {
+        () => ()
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/missing-doc-private-macro.rs b/src/test/ui/lint/missing-doc-private-macro.rs
index 8d1d5c56880..0d4332ed08b 100644
--- a/src/test/ui/lint/missing-doc-private-macro.rs
+++ b/src/test/ui/lint/missing-doc-private-macro.rs
@@ -29,13 +29,13 @@ mod submodule {
 
     #[macro_export]
     macro_rules! exported_to_top_level {
-        //~^ ERROR missing documentation for macro
+        //~^ ERROR missing documentation for a macro
         () => ()
     }
 }
 
 pub macro top_level_pub_macro {
-    //~^ ERROR missing documentation for macro
+    //~^ ERROR missing documentation for a macro
     () => ()
 }
 
diff --git a/src/test/ui/lint/missing-doc-private-macro.stderr b/src/test/ui/lint/missing-doc-private-macro.stderr
index a5d39faf405..979b007d0ec 100644
--- a/src/test/ui/lint/missing-doc-private-macro.stderr
+++ b/src/test/ui/lint/missing-doc-private-macro.stderr
@@ -1,4 +1,4 @@
-error: missing documentation for macro
+error: missing documentation for a macro
   --> $DIR/missing-doc-private-macro.rs:31:5
    |
 LL |     macro_rules! exported_to_top_level {
@@ -10,7 +10,7 @@ note: the lint level is defined here
 LL | #![deny(missing_docs)]
    |         ^^^^^^^^^^^^
 
-error: missing documentation for macro
+error: missing documentation for a macro
   --> $DIR/missing-doc-private-macro.rs:37:1
    |
 LL | pub macro top_level_pub_macro {
diff --git a/src/test/ui/macros/macro-stability-rpass.rs b/src/test/ui/macros/macro-stability-rpass.rs
index a5f538ba6b3..2d02b95288d 100644
--- a/src/test/ui/macros/macro-stability-rpass.rs
+++ b/src/test/ui/macros/macro-stability-rpass.rs
@@ -1,7 +1,8 @@
 // run-pass
 // aux-build:unstable-macros.rs
 
-#![feature(unstable_macros, local_unstable)]
+#![unstable(feature = "one_two_three_testing", issue = "none")]
+#![feature(staged_api, unstable_macros, local_unstable)]
 
 #[macro_use] extern crate unstable_macros;
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index d358e9fb876..940eee7a788 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -122,6 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             },
             hir::ItemKind::Const(..)
             | hir::ItemKind::Enum(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::Static(..)
             | hir::ItemKind::Struct(..)
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 977e6d966e8..667cdd83025 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
             },
             hir::ItemKind::Const(..)
             | hir::ItemKind::Enum(..)
+            | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::Static(..)
             | hir::ItemKind::Struct(..)
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
index 6bf216cec16..e97983a2e14 100644
--- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
@@ -381,6 +381,13 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
             let item_ty = cx.tcx.type_of(did);
             println!("function of type {:#?}", item_ty);
         },
+        hir::ItemKind::Macro(ref macro_def) => {
+            if macro_def.macro_rules {
+                println!("macro introduced by `macro_rules!`");
+            } else {
+                println!("macro introduced by `macro`");
+            }
+        },
         hir::ItemKind::Mod(..) => println!("module"),
         hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
         hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),