diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 13b1a589d9ba9..6ffb3d65abcad 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1436,7 +1436,7 @@ pub enum ExprKind {
     /// `'label: loop { block }`
     Loop(P<Block>, Option<Label>, Span),
     /// A `match` block.
-    Match(P<Expr>, ThinVec<Arm>),
+    Match(P<Expr>, ThinVec<Arm>, MatchKind),
     /// A closure (e.g., `move |a, b, c| a + b + c`).
     Closure(Box<Closure>),
     /// A block (`'label: { ... }`).
@@ -1761,6 +1761,15 @@ pub enum StrStyle {
     Raw(u8),
 }
 
+/// The kind of match expression
+#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
+pub enum MatchKind {
+    /// match expr { ... }
+    Prefix,
+    /// expr.match { ... }
+    Postfix,
+}
+
 /// A literal in a meta item.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct MetaItemLit {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index bbbb34f7f2f48..f9b519b307921 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1423,7 +1423,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
             visit_opt(label, |label| vis.visit_label(label));
             vis.visit_span(span);
         }
-        ExprKind::Match(expr, arms) => {
+        ExprKind::Match(expr, arms, _kind) => {
             vis.visit_expr(expr);
             arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
         }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 9efb87e53cde9..64c2cbd297d0f 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -919,7 +919,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
             visit_opt!(visitor, visit_label, opt_label);
             try_visit!(visitor.visit_block(block));
         }
-        ExprKind::Match(subexpression, arms) => {
+        ExprKind::Match(subexpression, arms, _kind) => {
             try_visit!(visitor.visit_expr(subexpression));
             walk_list!(visitor, visit_arm, arms);
         }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 85b599902b959..0a196f2fafc28 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -181,10 +181,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     )
                 }),
                 ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
-                ExprKind::Match(expr, arms) => hir::ExprKind::Match(
+                ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match(
                     self.lower_expr(expr),
                     self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
-                    hir::MatchSource::Normal,
+                    match kind {
+                        MatchKind::Prefix => hir::MatchSource::Normal,
+                        MatchKind::Postfix => hir::MatchSource::Postfix,
+                    },
                 ),
                 ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
                 ExprKind::Closure(box Closure {
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index a28fcb0077948..296f237763eb2 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -565,6 +565,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(generic_const_items, "generic const items are experimental");
     gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
     gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
+    gate_all!(postfix_match, "postfix match is experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 433ef03b6e544..8bd8b6ac14475 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -1,6 +1,6 @@
 use crate::pp::Breaks::Inconsistent;
 use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
-use ast::ForLoopKind;
+use ast::{ForLoopKind, MatchKind};
 use itertools::{Itertools, Position};
 use rustc_ast::ptr::P;
 use rustc_ast::token;
@@ -589,12 +589,22 @@ impl<'a> State<'a> {
                 self.word_nbsp("loop");
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Match(expr, arms) => {
+            ast::ExprKind::Match(expr, arms, match_kind) => {
                 self.cbox(0);
                 self.ibox(0);
-                self.word_nbsp("match");
-                self.print_expr_as_cond(expr);
-                self.space();
+
+                match match_kind {
+                    MatchKind::Prefix => {
+                        self.word_nbsp("match");
+                        self.print_expr_as_cond(expr);
+                        self.space();
+                    }
+                    MatchKind::Postfix => {
+                        self.print_expr_as_cond(expr);
+                        self.word_nbsp(".match");
+                    }
+                }
+
                 self.bopen();
                 self.print_inner_attributes_no_trailing_hardbreak(attrs);
                 for arm in arms {
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 56c56b2704b00..92efeab08ebea 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -245,7 +245,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             ExprKind::Let(_, local_expr, _, _) => {
                 self.manage_cond_expr(local_expr);
             }
-            ExprKind::Match(local_expr, _) => {
+            ExprKind::Match(local_expr, ..) => {
                 self.manage_cond_expr(local_expr);
             }
             ExprKind::MethodCall(call) => {
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 6eccd67f8af19..60dbdf8b54460 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -132,7 +132,7 @@ fn cs_partial_cmp(
                 // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
 
                 if !tag_then_data
-                    && let ExprKind::Match(_, arms) = &mut expr1.kind
+                    && let ExprKind::Match(_, arms, _) = &mut expr1.kind
                     && let Some(last) = arms.last_mut()
                     && let PatKind::Wild = last.pat.kind
                 {
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index b11a1b6cda154..108c1078eaa5b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::path_std;
 
-use ast::EnumDef;
-use rustc_ast::{self as ast, MetaItem};
+use rustc_ast::{self as ast, EnumDef, MetaItem};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 989b7b485c992..159640163579b 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -1,6 +1,6 @@
 use crate::base::ExtCtxt;
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp};
+use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp};
 use rustc_ast::{attr, token, util::literal};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -524,7 +524,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: ThinVec<ast::Arm>) -> P<Expr> {
-        self.expr(span, ast::ExprKind::Match(arg, arms))
+        self.expr(span, ast::ExprKind::Match(arg, arms, MatchKind::Prefix))
     }
 
     pub fn expr_if(
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 17c4d81474e27..89f471d21bb07 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -555,6 +555,8 @@ declare_features! (
     (unstable, offset_of_nested, "1.77.0", Some(120140)),
     /// Allows using `#[optimize(X)]`.
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
+    /// Allows postfix match `expr.match { ... }`
+    (unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 3dac6880f17e1..9cec42c91c152 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2004,6 +2004,8 @@ pub enum LocalSource {
 pub enum MatchSource {
     /// A `match _ { .. }`.
     Normal,
+    /// A `expr.match { .. }`.
+    Postfix,
     /// A desugared `for _ in _ { .. }` loop.
     ForLoopDesugar,
     /// A desugared `?` operator.
@@ -2020,6 +2022,7 @@ impl MatchSource {
         use MatchSource::*;
         match self {
             Normal => "match",
+            Postfix => ".match",
             ForLoopDesugar => "for",
             TryDesugar(_) => "?",
             AwaitDesugar => ".await",
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 3481c66da702e..32b45f865e942 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -865,7 +865,7 @@ trait UnusedDelimLint {
                 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
             }
 
-            Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
+            Match(ref head, ..) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
                 let left = e.span.lo() + rustc_span::BytePos(5);
                 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
             }
@@ -1133,7 +1133,7 @@ impl EarlyLintPass for UnusedParens {
                 }
                 return;
             }
-            ExprKind::Match(ref _expr, ref arm) => {
+            ExprKind::Match(ref _expr, ref arm, _) => {
                 for a in arm {
                     if let Some(body) = &a.body {
                         self.check_unused_delims_expr(
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 2685bae4d09c8..080d45a1ae279 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -481,6 +481,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             // when the iterator is an uninhabited type. unreachable_code will trigger instead.
             hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
             hir::MatchSource::ForLoopDesugar
+            | hir::MatchSource::Postfix
             | hir::MatchSource::Normal
             | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
             // Unreachable patterns in try and await expressions occur when one of
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 6cc358db9fcae..e9c1bc7ad5666 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -11,7 +11,7 @@ use crate::errors;
 use crate::maybe_recover_from_interpolated_ty_qpath;
 use ast::mut_visit::{noop_visit_expr, MutVisitor};
 use ast::token::IdentIsRaw;
-use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment};
+use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment};
 use core::mem;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -1375,6 +1375,13 @@ impl<'a> Parser<'a> {
             return Ok(self.mk_await_expr(self_arg, lo));
         }
 
+        // Post-fix match
+        if self.eat_keyword(kw::Match) {
+            let match_span = self.prev_token.span;
+            self.psess.gated_spans.gate(sym::postfix_match, match_span);
+            return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);
+        }
+
         let fn_span_lo = self.token.span;
         let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
         self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]);
@@ -2889,8 +2896,20 @@ impl<'a> Parser<'a> {
     /// Parses a `match ... { ... }` expression (`match` token already eaten).
     fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
         let match_span = self.prev_token.span;
-        let lo = self.prev_token.span;
         let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+
+        self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
+    }
+
+    /// Parses the block of a `match expr { ... }` or a `expr.match { ... }`
+    /// expression. This is after the match token and scrutinee are eaten
+    fn parse_match_block(
+        &mut self,
+        lo: Span,
+        match_span: Span,
+        scrutinee: P<Expr>,
+        match_kind: MatchKind,
+    ) -> PResult<'a, P<Expr>> {
         if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
             if self.token == token::Semi {
                 e.span_suggestion_short(
@@ -2933,7 +2952,7 @@ impl<'a> Parser<'a> {
                     });
                     return Ok(self.mk_expr_with_attrs(
                         span,
-                        ExprKind::Match(scrutinee, arms),
+                        ExprKind::Match(scrutinee, arms, match_kind),
                         attrs,
                     ));
                 }
@@ -2941,7 +2960,7 @@ impl<'a> Parser<'a> {
         }
         let hi = self.token.span;
         self.bump();
-        Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
+        Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs))
     }
 
     /// Attempt to recover from match arm body with statements and no surrounding braces.
@@ -3950,7 +3969,7 @@ impl MutVisitor for CondChecker<'_> {
             | ExprKind::While(_, _, _)
             | ExprKind::ForLoop { .. }
             | ExprKind::Loop(_, _, _)
-            | ExprKind::Match(_, _)
+            | ExprKind::Match(_, _, _)
             | ExprKind::Closure(_)
             | ExprKind::Block(_, _)
             | ExprKind::Gen(_, _, _)
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 02792491f5ed0..de123a16859ea 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -48,7 +48,7 @@ impl NonConstExpr {
             Self::Match(TryDesugar(_)) => &[sym::const_try],
 
             // All other expressions are allowed.
-            Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[],
+            Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[],
         };
 
         Some(gates)
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3784a08b1b720..2474189fef6e1 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1319,6 +1319,7 @@ symbols! {
         poll,
         poll_next,
         post_dash_lto: "post-lto",
+        postfix_match,
         powerpc_target_feature,
         powf128,
         powf16,
diff --git a/src/doc/unstable-book/src/language-features/postfix-match.md b/src/doc/unstable-book/src/language-features/postfix-match.md
new file mode 100644
index 0000000000000..cd6b6a7442c5a
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/postfix-match.md
@@ -0,0 +1,22 @@
+# `postfix-match`
+
+`postfix-match` adds the feature for matching upon values postfix
+the expressions that generate the values.
+
+```rust,edition2021
+#![feature(postfix_match)]
+
+enum Foo {
+    Bar,
+    Baz
+}
+
+fn get_foo() -> Foo {
+    Foo::Bar
+}
+
+get_foo().match {
+    Foo::Bar => {},
+    Foo::Baz => panic!(),
+}
+```
diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs
index fb434fb7450a7..3bdf13dbbea65 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_else.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs
@@ -105,7 +105,7 @@ impl<'ast> Visitor<'ast> for BreakVisitor {
     fn visit_expr(&mut self, expr: &'ast Expr) {
         self.is_break = match expr.kind {
             ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
-            ExprKind::Match(_, ref arms) => arms.iter().all(|arm|
+            ExprKind::Match(_, ref arms, _) => arms.iter().all(|arm|
                 arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body))
             ),
             ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index 60e9d262e7e09..ab1b3043f0c38 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -552,7 +552,7 @@ fn ident_difference_expr_with_base_location(
         | (Gen(_, _, _), Gen(_, _, _))
         | (Block(_, _), Block(_, _))
         | (Closure(_), Closure(_))
-        | (Match(_, _), Match(_, _))
+        | (Match(_, _, _), Match(_, _, _))
         | (Loop(_, _, _), Loop(_, _, _))
         | (ForLoop { .. }, ForLoop { .. })
         | (While(_, _, _), While(_, _, _))
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 3b3939da7b6bc..eb916a37f6ea5 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -198,7 +198,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         },
         (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
         (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
-        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
+        (Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm),
         (
             Closure(box ast::Closure {
                 binder: lb,
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index c500b30b99826..053afcc52d4c0 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -3,7 +3,7 @@ use std::cmp::min;
 
 use itertools::Itertools;
 use rustc_ast::token::{Delimiter, Lit, LitKind};
-use rustc_ast::{ast, ptr, token, ForLoopKind};
+use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind};
 use rustc_span::{BytePos, Span};
 
 use crate::chains::rewrite_chain;
@@ -170,8 +170,8 @@ pub(crate) fn format_expr(
                 }
             }
         }
-        ast::ExprKind::Match(ref cond, ref arms) => {
-            rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs)
+        ast::ExprKind::Match(ref cond, ref arms, kind) => {
+            rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind)
         }
         ast::ExprKind::Path(ref qself, ref path) => {
             rewrite_path(context, PathContext::Expr, qself, path, shape)
@@ -625,7 +625,7 @@ pub(crate) fn rewrite_cond(
     shape: Shape,
 ) -> Option<String> {
     match expr.kind {
-        ast::ExprKind::Match(ref cond, _) => {
+        ast::ExprKind::Match(ref cond, _, MatchKind::Prefix) => {
             // `match `cond` {`
             let cond_shape = match context.config.indent_style() {
                 IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs
index 5a00984d4c00e..e68903c871576 100644
--- a/src/tools/rustfmt/src/matches.rs
+++ b/src/tools/rustfmt/src/matches.rs
@@ -2,7 +2,7 @@
 
 use std::iter::repeat;
 
-use rustc_ast::{ast, ptr};
+use rustc_ast::{ast, ptr, MatchKind};
 use rustc_span::{BytePos, Span};
 
 use crate::comment::{combine_strs_with_missing_comments, rewrite_comment};
@@ -72,6 +72,7 @@ pub(crate) fn rewrite_match(
     shape: Shape,
     span: Span,
     attrs: &[ast::Attribute],
+    match_kind: MatchKind,
 ) -> Option<String> {
     // Do not take the rhs overhead from the upper expressions into account
     // when rewriting match condition.
@@ -131,15 +132,27 @@ pub(crate) fn rewrite_match(
         }
     } else {
         let span_after_cond = mk_sp(cond.span.hi(), span.hi());
-        Some(format!(
-            "match {}{}{{\n{}{}{}\n{}}}",
-            cond_str,
-            block_sep,
-            inner_attrs_str,
-            nested_indent_str,
-            rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
-            shape.indent.to_string(context.config),
-        ))
+
+        match match_kind {
+            MatchKind::Prefix => Some(format!(
+                "match {}{}{{\n{}{}{}\n{}}}",
+                cond_str,
+                block_sep,
+                inner_attrs_str,
+                nested_indent_str,
+                rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
+                shape.indent.to_string(context.config),
+            )),
+            MatchKind::Postfix => Some(format!(
+                "{}.match{}{{\n{}{}{}\n{}}}",
+                cond_str,
+                block_sep,
+                inner_attrs_str,
+                nested_indent_str,
+                rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
+                shape.indent.to_string(context.config),
+            )),
+        }
     }
 }
 
diff --git a/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs b/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs
new file mode 100644
index 0000000000000..b2366723631ed
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs
@@ -0,0 +1,20 @@
+#![feature(postfix_match)]
+
+fn main() {
+    let val = Some(42);
+
+    val.match {
+        Some(_) => 2,
+        _ => 1
+    };
+
+    Some(2).match {
+        Some(_) => true,
+        None => false
+    }.match {
+        false => "ferris is cute",
+        true => "I turn cats in to petted cats",
+    }.match {
+        _ => (),
+    }
+}
\ No newline at end of file
diff --git a/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs b/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs
new file mode 100644
index 0000000000000..f439f2726235e
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs
@@ -0,0 +1,20 @@
+#![feature(postfix_match)]
+
+fn main() {
+    let val = Some(42);
+
+    val.match {
+        Some(_) => 2,
+        _ => 1,
+    };
+
+    Some(2).match {
+        Some(_) => true,
+        None => false,
+    }.match {
+        false => "ferris is cute",
+        true => "I turn cats in to petted cats",
+    }.match {
+        _ => (),
+    }
+}
diff --git a/tests/pretty/postfix-match.rs b/tests/pretty/postfix-match.rs
new file mode 100644
index 0000000000000..5bb54e15275ba
--- /dev/null
+++ b/tests/pretty/postfix-match.rs
@@ -0,0 +1,21 @@
+#![feature(postfix_match)]
+
+fn main() {
+    let val = Some(42);
+
+    val.match {
+        Some(_) => 2,
+        _ => 1
+    };
+
+
+    Some(2).match {
+        Some(_) => true,
+        None => false
+    }.match {
+        false => "ferris is cute",
+        true => "I turn cats in to petted cats",
+    }.match {
+        _ => (),
+    }
+}
diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.rs b/tests/ui/feature-gates/feature-gate-postfix_match.rs
new file mode 100644
index 0000000000000..dce7e81a9ae44
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-postfix_match.rs
@@ -0,0 +1,17 @@
+// Testing that postfix match doesn't work without enabling the feature
+
+fn main() {
+    let val = Some(42);
+
+    val.match { //~ ERROR postfix match is experimental
+        Some(42) => "the answer to life, the universe, and everything",
+        _ => "might be the answer to something"
+    };
+
+    // Test that the gate works behind a cfg
+    #[cfg(FALSE)]
+    val.match { //~ ERROR postfix match is experimental
+        Some(42) => "the answer to life, the universe, and everything",
+        _ => "might be the answer to something"
+    };
+}
diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.stderr b/tests/ui/feature-gates/feature-gate-postfix_match.stderr
new file mode 100644
index 0000000000000..136838788dd29
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-postfix_match.stderr
@@ -0,0 +1,23 @@
+error[E0658]: postfix match is experimental
+  --> $DIR/feature-gate-postfix_match.rs:6:9
+   |
+LL |     val.match {
+   |         ^^^^^
+   |
+   = note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information
+   = help: add `#![feature(postfix_match)]` 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]: postfix match is experimental
+  --> $DIR/feature-gate-postfix_match.rs:13:9
+   |
+LL |     val.match {
+   |         ^^^^^
+   |
+   = note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information
+   = help: add `#![feature(postfix_match)]` 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: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/match/postfix-match/pf-match-chain.rs b/tests/ui/match/postfix-match/pf-match-chain.rs
new file mode 100644
index 0000000000000..80546e1963bb6
--- /dev/null
+++ b/tests/ui/match/postfix-match/pf-match-chain.rs
@@ -0,0 +1,16 @@
+//@ run-pass
+
+#![feature(postfix_match)]
+
+fn main() {
+    1.match {
+        2 => Some(0),
+        _ => None,
+    }.match {
+        None => Ok(true),
+        Some(_) => Err("nope")
+    }.match {
+        Ok(_) => (),
+        Err(_) => panic!()
+    }
+}
diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs
new file mode 100644
index 0000000000000..f4cac46f7cd64
--- /dev/null
+++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs
@@ -0,0 +1,7 @@
+#![feature(postfix_match)]
+
+fn main() {
+    Some(1).match { //~ non-exhaustive patterns
+        None => {},
+    }
+}
diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr
new file mode 100644
index 0000000000000..f458218bb5d67
--- /dev/null
+++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/pf-match-exhaustiveness.rs:4:5
+   |
+LL |     Some(1).match {
+   |     ^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<i32>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<i32>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL ~         Some(_) => todo!(),
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/match/postfix-match/pf-match-types.rs b/tests/ui/match/postfix-match/pf-match-types.rs
new file mode 100644
index 0000000000000..af205926fb610
--- /dev/null
+++ b/tests/ui/match/postfix-match/pf-match-types.rs
@@ -0,0 +1,15 @@
+#![feature(postfix_match)]
+
+fn main() {
+    Some(10).match {
+    //~^ NOTE `match` arms have incompatible types
+        Some(5) => false,
+        //~^ NOTE this is found to be of type `bool`
+        Some(2) => true,
+        //~^ NOTE this is found to be of type `bool`
+        None    => (),
+        //~^ ERROR `match` arms have incompatible types
+        //~| NOTE expected `bool`, found `()`
+        _       => true
+    }
+}
diff --git a/tests/ui/match/postfix-match/pf-match-types.stderr b/tests/ui/match/postfix-match/pf-match-types.stderr
new file mode 100644
index 0000000000000..0cfc1363d5f1e
--- /dev/null
+++ b/tests/ui/match/postfix-match/pf-match-types.stderr
@@ -0,0 +1,21 @@
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/pf-match-types.rs:10:20
+   |
+LL | /     Some(10).match {
+LL | |
+LL | |         Some(5) => false,
+   | |                    ----- this is found to be of type `bool`
+LL | |
+LL | |         Some(2) => true,
+   | |                    ---- this is found to be of type `bool`
+LL | |
+LL | |         None    => (),
+   | |                    ^^ expected `bool`, found `()`
+...  |
+LL | |         _       => true
+LL | |     }
+   | |_____- `match` arms have incompatible types
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/postfix-match/postfix-match.rs b/tests/ui/match/postfix-match/postfix-match.rs
new file mode 100644
index 0000000000000..03c4e8ab5451f
--- /dev/null
+++ b/tests/ui/match/postfix-match/postfix-match.rs
@@ -0,0 +1,62 @@
+//@ run-pass
+
+#![feature(postfix_match)]
+
+struct Bar {
+    foo: u8,
+    baz: u8,
+}
+
+pub fn main() {
+    let thing = Some("thing");
+
+    thing.match {
+        Some("nothing") => {},
+        Some(text) if text.eq_ignore_ascii_case("tapir")  => {},
+        Some("true") | Some("false") => {},
+        Some("thing") => {},
+        Some(_) => {},
+        None => {}
+    };
+
+    let num = 2u8;
+
+    num.match {
+        0 => {},
+        1..=5 => {},
+        _ => {},
+    };
+
+    let slic = &[1, 2, 3, 4][..];
+
+    slic.match {
+        [1] => {},
+        [2, _tail @ ..] => {},
+        [1, _] => {},
+        _ => {},
+    };
+
+    slic[0].match {
+        1 => 0,
+        i => i,
+    };
+
+    let out = (1, 2).match {
+        (1, 3) => 0,
+        (_, 1) => 0,
+        (1, i) => i,
+        _ => 3,
+    };
+    assert!(out == 2);
+
+    let strct = Bar {
+        foo: 3,
+        baz: 4
+    };
+
+    strct.match {
+        Bar { foo: 1, .. } => {},
+        Bar { baz: 2, .. } => {},
+        _ => (),
+    };
+}