From c4766cfe69ecfa70f2efc1b166a3ddc7ff518f9e Mon Sep 17 00:00:00 2001
From: Caio <c410.f3r@gmail.com>
Date: Thu, 18 May 2023 22:11:13 -0300
Subject: [PATCH 1/2] [RFC-2011] Expand `matches!`

---
 compiler/rustc_ast/src/ast.rs                 |   4 +
 compiler/rustc_ast/src/mut_visit.rs           |  27 +++--
 compiler/rustc_ast/src/util/parser.rs         |   2 +
 compiler/rustc_ast/src/visit.rs               |   5 +
 compiler/rustc_ast_lowering/src/expr.rs       |   7 ++
 .../rustc_ast_pretty/src/pprust/state/expr.rs |  14 +++
 compiler/rustc_builtin_macros/src/assert.rs   |  44 +++----
 .../src/assert/context.rs                     |  15 ++-
 compiler/rustc_parse/src/parser/expr.rs       | 110 +++++++++++++++---
 compiler/rustc_passes/src/hir_stats.rs        |   3 +-
 compiler/rustc_span/src/symbol.rs             |   1 +
 library/core/src/macros/mod.rs                |  29 ++++-
 src/tools/clippy/clippy_utils/src/sugg.rs     |   1 +
 src/tools/rustfmt/src/expr.rs                 |   1 +
 src/tools/rustfmt/src/utils.rs                |   1 +
 .../const_goto.issue_77355_opt.ConstGoto.diff |  18 +--
 .../offset_of.concrete.ConstProp.diff         |  16 +--
 .../offset_of.generic.ConstProp.diff          |   8 +-
 ...ranches.foo.MatchBranchSimplification.diff |  28 ++---
 .../all-expr-kinds.rs                         |   3 +
 tests/ui/stdlib-unit-tests/matches2021.rs     |   2 +
 21 files changed, 251 insertions(+), 88 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 43b429f6947ec..9562dc7695227 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1291,6 +1291,7 @@ impl Expr {
             ExprKind::Struct(..) => ExprPrecedence::Struct,
             ExprKind::Repeat(..) => ExprPrecedence::Repeat,
             ExprKind::Paren(..) => ExprPrecedence::Paren,
+            ExprKind::Matches(..) => ExprPrecedence::Matches,
             ExprKind::Try(..) => ExprPrecedence::Try,
             ExprKind::Yield(..) => ExprPrecedence::Yield,
             ExprKind::Yeet(..) => ExprPrecedence::Yeet,
@@ -1488,6 +1489,9 @@ pub enum ExprKind {
     /// Output of the `offset_of!()` macro.
     OffsetOf(P<Ty>, P<[Ident]>),
 
+    /// Output of the `matches!()` macro.
+    Matches(P<Expr>, P<Arm>, P<Arm>),
+
     /// A macro invocation; pre-expansion.
     MacCall(P<MacCall>),
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 66b94d12a32c6..fa5d8d61cc29d 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -148,6 +148,10 @@ pub trait MutVisitor: Sized {
         noop_visit_anon_const(c, self);
     }
 
+    fn visit_arm(&mut self, arm: &mut P<Arm>) {
+        noop_visit_arm(arm, self)
+    }
+
     fn visit_expr(&mut self, e: &mut P<Expr>) {
         noop_visit_expr(e, self);
     }
@@ -443,13 +447,7 @@ pub 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);
-    vis.visit_pat(pat);
-    visit_opt(guard, |guard| vis.visit_expr(guard));
-    vis.visit_expr(body);
-    vis.visit_span(span);
+    noop_visit_arm(&mut arm, vis);
     smallvec![arm]
 }
 
@@ -695,6 +693,16 @@ pub fn visit_attr_tt<T: MutVisitor>(tt: &mut AttrTokenTree, vis: &mut T) {
     }
 }
 
+fn noop_visit_arm<T: MutVisitor>(arm: &mut Arm, vis: &mut T) {
+    let Arm { attrs, pat, guard, body, span, id, is_placeholder: _ } = arm;
+    visit_attrs(attrs, vis);
+    vis.visit_id(id);
+    vis.visit_pat(pat);
+    visit_opt(guard, |guard| vis.visit_expr(guard));
+    vis.visit_expr(body);
+    vis.visit_span(span);
+}
+
 // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
 pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
     match tt {
@@ -1334,6 +1342,11 @@ pub fn noop_visit_expr<T: MutVisitor>(
             vis.visit_expr(f);
             visit_thin_exprs(args, vis);
         }
+        ExprKind::Matches(expr, true_arm, false_arm) => {
+            vis.visit_expr(expr);
+            vis.visit_arm(true_arm);
+            vis.visit_arm(false_arm);
+        }
         ExprKind::MethodCall(box MethodCall {
             seg: PathSegment { ident, id, args: seg_args },
             receiver,
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 35afd5423721d..dbdb97c3c2019 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -267,6 +267,7 @@ pub enum ExprPrecedence {
     InlineAsm,
     OffsetOf,
     Mac,
+    Matches,
     FormatArgs,
 
     Array,
@@ -329,6 +330,7 @@ impl ExprPrecedence {
             | ExprPrecedence::Field
             | ExprPrecedence::Index
             | ExprPrecedence::Try
+            | ExprPrecedence::Matches
             | ExprPrecedence::InlineAsm
             | ExprPrecedence::Mac
             | ExprPrecedence::FormatArgs
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 275692ad5dda7..346c75a2d53ee 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -803,6 +803,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(callee_expression);
             walk_list!(visitor, visit_expr, arguments);
         }
+        ExprKind::Matches(expr, true_arm, false_arm) => {
+            visitor.visit_expr(expr);
+            visitor.visit_arm(true_arm);
+            visitor.visit_arm(false_arm);
+        }
         ExprKind::MethodCall(box MethodCall { seg, receiver, args, span: _ }) => {
             visitor.visit_path_segment(seg);
             visitor.visit_expr(receiver);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 5e0ab80c6ac9f..240d58296a02a 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -274,6 +274,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::InlineAsm(asm) => {
                     hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
                 }
+                ExprKind::Matches(expr, true_arm, false_arm) => hir::ExprKind::Match(
+                    self.lower_expr(expr),
+                    self.arena.alloc_from_iter(
+                        [true_arm, false_arm].iter().map(|elem| self.lower_arm(elem)),
+                    ),
+                    hir::MatchSource::Normal,
+                ),
                 ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
                 ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
                     self.lower_ty(
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 87c32ffce1214..8baf4b064dd07 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -555,6 +555,20 @@ impl<'a> State<'a> {
                 self.end();
                 self.pclose();
             }
+            ast::ExprKind::Matches(expr, true_arm, _) => {
+                self.word("builtin # matches");
+                self.popen();
+                self.rbox(0, Inconsistent);
+                self.print_expr(expr);
+                self.word(", ");
+                self.print_pat(&true_arm.pat);
+                if let Some(elem) = &true_arm.guard {
+                    self.word("if");
+                    self.print_expr(elem);
+                }
+                self.pclose();
+                self.end();
+            }
             ast::ExprKind::OffsetOf(container, fields) => {
                 self.word("builtin # offset_of");
                 self.popen();
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index ab4ea9c8c2052..69fde4f320048 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -31,31 +31,13 @@ pub fn expand_assert<'cx>(
     // context to pick up whichever is currently in scope.
     let call_site_span = cx.with_call_site_ctxt(span);
 
-    let panic_path = || {
-        if use_panic_2021(span) {
-            // On edition 2021, we always call `$crate::panic::panic_2021!()`.
-            Path {
-                span: call_site_span,
-                segments: cx
-                    .std_path(&[sym::panic, sym::panic_2021])
-                    .into_iter()
-                    .map(|ident| PathSegment::from_ident(ident))
-                    .collect(),
-                tokens: None,
-            }
-        } else {
-            // Before edition 2021, we call `panic!()` unqualified,
-            // such that it calls either `std::panic!()` or `core::panic!()`.
-            Path::from_ident(Ident::new(sym::panic, call_site_span))
-        }
-    };
-
     // Simply uses the user provided message instead of generating custom outputs
     let expr = if let Some(tokens) = custom_message {
+        let panic_path = panic_path(call_site_span, cx, span);
         let then = cx.expr(
             call_site_span,
             ExprKind::MacCall(P(MacCall {
-                path: panic_path(),
+                path: panic_path,
                 args: P(DelimArgs {
                     dspan: DelimSpan::from_single(call_site_span),
                     delim: MacDelimiter::Parenthesis,
@@ -69,7 +51,8 @@ pub fn expand_assert<'cx>(
     //
     // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
     else if let Some(features) = cx.ecfg.features && features.generic_assert {
-        context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
+        let panic_path = panic_path(call_site_span, cx, span);
+        context::Context::new(cx, call_site_span).build(cond_expr, panic_path)
     }
     // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
     // string
@@ -110,6 +93,25 @@ fn expr_if_not(
     cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
 }
 
+fn panic_path(call_site_span: Span, cx: &mut ExtCtxt<'_>, span: Span) -> Path {
+    if use_panic_2021(span) {
+        // On edition 2021, we always call `$crate::panic::panic_2021!()`.
+        Path {
+            span: call_site_span,
+            segments: cx
+                .std_path(&[sym::panic, sym::panic_2021])
+                .into_iter()
+                .map(|ident| PathSegment::from_ident(ident))
+                .collect(),
+            tokens: None,
+        }
+    } else {
+        // Before edition 2021, we call `panic!()` unqualified,
+        // such that it calls either `std::panic!()` or `core::panic!()`.
+        Path::from_ident(Ident::new(sym::panic, call_site_span))
+    }
+}
+
 fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
     let mut parser = cx.new_parser_from_tts(stream);
 
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index ea830a0ce60df..39679e9a881c3 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -22,7 +22,7 @@ pub(super) struct Context<'cx, 'a> {
     best_case_captures: Vec<Stmt>,
     // Top-level `let captureN = Capture::new()` statements
     capture_decls: Vec<Capture>,
-    cx: &'cx ExtCtxt<'a>,
+    cx: &'cx mut ExtCtxt<'a>,
     // Formatting string used for debugging
     fmt_string: String,
     // If the current expression being visited consumes itself. Used to construct
@@ -41,7 +41,7 @@ pub(super) struct Context<'cx, 'a> {
 }
 
 impl<'cx, 'a> Context<'cx, 'a> {
-    pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
+    pub(super) fn new(cx: &'cx mut ExtCtxt<'a>, span: Span) -> Self {
         Self {
             best_case_captures: <_>::default(),
             capture_decls: <_>::default(),
@@ -85,8 +85,8 @@ impl<'cx, 'a> Context<'cx, 'a> {
 
         let mut assert_then_stmts = ThinVec::with_capacity(2);
         assert_then_stmts.extend(best_case_captures);
-        assert_then_stmts.push(self.cx.stmt_expr(panic));
-        let assert_then = self.cx.block(span, assert_then_stmts);
+        assert_then_stmts.push(cx.stmt_expr(panic));
+        let assert_then = cx.block(span, assert_then_stmts);
 
         let mut stmts = ThinVec::with_capacity(4);
         stmts.push(initial_imports);
@@ -237,6 +237,9 @@ impl<'cx, 'a> Context<'cx, 'a> {
                 self.manage_cond_expr(prefix);
                 self.manage_cond_expr(suffix);
             }
+            ExprKind::Matches(expr, _, _) => {
+                self.manage_cond_expr(expr);
+            }
             ExprKind::MethodCall(call) => {
                 for arg in &mut call.args {
                     self.manage_cond_expr(arg);
@@ -295,17 +298,17 @@ impl<'cx, 'a> Context<'cx, 'a> {
             | ExprKind::Continue(_)
             | ExprKind::Err
             | ExprKind::Field(_, _)
-            | ExprKind::FormatArgs(_)
             | ExprKind::ForLoop(_, _, _, _)
+            | ExprKind::FormatArgs(_)
             | ExprKind::If(_, _, _)
             | ExprKind::IncludedBytes(..)
             | ExprKind::InlineAsm(_)
-            | ExprKind::OffsetOf(_, _)
             | ExprKind::Let(_, _, _)
             | ExprKind::Lit(_)
             | ExprKind::Loop(_, _, _)
             | ExprKind::MacCall(_)
             | ExprKind::Match(_, _)
+            | ExprKind::OffsetOf(_, _)
             | ExprKind::Path(_, _)
             | ExprKind::Ret(_)
             | ExprKind::Try(_)
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index ee712a8e1b5db..b0eaf0898cb1f 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -9,6 +9,7 @@ use super::{
 use crate::errors;
 use crate::maybe_recover_from_interpolated_ty_qpath;
 use ast::{Path, PathSegment};
+use core::borrow::BorrowMut;
 use core::mem;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -1473,17 +1474,34 @@ impl<'a> Parser<'a> {
         };
 
         // `!`, as an operator, is prefix, so we know this isn't that.
-        let (span, kind) = if self.eat(&token::Not) {
+        let expr = if self.eat(&token::Not) {
+            fn mac_call_expr(args: P<ast::DelimArgs>, lo: Span, path: Path, this: &mut Parser<'_>) -> P<Expr> {
+                this.mk_expr(
+                    lo.to(this.prev_token.span),
+                    ExprKind::MacCall(P(MacCall { path, args }))
+                )
+            }
             // MACRO INVOCATION expression
             if qself.is_some() {
                 self.sess.emit_err(errors::MacroInvocationWithQualifiedPath(path.span));
             }
+            let args = self.parse_delim_args()?;
             let lo = path.span;
-            let mac = P(MacCall {
-                path,
-                args: self.parse_delim_args()?,
-            });
-            (lo.to(self.prev_token.span), ExprKind::MacCall(mac))
+            if let Some(first_segment) = path.segments.first() {
+                match parse_builtin_mac(&first_segment.ident, lo, || {
+                    Parser::new(&self.sess, args.tokens.clone(), false, None)
+                }) {
+                    Err(err) => {
+                        err.cancel();
+                        mac_call_expr(args, lo, path, self)
+                    },
+                    Ok(None) => mac_call_expr(args, lo, path, self),
+                    Ok(Some(builtin_expr)) => builtin_expr,
+                }
+            }
+            else {
+                mac_call_expr(args, lo, path, self)
+            }
         } else if self.check(&token::OpenDelim(Delimiter::Brace))
             && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)
         {
@@ -1492,10 +1510,9 @@ impl<'a> Parser<'a> {
             }
             return expr;
         } else {
-            (path.span, ExprKind::Path(qself, path))
+            self.mk_expr(path.span, ExprKind::Path(qself, path))
         };
 
-        let expr = self.mk_expr(span, kind);
         self.maybe_recover_from_bad_qpath(expr)
     }
 
@@ -1774,13 +1791,7 @@ impl<'a> Parser<'a> {
 
     /// Parse `builtin # ident(args,*)`.
     fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
-        self.parse_builtin(|this, lo, ident| {
-            if ident.name == sym::offset_of {
-                return Ok(Some(this.parse_expr_offset_of(lo)?));
-            }
-
-            Ok(None)
-        })
+        self.parse_builtin(|this, lo, ident| parse_builtin_mac(&ident, lo, || this))
     }
 
     pub(crate) fn parse_builtin<T>(
@@ -1827,6 +1838,55 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into())))
     }
 
+    pub(crate) fn parse_expr_matches(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
+        let expr = self.parse_expr()?;
+        self.expect(&TokenKind::Comma)?;
+        let pat = self.parse_pat_allow_top_alt(
+            None,
+            RecoverComma::No,
+            RecoverColon::Yes,
+            CommaRecoveryMode::LikelyTuple,
+        )?;
+        let guard = if self.eat_keyword(kw::If) {
+            let guard = self.parse_expr()?;
+            Some(guard)
+        } else {
+            None
+        };
+        let _ = self.eat(&token::Comma);
+        let span = lo.to(self.token.span);
+        let true_arm = Arm {
+            attrs: <_>::default(),
+            pat,
+            guard,
+            body: self.mk_expr(
+                span,
+                ExprKind::Lit(token::Lit::new(token::LitKind::Bool, kw::True, None)),
+            ),
+            span,
+            id: DUMMY_NODE_ID,
+            is_placeholder: false,
+        };
+        let false_arm = Arm {
+            attrs: <_>::default(),
+            pat: P(ast::Pat {
+                id: DUMMY_NODE_ID,
+                kind: ast::PatKind::Wild,
+                span,
+                tokens: <_>::default(),
+            }),
+            guard: None,
+            body: self.mk_expr(
+                span,
+                ExprKind::Lit(token::Lit::new(token::LitKind::Bool, kw::False, None)),
+            ),
+            span,
+            id: DUMMY_NODE_ID,
+            is_placeholder: false,
+        };
+        Ok(self.mk_expr(lo.to(self.token.span), ExprKind::Matches(expr, P(true_arm), P(false_arm))))
+    }
+
     /// Returns a string literal if the next token is a string literal.
     /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
     /// and returns `None` if the next token is not literal at all.
@@ -3317,7 +3377,7 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<Expr>> {
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
             let res = f(this, attrs)?;
-            let trailing = if this.restrictions.contains(Restrictions::STMT_EXPR)
+            let trailing = if this.restrictions.contains(Restrictions::STMT_EXPRPAr)
                 && this.token.kind == token::Semi
             {
                 TrailingToken::Semi
@@ -3333,3 +3393,21 @@ impl<'a> Parser<'a> {
         })
     }
 }
+
+/// `parser` is a callback to avoid unecessary clones of `Parser`.
+pub(crate) fn parse_builtin_mac<'any, B>(
+    ident: &Ident,
+    lo: Span,
+    parser: impl FnOnce() -> B,
+) -> PResult<'any, Option<P<Expr>>>
+where
+    B: BorrowMut<Parser<'any>>,
+{
+    if ident.name == sym::matches {
+        return Ok(Some(parser().borrow_mut().parse_expr_matches(lo)?));
+    }
+    if ident.name == sym::offset_of {
+        return Ok(Some(parser().borrow_mut().parse_expr_offset_of(lo)?));
+    }
+    Ok(None)
+}
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index dc5e454074ded..72d2a7ebc3f75 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -569,7 +569,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
                 Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
                 If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
                 AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
-                InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
+                InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err,
+                Matches
             ]
         );
         ast_visit::walk_expr(self, e)
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 874d578fe1db3..c2a697fe6053b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -923,6 +923,7 @@ symbols! {
         masked,
         match_beginning_vert,
         match_default_bindings,
+        matches,
         matches_macro,
         maxnumf32,
         maxnumf64,
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index b24882ddb179f..abc5e71a7926c 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -336,9 +336,10 @@ pub macro debug_assert_matches($($arg:tt)*) {
 /// let bar = Some(4);
 /// assert!(matches!(bar, Some(x) if x > 2));
 /// ```
+#[cfg(bootstrap)]
+#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
 #[macro_export]
 #[stable(feature = "matches_macro", since = "1.42.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
 macro_rules! matches {
     ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
         match $expression {
@@ -348,6 +349,31 @@ macro_rules! matches {
     };
 }
 
+/// Returns whether the given expression matches any of the given patterns.
+///
+/// Like in a `match` expression, the pattern can be optionally followed by `if`
+/// and a guard expression that has access to names bound by the pattern.
+///
+/// # Examples
+///
+/// ```
+/// let foo = 'f';
+/// assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));
+///
+/// let bar = Some(4);
+/// assert!(matches!(bar, Some(x) if x > 2));
+/// ```
+#[allow_internal_unstable(builtin_syntax)]
+#[cfg(not(bootstrap))]
+#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
+#[macro_export]
+#[stable(feature = "matches_macro", since = "1.42.0")]
+macro_rules! matches {
+    ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+        builtin # matches($expression, $pattern $(if $guard)?)
+    };
+}
+
 /// Unwraps a result or propagates its error.
 ///
 /// The [`?` operator][propagating-errors] was added to replace `try!`
@@ -782,7 +808,6 @@ macro_rules! todo {
 /// with exception of expansion functions transforming macro inputs into outputs,
 /// those functions are provided by the compiler.
 pub(crate) mod builtin {
-
     /// Causes compilation to fail with the given error message when encountered.
     ///
     /// This macro should be used when a crate uses a conditional compilation strategy to provide
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 14f7f03016fbe..8b939442461e9 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -217,6 +217,7 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::Try(..)
             | ast::ExprKind::TryBlock(..)
             | ast::ExprKind::Tup(..)
+            | ast::ExprKind::Matches(..)
             | ast::ExprKind::Array(..)
             | ast::ExprKind::While(..)
             | ast::ExprKind::Await(..)
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 5dc628adb0c6f..a4a1685180102 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -401,6 +401,7 @@ pub(crate) fn format_expr(
         ast::ExprKind::Underscore => Some("_".to_owned()),
         ast::ExprKind::FormatArgs(..)
         | ast::ExprKind::IncludedBytes(..)
+        | ast::ExprKind::Matches(..)
         | ast::ExprKind::OffsetOf(..) => {
             // These do not occur in the AST because macros aren't expanded.
             unreachable!()
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index ca1716574071b..677433f6040b1 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -500,6 +500,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
         | ast::ExprKind::IncludedBytes(..)
         | ast::ExprKind::InlineAsm(..)
         | ast::ExprKind::OffsetOf(..)
+        | ast::ExprKind::Matches(..)
         | ast::ExprKind::Let(..)
         | ast::ExprKind::Path(..)
         | ast::ExprKind::Range(..)
diff --git a/tests/mir-opt/const_goto.issue_77355_opt.ConstGoto.diff b/tests/mir-opt/const_goto.issue_77355_opt.ConstGoto.diff
index a717d1bbd12f2..d5627a9b4f4e5 100644
--- a/tests/mir-opt/const_goto.issue_77355_opt.ConstGoto.diff
+++ b/tests/mir-opt/const_goto.issue_77355_opt.ConstGoto.diff
@@ -4,32 +4,32 @@
   fn issue_77355_opt(_1: Foo) -> u64 {
       debug num => _1;                     // in scope 0 at $DIR/const_goto.rs:+0:20: +0:23
       let mut _0: u64;                     // return place in scope 0 at $DIR/const_goto.rs:+0:33: +0:36
--     let mut _2: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-     let mut _2: bool;                    // in scope 0 at $DIR/const_goto.rs:+1:8: +1:37
 -     let mut _3: isize;                   // in scope 0 at $DIR/const_goto.rs:+1:22: +1:28
 +     let mut _2: isize;                   // in scope 0 at $DIR/const_goto.rs:+1:22: +1:28
   
       bb0: {
--         StorageLive(_2);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         StorageLive(_2);                 // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
 -         _3 = discriminant(_1);           // scope 0 at $DIR/const_goto.rs:+1:17: +1:20
--         switchInt(move _3) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         switchInt(move _3) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto.rs:+1:8: +1:20
 +         _2 = discriminant(_1);           // scope 0 at $DIR/const_goto.rs:+1:17: +1:20
-+         switchInt(move _2) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++         switchInt(move _2) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto.rs:+1:8: +1:20
       }
   
       bb1: {
--         _2 = const false;                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
--         goto -> bb3;                     // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         _2 = const false;                // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
+-         goto -> bb3;                     // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
 +         _0 = const 42_u64;               // scope 0 at $DIR/const_goto.rs:+1:53: +1:55
 +         goto -> bb3;                     // scope 0 at $DIR/const_goto.rs:+1:5: +1:57
       }
   
       bb2: {
--         _2 = const true;                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
--         goto -> bb3;                     // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         _2 = const true;                 // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
+-         goto -> bb3;                     // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
 -     }
 - 
 -     bb3: {
--         switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/const_goto.rs:+1:8: +1:37
 -     }
 - 
 -     bb4: {
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff
index e3757941c8cd1..0d174e4b69985 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff
@@ -22,17 +22,17 @@
   
       bb0: {
           StorageLive(_1);                 // scope 0 at $DIR/offset_of.rs:+1:9: +1:10
--         _1 = OffsetOf(Alpha, [0]);       // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
-+         _1 = const 4_usize;              // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+-         _1 = OffsetOf(Alpha, [0]);       // scope 0 at $DIR/offset_of.rs:+1:13: +1:32
++         _1 = const 4_usize;              // scope 0 at $DIR/offset_of.rs:+1:13: +1:32
           StorageLive(_2);                 // scope 1 at $DIR/offset_of.rs:+2:9: +2:10
--         _2 = OffsetOf(Alpha, [1]);       // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
-+         _2 = const 0_usize;              // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+-         _2 = OffsetOf(Alpha, [1]);       // scope 1 at $DIR/offset_of.rs:+2:13: +2:32
++         _2 = const 0_usize;              // scope 1 at $DIR/offset_of.rs:+2:13: +2:32
           StorageLive(_3);                 // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
--         _3 = OffsetOf(Alpha, [2, 0]);    // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
-+         _3 = const 2_usize;              // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+-         _3 = OffsetOf(Alpha, [2, 0]);    // scope 2 at $DIR/offset_of.rs:+3:14: +3:35
++         _3 = const 2_usize;              // scope 2 at $DIR/offset_of.rs:+3:14: +3:35
           StorageLive(_4);                 // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
--         _4 = OffsetOf(Alpha, [2, 1]);    // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
-+         _4 = const 3_usize;              // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+-         _4 = OffsetOf(Alpha, [2, 1]);    // scope 3 at $DIR/offset_of.rs:+4:14: +4:35
++         _4 = const 3_usize;              // scope 3 at $DIR/offset_of.rs:+4:14: +4:35
           _0 = const ();                   // scope 0 at $DIR/offset_of.rs:+0:15: +5:2
           StorageDead(_4);                 // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
           StorageDead(_3);                 // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff
index 4a655604cd183..28cc86e68fd2a 100644
--- a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff
+++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff
@@ -22,13 +22,13 @@
   
       bb0: {
           StorageLive(_1);                 // scope 0 at $DIR/offset_of.rs:+1:9: +1:11
-          _1 = OffsetOf(Gamma<T>, [0]);    // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+          _1 = OffsetOf(Gamma<T>, [0]);    // scope 0 at $DIR/offset_of.rs:+1:14: +1:36
           StorageLive(_2);                 // scope 1 at $DIR/offset_of.rs:+2:9: +2:11
-          _2 = OffsetOf(Gamma<T>, [1]);    // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+          _2 = OffsetOf(Gamma<T>, [1]);    // scope 1 at $DIR/offset_of.rs:+2:14: +2:36
           StorageLive(_3);                 // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
-          _3 = OffsetOf(Delta<T>, [1]);    // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+          _3 = OffsetOf(Delta<T>, [1]);    // scope 2 at $DIR/offset_of.rs:+3:14: +3:36
           StorageLive(_4);                 // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
-          _4 = OffsetOf(Delta<T>, [2]);    // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+          _4 = OffsetOf(Delta<T>, [2]);    // scope 3 at $DIR/offset_of.rs:+4:14: +4:36
           _0 = const ();                   // scope 0 at $DIR/offset_of.rs:+0:17: +5:2
           StorageDead(_4);                 // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
           StorageDead(_3);                 // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
diff --git a/tests/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff
index 0580f73341d21..e351b62591dba 100644
--- a/tests/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff
+++ b/tests/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff
@@ -4,33 +4,33 @@
   fn foo(_1: Option<()>) -> () {
       debug bar => _1;                     // in scope 0 at $DIR/matches_reduce_branches.rs:+0:8: +0:11
       let mut _0: ();                      // return place in scope 0 at $DIR/matches_reduce_branches.rs:+0:25: +0:25
-      let mut _2: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _2: bool;                    // in scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
       let mut _3: isize;                   // in scope 0 at $DIR/matches_reduce_branches.rs:+1:22: +1:26
-+     let mut _4: isize;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++     let mut _4: isize;                   // in scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:20
   
       bb0: {
-          StorageLive(_2);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_2);                 // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
           _3 = discriminant(_1);           // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
--         switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+         StorageLive(_4);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+         _4 = move _3;                    // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+         _2 = Eq(_4, const 0_isize);      // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+         StorageDead(_4);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+         switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:20
++         StorageLive(_4);                 // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:20
++         _4 = move _3;                    // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:20
++         _2 = Eq(_4, const 0_isize);      // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
++         StorageDead(_4);                 // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:20
++         switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
       }
   
       bb1: {
--         _2 = const false;                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
--         goto -> bb3;                     // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         _2 = const false;                // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
+-         goto -> bb3;                     // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
 -     }
 - 
 -     bb2: {
--         _2 = const true;                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
--         goto -> bb3;                     // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         _2 = const true;                 // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
+-         goto -> bb3;                     // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
 -     }
 - 
 -     bb3: {
--         switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+-         switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/matches_reduce_branches.rs:+1:8: +1:26
 -     }
 - 
 -     bb4: {
diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
index e88e24482ccc8..52a5ad6a28f1b 100644
--- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
+++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
@@ -86,6 +86,9 @@ fn main() {
     // index
     [ [1i32, 1][elem as usize] == 3 ] => "Assertion failed: [1i32, 1][elem as usize] == 3\nWith captures:\n  elem = 1\n"
 
+    // matches
+    [ matches!(Some(elem == 1i32), None) ] => "Assertion failed: builtin # matches(Some(elem == 1i32), None)\nWith captures:\n  elem = 1\n"
+
     // method call
     [ FOO.add(elem, elem) == 3 ] => "Assertion failed: FOO.add(elem, elem) == 3\nWith captures:\n  elem = 1\n"
 
diff --git a/tests/ui/stdlib-unit-tests/matches2021.rs b/tests/ui/stdlib-unit-tests/matches2021.rs
index 9143a8cdd59bc..1b64bfba2bf85 100644
--- a/tests/ui/stdlib-unit-tests/matches2021.rs
+++ b/tests/ui/stdlib-unit-tests/matches2021.rs
@@ -1,3 +1,5 @@
+#![allow(unreachable_patterns)]
+
 // run-pass
 // edition:2021
 

From f026104452321df55b2ebe47e2ef4bce915e73d1 Mon Sep 17 00:00:00 2001
From: Caio <c410.f3r@gmail.com>
Date: Thu, 18 May 2023 22:16:59 -0300
Subject: [PATCH 2/2] Typo

---
 compiler/rustc_parse/src/parser/expr.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index b0eaf0898cb1f..4b9037d36fa82 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3377,7 +3377,7 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<Expr>> {
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
             let res = f(this, attrs)?;
-            let trailing = if this.restrictions.contains(Restrictions::STMT_EXPRPAr)
+            let trailing = if this.restrictions.contains(Restrictions::STMT_EXPR)
                 && this.token.kind == token::Semi
             {
                 TrailingToken::Semi