diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index c9d2f5c779bb6..ed8bf58eb23ef 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -429,10 +429,10 @@ pub fn noop_flat_map_pat_field<T: MutVisitor>(
 ) -> SmallVec<[PatField; 1]> {
     let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = &mut fp;
     vis.visit_id(id);
+    visit_attrs(attrs, vis);
     vis.visit_ident(ident);
     vis.visit_pat(pat);
     vis.visit_span(span);
-    visit_attrs(attrs, vis);
     smallvec![fp]
 }
 
@@ -443,8 +443,8 @@ fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
         UseTreeKind::Simple(rename) => visit_opt(rename, |rename| vis.visit_ident(rename)),
         UseTreeKind::Nested { items, .. } => {
             for (tree, id) in items {
-                vis.visit_use_tree(tree);
                 vis.visit_id(id);
+                vis.visit_use_tree(tree);
             }
         }
         UseTreeKind::Glob => {}
@@ -454,8 +454,8 @@ fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
 
 pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[Arm; 1]> {
     let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = &mut arm;
-    visit_attrs(attrs, vis);
     vis.visit_id(id);
+    visit_attrs(attrs, vis);
     vis.visit_pat(pat);
     visit_opt(guard, |guard| vis.visit_expr(guard));
     visit_opt(body, |body| vis.visit_expr(body));
@@ -548,10 +548,10 @@ pub fn noop_flat_map_variant<T: MutVisitor>(
     visitor: &mut T,
 ) -> SmallVec<[Variant; 1]> {
     let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = &mut variant;
+    visitor.visit_id(id);
+    visit_attrs(attrs, visitor);
     visitor.visit_ident(ident);
     visitor.visit_vis(vis);
-    visit_attrs(attrs, visitor);
-    visitor.visit_id(id);
     visitor.visit_variant_data(data);
     visit_opt(disr_expr, |disr_expr| visitor.visit_anon_const(disr_expr));
     visitor.visit_span(span);
@@ -565,8 +565,8 @@ fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: &mu
 fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens }: &mut Path, vis: &mut T) {
     vis.visit_span(span);
     for PathSegment { ident, id, args } in segments {
-        vis.visit_ident(ident);
         vis.visit_id(id);
+        vis.visit_ident(ident);
         visit_opt(args, |args| vis.visit_generic_args(args));
     }
     visit_lazy_tts(tokens, vis);
@@ -620,6 +620,7 @@ fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
 fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
     let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
     vis.visit_id(id);
+    visit_attrs(attrs, vis);
     vis.visit_pat(pat);
     visit_opt(ty, |ty| vis.visit_ty(ty));
     match kind {
@@ -634,7 +635,6 @@ fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
     }
     vis.visit_span(span);
     visit_opt(colon_sp, |sp| vis.visit_span(sp));
-    visit_attrs(attrs, vis);
     visit_lazy_tts(tokens, vis);
 }
 
@@ -894,9 +894,9 @@ fn noop_visit_coroutine_kind<T: MutVisitor>(coroutine_kind: &mut CoroutineKind,
         CoroutineKind::Async { span, closure_id, return_impl_trait_id }
         | CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
         | CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id } => {
-            vis.visit_span(span);
             vis.visit_id(closure_id);
             vis.visit_id(return_impl_trait_id);
+            vis.visit_span(span);
         }
     }
 }
@@ -932,8 +932,8 @@ fn noop_visit_precise_capturing_arg<T: MutVisitor>(arg: &mut PreciseCapturingArg
             vis.visit_lifetime(lt);
         }
         PreciseCapturingArg::Arg(path, id) => {
-            vis.visit_path(path);
             vis.visit_id(id);
+            vis.visit_path(path);
         }
     }
 }
@@ -944,11 +944,11 @@ pub fn noop_flat_map_generic_param<T: MutVisitor>(
 ) -> SmallVec<[GenericParam; 1]> {
     let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param;
     vis.visit_id(id);
+    visit_attrs(attrs, vis);
     vis.visit_ident(ident);
     if let Some(colon_span) = colon_span {
         vis.visit_span(colon_span);
     }
-    visit_attrs(attrs, vis);
     visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
     match kind {
         GenericParamKind::Lifetime => {}
@@ -1015,16 +1015,16 @@ fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T)
             fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
         }
         VariantData::Tuple(fields, id) => {
-            fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
             vis.visit_id(id);
+            fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
         }
         VariantData::Unit(id) => vis.visit_id(id),
     }
 }
 
 fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
-    vis.visit_path(path);
     vis.visit_id(ref_id);
+    vis.visit_path(path);
 }
 
 fn noop_visit_poly_trait_ref<T: MutVisitor>(p: &mut PolyTraitRef, vis: &mut T) {
@@ -1039,12 +1039,12 @@ pub fn noop_flat_map_field_def<T: MutVisitor>(
     visitor: &mut T,
 ) -> SmallVec<[FieldDef; 1]> {
     let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd;
+    visitor.visit_id(id);
+    visit_attrs(attrs, visitor);
     visitor.visit_span(span);
     visit_opt(ident, |ident| visitor.visit_ident(ident));
     visitor.visit_vis(vis);
-    visitor.visit_id(id);
     visitor.visit_ty(ty);
-    visit_attrs(attrs, visitor);
     smallvec![fd]
 }
 
@@ -1053,11 +1053,11 @@ pub fn noop_flat_map_expr_field<T: MutVisitor>(
     vis: &mut T,
 ) -> SmallVec<[ExprField; 1]> {
     let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = &mut f;
+    vis.visit_id(id);
+    visit_attrs(attrs, vis);
     vis.visit_ident(ident);
     vis.visit_expr(expr);
-    vis.visit_id(id);
     vis.visit_span(span);
-    visit_attrs(attrs, vis);
     smallvec![f]
 }
 
@@ -1429,6 +1429,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
     Expr { kind, id, span, attrs, tokens }: &mut Expr,
     vis: &mut T,
 ) {
+    vis.visit_id(id);
+    visit_attrs(attrs, vis);
     match kind {
         ExprKind::Array(exprs) => visit_thin_exprs(exprs, vis),
         ExprKind::ConstBlock(anon_const) => {
@@ -1449,8 +1451,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
             args: call_args,
             span,
         }) => {
-            vis.visit_ident(ident);
             vis.visit_id(id);
+            vis.visit_ident(ident);
             visit_opt(seg_args, |args| vis.visit_generic_args(args));
             vis.visit_method_receiver_expr(receiver);
             visit_thin_exprs(call_args, vis);
@@ -1601,9 +1603,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
         ExprKind::TryBlock(body) => vis.visit_block(body),
         ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err(_) | ExprKind::Dummy => {}
     }
-    vis.visit_id(id);
     vis.visit_span(span);
-    visit_attrs(attrs, vis);
     visit_lazy_tts(tokens, vis);
 }
 
@@ -1645,8 +1645,8 @@ fn noop_flat_map_stmt_kind<T: MutVisitor>(kind: StmtKind, vis: &mut T) -> SmallV
         StmtKind::Empty => smallvec![StmtKind::Empty],
         StmtKind::MacCall(mut mac) => {
             let MacCallStmt { mac: mac_, style: _, attrs, tokens } = mac.deref_mut();
-            vis.visit_mac_call(mac_);
             visit_attrs(attrs, vis);
+            vis.visit_mac_call(mac_);
             visit_lazy_tts(tokens, vis);
             smallvec![StmtKind::MacCall(mac)]
         }
@@ -1657,8 +1657,8 @@ fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) {
     match &mut visibility.kind {
         VisibilityKind::Public | VisibilityKind::Inherited => {}
         VisibilityKind::Restricted { path, id, shorthand: _ } => {
-            vis.visit_path(path);
             vis.visit_id(id);
+            vis.visit_path(path);
         }
     }
     vis.visit_span(&mut visibility.span);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index ce38a67ea69ab..e2ef0542bf9c1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -298,8 +298,8 @@ pub trait Visitor<'ast>: Sized {
 }
 
 pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result {
-    walk_list!(visitor, visit_item, &krate.items);
     walk_list!(visitor, visit_attribute, &krate.attrs);
+    walk_list!(visitor, visit_item, &krate.items);
     V::Result::output()
 }
 
@@ -462,25 +462,25 @@ pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) -
 where
     V: Visitor<'a>,
 {
+    walk_list!(visitor, visit_attribute, &variant.attrs);
     try_visit!(visitor.visit_ident(variant.ident));
     try_visit!(visitor.visit_vis(&variant.vis));
     try_visit!(visitor.visit_variant_data(&variant.data));
     visit_opt!(visitor, visit_variant_discr, &variant.disr_expr);
-    walk_list!(visitor, visit_attribute, &variant.attrs);
     V::Result::output()
 }
 
 pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) -> V::Result {
+    walk_list!(visitor, visit_attribute, &f.attrs);
     try_visit!(visitor.visit_expr(&f.expr));
     try_visit!(visitor.visit_ident(f.ident));
-    walk_list!(visitor, visit_attribute, &f.attrs);
     V::Result::output()
 }
 
 pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) -> V::Result {
+    walk_list!(visitor, visit_attribute, &fp.attrs);
     try_visit!(visitor.visit_ident(fp.ident));
     try_visit!(visitor.visit_pat(&fp.pat));
-    walk_list!(visitor, visit_attribute, &fp.attrs);
     V::Result::output()
 }
 
@@ -722,8 +722,8 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>(
     visitor: &mut V,
     param: &'a GenericParam,
 ) -> V::Result {
-    try_visit!(visitor.visit_ident(param.ident));
     walk_list!(visitor, visit_attribute, &param.attrs);
+    try_visit!(visitor.visit_ident(param.ident));
     walk_list!(visitor, visit_param_bound, &param.bounds, BoundKind::Bound);
     match &param.kind {
         GenericParamKind::Lifetime => (),
@@ -882,10 +882,10 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(
     ctxt: AssocCtxt,
 ) -> V::Result {
     let &Item { id: _, span: _, ident, ref vis, ref attrs, ref kind, tokens: _ } = item;
+    walk_list!(visitor, visit_attribute, attrs);
     try_visit!(visitor.visit_vis(vis));
     try_visit!(visitor.visit_ident(ident));
     try_visit!(kind.walk(item, ctxt, visitor));
-    walk_list!(visitor, visit_attribute, attrs);
     V::Result::output()
 }
 
@@ -898,10 +898,10 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
 }
 
 pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
+    walk_list!(visitor, visit_attribute, &field.attrs);
     try_visit!(visitor.visit_vis(&field.vis));
     visit_opt!(visitor, visit_ident, field.ident);
     try_visit!(visitor.visit_ty(&field.ty));
-    walk_list!(visitor, visit_attribute, &field.attrs);
     V::Result::output()
 }
 
@@ -918,8 +918,8 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V:
         StmtKind::Empty => {}
         StmtKind::MacCall(mac) => {
             let MacCallStmt { mac, attrs, style: _, tokens: _ } = &**mac;
-            try_visit!(visitor.visit_mac_call(mac));
             walk_list!(visitor, visit_attribute, attrs);
+            try_visit!(visitor.visit_mac_call(mac));
         }
     }
     V::Result::output()
@@ -1141,10 +1141,10 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) -> V::R
 }
 
 pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) -> V::Result {
+    walk_list!(visitor, visit_attribute, &arm.attrs);
     try_visit!(visitor.visit_pat(&arm.pat));
     visit_opt!(visitor, visit_expr, &arm.guard);
     visit_opt!(visitor, visit_expr, &arm.body);
-    walk_list!(visitor, visit_attribute, &arm.attrs);
     V::Result::output()
 }
 
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index eac5083ffbf0c..46cf87d1e3c17 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -604,6 +604,9 @@ lint_opaque_hidden_inferred_bound_sugg = add this bound
 lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
     .suggestion = use pat_param to preserve semantics
 
+lint_out_of_scope_macro_calls = cannot find macro `{$path}` in this scope
+    .help = import `macro_rules` with `use` to make it callable above its definition
+
 lint_overflowing_bin_hex = literal out of range for `{$ty}`
     .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}`
     .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}`
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index adb2a3275c0aa..05e075205c4b7 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -434,5 +434,8 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
             lints::InnerAttributeUnstable::CustomInnerAttribute
         }
         .decorate_lint(diag),
+        BuiltinLintDiag::OutOfScopeMacroCalls { path } => {
+            lints::OutOfScopeMacroCalls { path }.decorate_lint(diag)
+        }
     }
 }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 6df3a11deb00b..14084405d0ee1 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2911,3 +2911,10 @@ pub struct UnsafeAttrOutsideUnsafeSuggestion {
     #[suggestion_part(code = ")")]
     pub right: Span,
 }
+
+#[derive(LintDiagnostic)]
+#[diag(lint_out_of_scope_macro_calls)]
+#[help]
+pub struct OutOfScopeMacroCalls {
+    pub path: String,
+}
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 265779c937474..a023d6161df0c 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -4945,3 +4945,42 @@ declare_lint! {
         reference: "issue #123757 <https://github.com/rust-lang/rust/issues/123757>",
     };
 }
+
+declare_lint! {
+    /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
+    /// above their definition, which may happen in key-value attributes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![doc = in_root!()]
+    ///
+    /// macro_rules! in_root { () => { "" } }
+    ///
+    /// fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The scope in which a `macro_rules` item is visible starts at that item and continues
+    /// below it. This is more similar to `let` than to other items, which are in scope both above
+    /// and below their definition.
+    /// Due to a bug `macro_rules` were accidentally in scope inside some key-value attributes
+    /// above their definition. The lint catches such cases.
+    /// To address the issue turn the `macro_rules` into a regularly scoped item by importing it
+    /// with `use`.
+    ///
+    /// This is a [future-incompatible] lint to transition this to a
+    /// hard error in the future.
+    ///
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    pub OUT_OF_SCOPE_MACRO_CALLS,
+    Warn,
+    "detects out of scope calls to `macro_rules` in key-value attributes",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reference: "issue #124535 <https://github.com/rust-lang/rust/issues/124535>",
+    };
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index f33aadfbbc84d..b44eb25216770 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -744,6 +744,9 @@ pub enum BuiltinLintDiag {
     InnerAttributeUnstable {
         is_macro: bool,
     },
+    OutOfScopeMacroCalls {
+        path: String,
+    },
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index e035749fc39ee..4e0f2792d9749 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -14,7 +14,7 @@ use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, Modul
 use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, ResolutionError};
 use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError};
 
-use rustc_ast::visit::{self, AssocCtxt, Visitor};
+use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
 use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
 use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
 use rustc_attr as attr;
@@ -1313,7 +1313,17 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             _ => {
                 let orig_macro_rules_scope = self.parent_scope.macro_rules;
                 self.build_reduced_graph_for_item(item);
-                visit::walk_item(self, item);
+                match item.kind {
+                    ItemKind::Mod(..) => {
+                        // Visit attributes after items for backward compatibility.
+                        // This way they can use `macro_rules` defined later.
+                        self.visit_vis(&item.vis);
+                        self.visit_ident(item.ident);
+                        item.kind.walk(item, AssocCtxt::Trait, self);
+                        visit::walk_list!(self, visit_attribute, &item.attrs);
+                    }
+                    _ => visit::walk_item(self, item),
+                }
                 match item.kind {
                     ItemKind::Mod(..) if self.contains_macro_use(&item.attrs) => {
                         self.parent_scope.macro_rules
@@ -1514,7 +1524,10 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         if krate.is_placeholder {
             self.visit_invoc_in_module(krate.id);
         } else {
-            visit::walk_crate(self, krate);
+            // Visit attributes after items for backward compatibility.
+            // This way they can use `macro_rules` defined later.
+            visit::walk_list!(self, visit_item, &krate.items);
+            visit::walk_list!(self, visit_attribute, &krate.attrs);
             self.contains_macro_use(&krate.attrs);
         }
     }
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 8763e7aec4325..026a2ca14128e 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution;
 use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope};
 use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
 use crate::Namespace::*;
-use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used};
+use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, NameBindingKind, Used};
 use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
 use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
 use rustc_ast::expand::StrippedCfgItem;
@@ -18,15 +18,18 @@ use rustc_errors::{Applicability, StashKey};
 use rustc_expand::base::{Annotatable, DeriveResolution, Indeterminate, ResolverExpand};
 use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
 use rustc_expand::compile_declarative_macro;
-use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion};
+use rustc_expand::expand::{
+    AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion,
+};
 use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
 use rustc_middle::ty::{TyCtxt, Visibility};
-use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
-use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
-use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
+use rustc_session::lint::builtin::{
+    LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, SOFT_UNSTABLE,
+    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACROS, UNUSED_MACRO_RULES,
+};
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::edit_distance;
@@ -289,6 +292,16 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
         let parent_scope = &ParentScope { derives, ..parent_scope };
         let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion();
         let node_id = invoc.expansion_data.lint_node_id;
+        // This is a heuristic, but it's good enough for the lint.
+        let looks_like_invoc_in_mod_inert_attr = self
+            .invocation_parents
+            .get(&invoc_id)
+            .or_else(|| self.invocation_parents.get(&eager_expansion_root))
+            .map(|&(mod_def_id, _)| mod_def_id)
+            .filter(|&mod_def_id| {
+                invoc.fragment_kind == AstFragmentKind::Expr
+                    && self.tcx.def_kind(mod_def_id) == DefKind::Mod
+            });
         let (ext, res) = self.smart_resolve_macro_path(
             path,
             kind,
@@ -299,6 +312,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
             force,
             soft_custom_inner_attributes_gate(path, invoc),
             deleg_impl,
+            looks_like_invoc_in_mod_inert_attr,
         )?;
 
         let span = invoc.span();
@@ -521,6 +535,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         force: bool,
         soft_custom_inner_attributes_gate: bool,
         deleg_impl: Option<LocalDefId>,
+        invoc_in_mod_inert_attr: Option<LocalDefId>,
     ) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
         let (ext, res) = match self.resolve_macro_or_delegation_path(
             path,
@@ -529,6 +544,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             true,
             force,
             deleg_impl,
+            invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)),
         ) {
             Ok((Some(ext), res)) => (ext, res),
             Ok((None, res)) => (self.dummy_ext(kind), res),
@@ -683,20 +699,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         trace: bool,
         force: bool,
     ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
-        self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None)
+        self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None, None)
     }
 
     fn resolve_macro_or_delegation_path(
         &mut self,
-        path: &ast::Path,
+        ast_path: &ast::Path,
         kind: Option<MacroKind>,
         parent_scope: &ParentScope<'a>,
         trace: bool,
         force: bool,
         deleg_impl: Option<LocalDefId>,
+        invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
     ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
-        let path_span = path.span;
-        let mut path = Segment::from_path(path);
+        let path_span = ast_path.span;
+        let mut path = Segment::from_path(ast_path);
 
         // Possibly apply the macro helper hack
         if deleg_impl.is_none()
@@ -762,6 +779,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             let res = binding.map(|binding| binding.res());
             self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span);
+            self.report_out_of_scope_macro_calls(
+                ast_path,
+                parent_scope,
+                invoc_in_mod_inert_attr,
+                binding.ok(),
+            );
             res
         };
 
@@ -1014,6 +1037,45 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
+    fn report_out_of_scope_macro_calls(
+        &mut self,
+        path: &ast::Path,
+        parent_scope: &ParentScope<'a>,
+        invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
+        binding: Option<NameBinding<'a>>,
+    ) {
+        if let Some((mod_def_id, node_id)) = invoc_in_mod_inert_attr
+            && let Some(binding) = binding
+            // This is a `macro_rules` itself, not some import.
+            && let NameBindingKind::Res(res) = binding.kind
+            && let Res::Def(DefKind::Macro(MacroKind::Bang), def_id) = res
+            // And the `macro_rules` is defined inside the attribute's module,
+            // so it cannot be in scope unless imported.
+            && self.tcx.is_descendant_of(def_id, mod_def_id.to_def_id())
+        {
+            // Try to resolve our ident ignoring `macro_rules` scopes.
+            // If such resolution is successful and gives the same result
+            // (e.g. if the macro is re-imported), then silence the lint.
+            let no_macro_rules = self.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty);
+            let fallback_binding = self.early_resolve_ident_in_lexical_scope(
+                path.segments[0].ident,
+                ScopeSet::Macro(MacroKind::Bang),
+                &ParentScope { macro_rules: no_macro_rules, ..*parent_scope },
+                None,
+                false,
+                None,
+            );
+            if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) {
+                self.tcx.sess.psess.buffer_lint(
+                    OUT_OF_SCOPE_MACRO_CALLS,
+                    path.span,
+                    node_id,
+                    BuiltinLintDiag::OutOfScopeMacroCalls { path: pprust::path_to_string(path) },
+                );
+            }
+        }
+    }
+
     pub(crate) fn check_reserved_macro_name(&mut self, ident: Ident, res: Res) {
         // Reserve some names that are not quite covered by the general check
         // performed on `Resolver::builtin_attrs`.
diff --git a/src/tools/clippy/tests/ui/cfg_attr_cargo_clippy.stderr b/src/tools/clippy/tests/ui/cfg_attr_cargo_clippy.stderr
index ddec0e648d10c..0a358f1a68475 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_cargo_clippy.stderr
+++ b/src/tools/clippy/tests/ui/cfg_attr_cargo_clippy.stderr
@@ -1,12 +1,18 @@
 error: `feature = "cargo-clippy"` was replaced by `clippy`
-  --> tests/ui/cfg_attr_cargo_clippy.rs:5:12
+  --> tests/ui/cfg_attr_cargo_clippy.rs:3:13
    |
-LL | #[cfg_attr(feature = "cargo-clippy", derive(Debug))]
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `clippy`
+LL | #![cfg_attr(feature = "cargo-clippy", doc = "a")]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `clippy`
    |
    = note: `-D clippy::deprecated-clippy-cfg-attr` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::deprecated_clippy_cfg_attr)]`
 
+error: `feature = "cargo-clippy"` was replaced by `clippy`
+  --> tests/ui/cfg_attr_cargo_clippy.rs:5:12
+   |
+LL | #[cfg_attr(feature = "cargo-clippy", derive(Debug))]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `clippy`
+
 error: `feature = "cargo-clippy"` was replaced by `clippy`
   --> tests/ui/cfg_attr_cargo_clippy.rs:6:16
    |
@@ -37,11 +43,5 @@ error: `feature = "cargo-clippy"` was replaced by `clippy`
 LL | #[cfg(all(feature = "cargo-clippy"))]
    |           ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `clippy`
 
-error: `feature = "cargo-clippy"` was replaced by `clippy`
-  --> tests/ui/cfg_attr_cargo_clippy.rs:3:13
-   |
-LL | #![cfg_attr(feature = "cargo-clippy", doc = "a")]
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `clippy`
-
 error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
index 23d5dcd3a8dab..aef6c39145263 100644
--- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
@@ -1,53 +1,53 @@
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:10:9
+  --> tests/ui/tabs_in_doc_comments.rs:6:5
    |
-LL |     ///     - First String:
-   |         ^^^^ help: consider using four spaces per tab
+LL | ///     - first        one
+   |     ^^^^ help: consider using four spaces per tab
    |
    = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]`
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:11:9
+  --> tests/ui/tabs_in_doc_comments.rs:6:13
    |
-LL |     ///         - needs to be inside here
-   |         ^^^^^^^^ help: consider using four spaces per tab
+LL | ///     - first        one
+   |                ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:14:9
+  --> tests/ui/tabs_in_doc_comments.rs:7:5
    |
-LL |     ///     - Second String:
-   |         ^^^^ help: consider using four spaces per tab
+LL | ///     - second    one
+   |     ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:15:9
+  --> tests/ui/tabs_in_doc_comments.rs:7:14
    |
-LL |     ///         - needs to be inside here
-   |         ^^^^^^^^ help: consider using four spaces per tab
+LL | ///     - second    one
+   |                 ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:5
+  --> tests/ui/tabs_in_doc_comments.rs:10:9
    |
-LL | ///     - first        one
-   |     ^^^^ help: consider using four spaces per tab
+LL |     ///     - First String:
+   |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:13
+  --> tests/ui/tabs_in_doc_comments.rs:11:9
    |
-LL | ///     - first        one
-   |                ^^^^^^^^ help: consider using four spaces per tab
+LL |     ///         - needs to be inside here
+   |         ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:5
+  --> tests/ui/tabs_in_doc_comments.rs:14:9
    |
-LL | ///     - second    one
-   |     ^^^^ help: consider using four spaces per tab
+LL |     ///     - Second String:
+   |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:14
+  --> tests/ui/tabs_in_doc_comments.rs:15:9
    |
-LL | ///     - second    one
-   |                 ^^^^ help: consider using four spaces per tab
+LL |     ///         - needs to be inside here
+   |         ^^^^^^^^ help: consider using four spaces per tab
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
index 16a8616529565..01f842a657de5 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr
@@ -1,61 +1,61 @@
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:13:1
+  --> tests/ui/unnecessary_clippy_cfg.rs:4:1
    |
-LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]`
+LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]`
    |
    = note: `-D clippy::unnecessary-clippy-cfg` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_clippy_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:15:36
+  --> tests/ui/unnecessary_clippy_cfg.rs:6:37
    |
-LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: write instead: `#[deny(clippy::non_minimal_cfg)]`
+   = note: write instead: `#![deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:17:36
+  --> tests/ui/unnecessary_clippy_cfg.rs:8:37
    |
-LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: write instead: `#[deny(clippy::non_minimal_cfg)]`
+   = note: write instead: `#![deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:19:1
+  --> tests/ui/unnecessary_clippy_cfg.rs:10:1
    |
-LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]`
+LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:4:1
+  --> tests/ui/unnecessary_clippy_cfg.rs:13:1
    |
-LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]`
+LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:6:37
+  --> tests/ui/unnecessary_clippy_cfg.rs:15:36
    |
-LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: write instead: `#![deny(clippy::non_minimal_cfg)]`
+   = note: write instead: `#[deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:8:37
+  --> tests/ui/unnecessary_clippy_cfg.rs:17:36
    |
-LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))]
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: write instead: `#![deny(clippy::non_minimal_cfg)]`
+   = note: write instead: `#[deny(clippy::non_minimal_cfg)]`
 
 error: no need to put clippy lints behind a `clippy` cfg
-  --> tests/ui/unnecessary_clippy_cfg.rs:10:1
+  --> tests/ui/unnecessary_clippy_cfg.rs:19:1
    |
-LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]`
+LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]`
 
 error: duplicated attribute
   --> tests/ui/unnecessary_clippy_cfg.rs:8:26
diff --git a/tests/ui/attributes/key-value-expansion-scope-pass.rs b/tests/ui/attributes/key-value-expansion-scope-pass.rs
new file mode 100644
index 0000000000000..6b1f4e5bd4bf0
--- /dev/null
+++ b/tests/ui/attributes/key-value-expansion-scope-pass.rs
@@ -0,0 +1,18 @@
+// Imports suppress the `out_of_scope_macro_calls` lint.
+
+//@ check-pass
+//@ edition:2018
+
+#![doc = in_root!()]
+
+macro_rules! in_root { () => { "" } }
+use in_root;
+
+mod macros_stay {
+    #![doc = in_mod!()]
+
+    macro_rules! in_mod { () => { "" } }
+    use in_mod;
+}
+
+fn main() {}
diff --git a/tests/ui/attributes/key-value-expansion-scope.rs b/tests/ui/attributes/key-value-expansion-scope.rs
index b84fe4873c371..b6eab1571d490 100644
--- a/tests/ui/attributes/key-value-expansion-scope.rs
+++ b/tests/ui/attributes/key-value-expansion-scope.rs
@@ -1,6 +1,8 @@
-#![doc = in_root!()] // FIXME, this is a bug
+#![doc = in_root!()] //~ WARN cannot find macro `in_root` in this scope
+                     //~| WARN this was previously accepted by the compiler
 #![doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope
-#![doc = in_mod_escape!()] // FIXME, this is a bug
+#![doc = in_mod_escape!()] //~ WARN cannot find macro `in_mod_escape` in this scope
+                           //~| WARN this was previously accepted by the compiler
 #![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope
 
 #[doc = in_root!()] //~ ERROR cannot find macro `in_root` in this scope
@@ -16,8 +18,11 @@ fn before() {
 
 macro_rules! in_root { () => { "" } }
 
+#[doc = in_mod!()] //~ WARN cannot find macro `in_mod` in this scope
+                   //~| WARN this was previously accepted by the compiler
 mod macros_stay {
-    #![doc = in_mod!()] // FIXME, this is a bug
+    #![doc = in_mod!()] //~ WARN cannot find macro `in_mod` in this scope
+                        //~| WARN this was previously accepted by the compiler
 
     macro_rules! in_mod { () => { "" } }
 
@@ -28,8 +33,11 @@ mod macros_stay {
 }
 
 #[macro_use]
+#[doc = in_mod_escape!()] //~ WARN cannot find macro `in_mod_escape` in this scope
+                          //~| WARN this was previously accepted by the compiler
 mod macros_escape {
-    #![doc = in_mod_escape!()] // FIXME, this is a bug
+    #![doc = in_mod_escape!()] //~ WARN cannot find macro `in_mod_escape` in this scope
+                               //~| WARN this was previously accepted by the compiler
 
     macro_rules! in_mod_escape { () => { "" } }
 
@@ -39,8 +47,9 @@ mod macros_escape {
     }
 }
 
+#[doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope
 fn block() {
-    #![doc = in_block!()]  //~ ERROR cannot find macro `in_block` in this scope
+    #![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope
 
     macro_rules! in_block { () => { "" } }
 
diff --git a/tests/ui/attributes/key-value-expansion-scope.stderr b/tests/ui/attributes/key-value-expansion-scope.stderr
index a66ee9b17fb92..d22fef7dd251e 100644
--- a/tests/ui/attributes/key-value-expansion-scope.stderr
+++ b/tests/ui/attributes/key-value-expansion-scope.stderr
@@ -1,5 +1,5 @@
 error: cannot find macro `in_mod` in this scope
-  --> $DIR/key-value-expansion-scope.rs:2:10
+  --> $DIR/key-value-expansion-scope.rs:3:10
    |
 LL | #![doc = in_mod!()]
    |          ^^^^^^
@@ -7,7 +7,7 @@ LL | #![doc = in_mod!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:4:10
+  --> $DIR/key-value-expansion-scope.rs:6:10
    |
 LL | #![doc = in_block!()]
    |          ^^^^^^^^
@@ -15,7 +15,7 @@ LL | #![doc = in_block!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_root` in this scope
-  --> $DIR/key-value-expansion-scope.rs:6:9
+  --> $DIR/key-value-expansion-scope.rs:8:9
    |
 LL | #[doc = in_root!()]
    |         ^^^^^^^
@@ -23,7 +23,7 @@ LL | #[doc = in_root!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod` in this scope
-  --> $DIR/key-value-expansion-scope.rs:7:9
+  --> $DIR/key-value-expansion-scope.rs:9:9
    |
 LL | #[doc = in_mod!()]
    |         ^^^^^^
@@ -31,7 +31,7 @@ LL | #[doc = in_mod!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod_escape` in this scope
-  --> $DIR/key-value-expansion-scope.rs:8:9
+  --> $DIR/key-value-expansion-scope.rs:10:9
    |
 LL | #[doc = in_mod_escape!()]
    |         ^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL | #[doc = in_mod_escape!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:9:9
+  --> $DIR/key-value-expansion-scope.rs:11:9
    |
 LL | #[doc = in_block!()]
    |         ^^^^^^^^
@@ -47,7 +47,7 @@ LL | #[doc = in_block!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_root` in this scope
-  --> $DIR/key-value-expansion-scope.rs:11:14
+  --> $DIR/key-value-expansion-scope.rs:13:14
    |
 LL |     #![doc = in_root!()]
    |              ^^^^^^^
@@ -55,7 +55,7 @@ LL |     #![doc = in_root!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod` in this scope
-  --> $DIR/key-value-expansion-scope.rs:12:14
+  --> $DIR/key-value-expansion-scope.rs:14:14
    |
 LL |     #![doc = in_mod!()]
    |              ^^^^^^
@@ -63,7 +63,7 @@ LL |     #![doc = in_mod!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod_escape` in this scope
-  --> $DIR/key-value-expansion-scope.rs:13:14
+  --> $DIR/key-value-expansion-scope.rs:15:14
    |
 LL |     #![doc = in_mod_escape!()]
    |              ^^^^^^^^^^^^^
@@ -71,7 +71,7 @@ LL |     #![doc = in_mod_escape!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:14:14
+  --> $DIR/key-value-expansion-scope.rs:16:14
    |
 LL |     #![doc = in_block!()]
    |              ^^^^^^^^
@@ -79,7 +79,15 @@ LL |     #![doc = in_block!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:43:14
+  --> $DIR/key-value-expansion-scope.rs:50:9
+   |
+LL | #[doc = in_block!()]
+   |         ^^^^^^^^
+   |
+   = help: have you added the `#[macro_use]` on the module/import?
+
+error: cannot find macro `in_block` in this scope
+  --> $DIR/key-value-expansion-scope.rs:52:14
    |
 LL |     #![doc = in_block!()]
    |              ^^^^^^^^
@@ -87,7 +95,7 @@ LL |     #![doc = in_block!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod` in this scope
-  --> $DIR/key-value-expansion-scope.rs:54:9
+  --> $DIR/key-value-expansion-scope.rs:63:9
    |
 LL | #[doc = in_mod!()]
    |         ^^^^^^
@@ -95,7 +103,7 @@ LL | #[doc = in_mod!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:56:9
+  --> $DIR/key-value-expansion-scope.rs:65:9
    |
 LL | #[doc = in_block!()]
    |         ^^^^^^^^
@@ -103,7 +111,7 @@ LL | #[doc = in_block!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_mod` in this scope
-  --> $DIR/key-value-expansion-scope.rs:59:14
+  --> $DIR/key-value-expansion-scope.rs:68:14
    |
 LL |     #![doc = in_mod!()]
    |              ^^^^^^
@@ -111,12 +119,73 @@ LL |     #![doc = in_mod!()]
    = help: have you added the `#[macro_use]` on the module/import?
 
 error: cannot find macro `in_block` in this scope
-  --> $DIR/key-value-expansion-scope.rs:61:14
+  --> $DIR/key-value-expansion-scope.rs:70:14
    |
 LL |     #![doc = in_block!()]
    |              ^^^^^^^^
    |
    = help: have you added the `#[macro_use]` on the module/import?
 
-error: aborting due to 15 previous errors
+warning: cannot find macro `in_root` in this scope
+  --> $DIR/key-value-expansion-scope.rs:1:10
+   |
+LL | #![doc = in_root!()]
+   |          ^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+   = note: `#[warn(out_of_scope_macro_calls)]` on by default
+
+warning: cannot find macro `in_mod_escape` in this scope
+  --> $DIR/key-value-expansion-scope.rs:4:10
+   |
+LL | #![doc = in_mod_escape!()]
+   |          ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+
+warning: cannot find macro `in_mod` in this scope
+  --> $DIR/key-value-expansion-scope.rs:21:9
+   |
+LL | #[doc = in_mod!()]
+   |         ^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+
+warning: cannot find macro `in_mod` in this scope
+  --> $DIR/key-value-expansion-scope.rs:24:14
+   |
+LL |     #![doc = in_mod!()]
+   |              ^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+
+warning: cannot find macro `in_mod_escape` in this scope
+  --> $DIR/key-value-expansion-scope.rs:36:9
+   |
+LL | #[doc = in_mod_escape!()]
+   |         ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+
+warning: cannot find macro `in_mod_escape` in this scope
+  --> $DIR/key-value-expansion-scope.rs:39:14
+   |
+LL |     #![doc = in_mod_escape!()]
+   |              ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
+   = help: import `macro_rules` with `use` to make it callable above its definition
+
+error: aborting due to 16 previous errors; 6 warnings emitted
 
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index 815013733a98b..609526150ba2e 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -1,47 +1,47 @@
 error[E0658]: the `#[optimize]` attribute is an experimental feature
-  --> $DIR/feature-gate-optimize_attribute.rs:7:1
+  --> $DIR/feature-gate-optimize_attribute.rs:2:1
    |
-LL | #[optimize(size)]
-   | ^^^^^^^^^^^^^^^^^
+LL | #![optimize(speed)]
+   | ^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the `#[optimize]` attribute is an experimental feature
-  --> $DIR/feature-gate-optimize_attribute.rs:10:1
+  --> $DIR/feature-gate-optimize_attribute.rs:4:1
    |
-LL | #[optimize(speed)]
-   | ^^^^^^^^^^^^^^^^^^
+LL | #[optimize(size)]
+   | ^^^^^^^^^^^^^^^^^
    |
    = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the `#[optimize]` attribute is an experimental feature
-  --> $DIR/feature-gate-optimize_attribute.rs:13:1
+  --> $DIR/feature-gate-optimize_attribute.rs:7:1
    |
-LL | #[optimize(banana)]
-   | ^^^^^^^^^^^^^^^^^^^
+LL | #[optimize(size)]
+   | ^^^^^^^^^^^^^^^^^
    |
    = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the `#[optimize]` attribute is an experimental feature
-  --> $DIR/feature-gate-optimize_attribute.rs:4:1
+  --> $DIR/feature-gate-optimize_attribute.rs:10:1
    |
-LL | #[optimize(size)]
-   | ^^^^^^^^^^^^^^^^^
+LL | #[optimize(speed)]
+   | ^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the `#[optimize]` attribute is an experimental feature
-  --> $DIR/feature-gate-optimize_attribute.rs:2:1
+  --> $DIR/feature-gate-optimize_attribute.rs:13:1
    |
-LL | #![optimize(speed)]
+LL | #[optimize(banana)]
    | ^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
diff --git a/tests/ui/feature-gates/feature-gate-staged_api.stderr b/tests/ui/feature-gates/feature-gate-staged_api.stderr
index 1a9fcb02b0dee..86ba509ae7d10 100644
--- a/tests/ui/feature-gates/feature-gate-staged_api.stderr
+++ b/tests/ui/feature-gates/feature-gate-staged_api.stderr
@@ -1,15 +1,15 @@
-error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/feature-gate-staged_api.rs:8:1
-   |
-LL | #[stable(feature = "a", since = "3.3.3")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error[E0734]: stability attributes may not be used outside of the standard library
   --> $DIR/feature-gate-staged_api.rs:1:1
    |
 LL | #![stable(feature = "a", since = "3.3.3")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error[E0734]: stability attributes may not be used outside of the standard library
+  --> $DIR/feature-gate-staged_api.rs:8:1
+   |
+LL | #[stable(feature = "a", since = "3.3.3")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0734`.
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index 88732f75cb4e1..e43cef7c15078 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -42,6 +42,20 @@ warning: unknown lint: `x5100`
 LL | #![deny(x5100)]
    |         ^^^^^
 
+warning: use of deprecated attribute `crate_id`: no longer used
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1
+   |
+LL | #![crate_id = "10"]
+   | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+   = note: `#[warn(deprecated)]` on by default
+
+warning: use of deprecated attribute `no_start`: no longer used
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:94:1
+   |
+LL | #![no_start]
+   | ^^^^^^^^^^^^ help: remove this attribute
+
 warning: unknown lint: `x5400`
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:8
    |
@@ -186,20 +200,6 @@ warning: unknown lint: `x5100`
 LL |     #[deny(x5100)] impl S { }
    |            ^^^^^
 
-warning: use of deprecated attribute `crate_id`: no longer used
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1
-   |
-LL | #![crate_id = "10"]
-   | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-   = note: `#[warn(deprecated)]` on by default
-
-warning: use of deprecated attribute `no_start`: no longer used
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:94:1
-   |
-LL | #![no_start]
-   | ^^^^^^^^^^^^ help: remove this attribute
-
 warning: `#[macro_export]` only has an effect on macro definitions
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:1
    |
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr b/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr
index 677fef3a926b5..bac3b018e2e43 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr
@@ -1,3 +1,15 @@
+error[E0734]: stability attributes may not be used outside of the standard library
+  --> $DIR/issue-43106-gating-of-stable.rs:7:1
+   |
+LL | #![stable()]
+   | ^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+  --> $DIR/issue-43106-gating-of-stable.rs:10:1
+   |
+LL | #[stable()]
+   | ^^^^^^^^^^^
+
 error[E0734]: stability attributes may not be used outside of the standard library
   --> $DIR/issue-43106-gating-of-stable.rs:14:9
    |
@@ -28,18 +40,6 @@ error[E0734]: stability attributes may not be used outside of the standard libra
 LL |     #[stable()]
    |     ^^^^^^^^^^^
 
-error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-43106-gating-of-stable.rs:10:1
-   |
-LL | #[stable()]
-   | ^^^^^^^^^^^
-
-error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-43106-gating-of-stable.rs:7:1
-   |
-LL | #![stable()]
-   | ^^^^^^^^^^^^
-
 error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0734`.
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr b/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr
index a2f361878c6db..9ea60f838008c 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr
@@ -1,3 +1,15 @@
+error[E0734]: stability attributes may not be used outside of the standard library
+  --> $DIR/issue-43106-gating-of-unstable.rs:7:1
+   |
+LL | #![unstable()]
+   | ^^^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+  --> $DIR/issue-43106-gating-of-unstable.rs:10:1
+   |
+LL | #[unstable()]
+   | ^^^^^^^^^^^^^
+
 error[E0734]: stability attributes may not be used outside of the standard library
   --> $DIR/issue-43106-gating-of-unstable.rs:14:9
    |
@@ -28,18 +40,6 @@ error[E0734]: stability attributes may not be used outside of the standard libra
 LL |     #[unstable()]
    |     ^^^^^^^^^^^^^
 
-error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-43106-gating-of-unstable.rs:10:1
-   |
-LL | #[unstable()]
-   | ^^^^^^^^^^^^^
-
-error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-43106-gating-of-unstable.rs:7:1
-   |
-LL | #![unstable()]
-   | ^^^^^^^^^^^^^^
-
 error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0734`.
diff --git a/tests/ui/stability-attribute/issue-106589.stderr b/tests/ui/stability-attribute/issue-106589.stderr
index ccf3f7164e3e1..c14ad2da04b69 100644
--- a/tests/ui/stability-attribute/issue-106589.stderr
+++ b/tests/ui/stability-attribute/issue-106589.stderr
@@ -1,13 +1,13 @@
 error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-106589.rs:6:1
+  --> $DIR/issue-106589.rs:3:1
    |
-LL | #[unstable(feature = "foo", issue = "none")]
+LL | #![stable(feature = "foo", since = "1.0.0")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/issue-106589.rs:3:1
+  --> $DIR/issue-106589.rs:6:1
    |
-LL | #![stable(feature = "foo", since = "1.0.0")]
+LL | #[unstable(feature = "foo", issue = "none")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors