diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 9ed478e6fccce..bc45c57596e15 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -18,7 +18,8 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_feature::Features;
-use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS;
+use rustc_lint_defs::builtin::{OR_PATTERNS_BACK_COMPAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS};
+use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::parser::Parser;
 use rustc_session::parse::ParseSess;
 use rustc_session::Session;
@@ -951,8 +952,32 @@ fn check_matcher_core(
         // Now `last` holds the complete set of NT tokens that could
         // end the sequence before SUFFIX. Check that every one works with `suffix`.
         for token in &last.tokens {
-            if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token {
+            if let TokenTree::MetaVarDecl(span, name, Some(kind)) = *token {
                 for next_token in &suffix_first.tokens {
+                    // Check if the old pat is used and the next token is `|`.
+                    if let NonterminalKind::Pat2015 { inferred: true } = kind {
+                        if let TokenTree::Token(token) = next_token {
+                            if let BinOp(token) = token.kind {
+                                if let token::BinOpToken::Or = token {
+                                    // It is suggestion to use pat2015, for example: $x:pat -> $x:pat2015.
+                                    let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                                        span,
+                                        name,
+                                        Some(NonterminalKind::Pat2015 { inferred: false }),
+                                    ));
+                                    sess.buffer_lint_with_diagnostic(
+                                        &OR_PATTERNS_BACK_COMPAT,
+                                        span,
+                                        ast::CRATE_NODE_ID,
+                                        &*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",),
+                                        BuiltinLintDiagnostics::OrPatternsBackCompat(
+                                            span, suggestion,
+                                        ),
+                                    );
+                                }
+                            }
+                        }
+                    }
                     match is_in_follow(next_token, kind) {
                         IsInFollow::Yes => {}
                         IsInFollow::No(possible) => {
@@ -1080,7 +1105,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                     _ => IsInFollow::No(TOKENS),
                 }
             }
-            NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => {
+            NonterminalKind::Pat2015 { .. } => {
                 const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
                 match tok {
                     TokenTree::Token(token) => match token.kind {
@@ -1091,6 +1116,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                     _ => IsInFollow::No(TOKENS),
                 }
             }
+            NonterminalKind::Pat2021 { .. } => {
+                const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
+                match tok {
+                    TokenTree::Token(token) => match token.kind {
+                        FatArrow | Comma | Eq => IsInFollow::Yes,
+                        Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
+                        _ => IsInFollow::No(TOKENS),
+                    },
+                    _ => IsInFollow::No(TOKENS),
+                }
+            }
             NonterminalKind::Path | NonterminalKind::Ty => {
                 const TOKENS: &[&str] = &[
                     "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 14bd823ea2266..b3a19bfbf7532 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -709,6 +709,9 @@ pub trait LintContext: Sized {
                 BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
                     db.note(&note);
                 }
+                BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
+                    db.span_suggestion(span, "use pat2015 to preserve semantics", suggestion, Applicability::MachineApplicable);
+                }
             }
             // Rewrap `db`, and pass control to the user.
             decorate(LintDiagnosticBuilder::new(db));
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 4eaee7bf6e854..b602490905c29 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2959,6 +2959,7 @@ declare_lint_pass! {
         DISJOINT_CAPTURE_DROP_REORDER,
         LEGACY_DERIVE_HELPERS,
         PROC_MACRO_BACK_COMPAT,
+        OR_PATTERNS_BACK_COMPAT,
     ]
 }
 
@@ -3136,3 +3137,37 @@ declare_lint! {
         })
     };
 }
+
+declare_lint! {
+    /// The `or_patterns_back_compat` lint detects usage of old versions of or-patterns.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(or_patterns_back_compat)]
+    /// macro_rules! match_any {
+    ///     ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
+    ///         match $expr {
+    ///             $(
+    ///                 $( $pat => $expr_arm, )+
+    ///             )+
+    ///         }
+    ///     };
+    /// }
+    ///
+    /// fn main() {
+    ///     let result: Result<i64, i32> = Err(42);
+    ///     let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
+    ///     assert_eq!(int, 42);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In Rust 2021, the pat matcher will match new patterns, which include the | character.
+    pub OR_PATTERNS_BACK_COMPAT,
+    Allow,
+    "detects usage of old versions of or-patterns",
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 400b367095ec3..70475563a4abe 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -267,6 +267,7 @@ pub enum BuiltinLintDiagnostics {
     LegacyDeriveHelpers(Span),
     ExternDepSpec(String, ExternDepSpec),
     ProcMacroBackCompat(String),
+    OrPatternsBackCompat(Span, String),
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.fixed b/src/test/ui/macros/macro-or-patterns-back-compat.fixed
new file mode 100644
index 0000000000000..4e8b057457c35
--- /dev/null
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.fixed
@@ -0,0 +1,26 @@
+// ignore-tidy-linelength
+// run-rustfix
+
+#![feature(edition_macro_pats)]
+#![deny(or_patterns_back_compat)]
+#![allow(unused_macros)]
+macro_rules! foo { ($x:pat2015 | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
+macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
+macro_rules! ogg { ($x:pat2015 | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! match_any {
+    ( $expr:expr , $( $( $pat:pat2015 )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+        match $expr {
+            $(
+                $( $pat => $expr_arm, )+
+            )+
+        }
+    };
+}
+
+fn main() {
+    let result: Result<i64, i32> = Err(42);
+    let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
+    assert_eq!(int, 42);
+}
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.rs b/src/test/ui/macros/macro-or-patterns-back-compat.rs
new file mode 100644
index 0000000000000..023abae36d06b
--- /dev/null
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.rs
@@ -0,0 +1,26 @@
+// ignore-tidy-linelength
+// run-rustfix
+
+#![feature(edition_macro_pats)]
+#![deny(or_patterns_back_compat)]
+#![allow(unused_macros)]
+macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
+macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
+macro_rules! ogg { ($x:pat | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+macro_rules! match_any {
+    ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+        match $expr {
+            $(
+                $( $pat => $expr_arm, )+
+            )+
+        }
+    };
+}
+
+fn main() {
+    let result: Result<i64, i32> = Err(42);
+    let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
+    assert_eq!(int, 42);
+}
diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.stderr b/src/test/ui/macros/macro-or-patterns-back-compat.stderr
new file mode 100644
index 0000000000000..29bbd696033c2
--- /dev/null
+++ b/src/test/ui/macros/macro-or-patterns-back-compat.stderr
@@ -0,0 +1,32 @@
+error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+  --> $DIR/macro-or-patterns-back-compat.rs:7:21
+   |
+LL | macro_rules! foo { ($x:pat | $y:pat) => {} }
+   |                     ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
+   |
+note: the lint level is defined here
+  --> $DIR/macro-or-patterns-back-compat.rs:5:9
+   |
+LL | #![deny(or_patterns_back_compat)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+  --> $DIR/macro-or-patterns-back-compat.rs:8:23
+   |
+LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
+   |                       ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
+
+error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+  --> $DIR/macro-or-patterns-back-compat.rs:11:21
+   |
+LL | macro_rules! ogg { ($x:pat | $y:pat2015) => {} }
+   |                     ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
+
+error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
+  --> $DIR/macro-or-patterns-back-compat.rs:13:26
+   |
+LL |     ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
+   |                          ^^^^^^^^ help: use pat2015 to preserve semantics: `$pat:pat2015`
+
+error: aborting due to 4 previous errors
+