about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLieselotte <52315535+she3py@users.noreply.github.com>2024-09-18 20:38:43 +0200
committerLieselotte <52315535+she3py@users.noreply.github.com>2024-09-18 20:38:43 +0200
commitdb09345ef6e7f39110704e6196bb239d8f10fc27 (patch)
treea0374ff643b776d21bb32c07aa4cb88631c4d2b5
parentc2047219b59ac7830c4315653858f28cf0d57b9d (diff)
downloadrust-db09345ef6e7f39110704e6196bb239d8f10fc27.tar.gz
rust-db09345ef6e7f39110704e6196bb239d8f10fc27.zip
Add suggestions for expressions in patterns
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_parse/messages.ftl8
-rw-r--r--compiler/rustc_parse/src/errors.rs77
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs227
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs10
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions1.stderr11
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions2.stderr23
-rw-r--r--tests/ui/parser/issues/issue-24375.stderr15
-rw-r--r--tests/ui/parser/pat-lt-bracket-6.stderr4
-rw-r--r--tests/ui/parser/recover/recover-pat-exprs.rs10
-rw-r--r--tests/ui/parser/recover/recover-pat-exprs.stderr569
-rw-r--r--tests/ui/parser/recover/recover-pat-issues.stderr75
-rw-r--r--tests/ui/parser/recover/recover-pat-lets.stderr20
-rw-r--r--tests/ui/parser/recover/recover-pat-ranges.stderr120
-rw-r--r--tests/ui/parser/recover/recover-pat-wildcards.stderr23
15 files changed, 1122 insertions, 72 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 094e3010471..59866012069 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -571,6 +571,8 @@ pub enum StashKey {
     /// Query cycle detected, stashing in favor of a better error.
     Cycle,
     UndeterminedMacroResolution,
+    /// Used by `Parser::maybe_recover_trailing_expr`
+    ExprInPat,
 }
 
 fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 3d0ce77bc47..f5aa8984f51 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -807,6 +807,14 @@ parse_unexpected_expr_in_pat =
 
     .label = arbitrary expressions are not allowed in patterns
 
+parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const`
+
+parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard
+
+parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`)
+
+parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard
+
 parse_unexpected_if_with_if = unexpected `if` in the condition expression
     .suggestion = remove the `if`
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index a91aa1f5018..e3e7fcebaaa 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength
+
 use std::borrow::Cow;
 
 use rustc_ast::token::Token;
@@ -2592,11 +2594,86 @@ pub(crate) struct ExpectedCommaAfterPatternField {
 #[derive(Diagnostic)]
 #[diag(parse_unexpected_expr_in_pat)]
 pub(crate) struct UnexpectedExpressionInPattern {
+    /// The unexpected expr's span.
     #[primary_span]
     #[label]
     pub span: Span,
     /// Was a `RangePatternBound` expected?
     pub is_bound: bool,
+    /// The unexpected expr's precedence (used in match arm guard suggestions).
+    pub expr_precedence: i8,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum UnexpectedExpressionInPatternSugg {
+    #[multipart_suggestion(
+        parse_unexpected_expr_in_pat_create_guard_sugg,
+        applicability = "maybe-incorrect"
+    )]
+    CreateGuard {
+        /// Where to put the suggested identifier.
+        #[suggestion_part(code = "{ident}")]
+        ident_span: Span,
+        /// Where to put the match arm.
+        #[suggestion_part(code = " if {ident} == {expr}")]
+        pat_hi: Span,
+        /// The suggested identifier.
+        ident: String,
+        /// The unexpected expression.
+        expr: String,
+    },
+
+    #[multipart_suggestion(
+        parse_unexpected_expr_in_pat_update_guard_sugg,
+        applicability = "maybe-incorrect"
+    )]
+    UpdateGuard {
+        /// Where to put the suggested identifier.
+        #[suggestion_part(code = "{ident}")]
+        ident_span: Span,
+        /// The beginning of the match arm guard's expression (insert a `(` if `Some`).
+        #[suggestion_part(code = "(")]
+        guard_lo: Option<Span>,
+        /// The end of the match arm guard's expression.
+        #[suggestion_part(code = "{guard_hi_paren} && {ident} == {expr}")]
+        guard_hi: Span,
+        /// Either `")"` or `""`.
+        guard_hi_paren: &'static str,
+        /// The suggested identifier.
+        ident: String,
+        /// The unexpected expression.
+        expr: String,
+    },
+
+    #[multipart_suggestion(
+        parse_unexpected_expr_in_pat_const_sugg,
+        applicability = "has-placeholders"
+    )]
+    Const {
+        /// Where to put the extracted constant declaration.
+        #[suggestion_part(code = "{indentation}const {ident}: /* Type */ = {expr};\n")]
+        stmt_lo: Span,
+        /// Where to put the suggested identifier.
+        #[suggestion_part(code = "{ident}")]
+        ident_span: Span,
+        /// The suggested identifier.
+        ident: String,
+        /// The unexpected expression.
+        expr: String,
+        /// The statement's block's indentation.
+        indentation: String,
+    },
+
+    #[multipart_suggestion(
+        parse_unexpected_expr_in_pat_inline_const_sugg,
+        applicability = "maybe-incorrect"
+    )]
+    InlineConst {
+        #[suggestion_part(code = "const {{ ")]
+        start_span: Span,
+        #[suggestion_part(code = " }}")]
+        end_span: Span,
+    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 94c01f7e0d4..647df25c82e 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1,12 +1,14 @@
-use rustc_ast::mut_visit::{walk_pat, MutVisitor};
+use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
+use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{
-    self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField,
-    PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
+    self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, ExprPrecedence,
+    LocalKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd,
+    RangeSyntax, Stmt, StmtKind,
 };
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, Diag, PResult};
+use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
@@ -21,8 +23,8 @@ use crate::errors::{
     InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
     ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern,
     SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
-    TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern,
-    UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
+    TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg,
+    UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
     UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens,
 };
 use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat};
@@ -448,12 +450,219 @@ impl<'a> Parser<'a> {
             || self.token == token::CloseDelim(Delimiter::Parenthesis)
                 && self.look_ahead(1, Token::is_range_separator);
 
+        let span = expr.span;
+
         Some((
-            self.dcx().emit_err(UnexpectedExpressionInPattern { span: expr.span, is_bound }),
-            expr.span,
+            self.dcx()
+                .create_err(UnexpectedExpressionInPattern {
+                    span,
+                    is_bound,
+                    expr_precedence: expr.precedence().order(),
+                })
+                .stash(span, StashKey::ExprInPat)
+                .unwrap(),
+            span,
         ))
     }
 
+    /// Called by [`Parser::parse_stmt_without_recovery`], used to add statement-aware subdiagnostics to the errors stashed
+    /// by [`Parser::maybe_recover_trailing_expr`].
+    pub(super) fn maybe_augment_stashed_expr_in_pats_with_suggestions(&mut self, stmt: &Stmt) {
+        if self.dcx().has_errors().is_none() {
+            // No need to walk the statement if there's no stashed errors.
+            return;
+        }
+
+        struct PatVisitor<'a> {
+            /// `self`
+            parser: &'a Parser<'a>,
+            /// The freshly-parsed statement.
+            stmt: &'a Stmt,
+            /// The current match arm (for arm guard suggestions).
+            arm: Option<&'a Arm>,
+            /// The current struct field (for variable name suggestions).
+            field: Option<&'a PatField>,
+        }
+
+        impl<'a> PatVisitor<'a> {
+            /// Looks for stashed [`StashKey::ExprInPat`] errors in `stash_span`, and emit them with suggestions.
+            /// `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns;
+            /// ```txt
+            /// &mut x.y
+            /// -----^^^ `stash_span`
+            /// |
+            /// `expr_span`
+            /// ```
+            /// `is_range_bound` is used to exclude arm guard suggestions in range pattern bounds.
+            fn maybe_add_suggestions_then_emit(
+                &self,
+                stash_span: Span,
+                expr_span: Span,
+                is_range_bound: bool,
+            ) {
+                self.parser.dcx().try_steal_modify_and_emit_err(
+                    stash_span,
+                    StashKey::ExprInPat,
+                    |err| {
+                        // Includes pre-pats (e.g. `&mut <err>`) in the diagnostic.
+                        err.span.replace(stash_span, expr_span);
+
+                        let sm = self.parser.psess.source_map();
+                        let stmt = self.stmt;
+                        let line_lo = sm.span_extend_to_line(stmt.span).shrink_to_lo();
+                        let indentation = sm.indentation_before(stmt.span).unwrap_or_default();
+                        let Ok(expr) = self.parser.span_to_snippet(expr_span) else {
+                            // FIXME: some suggestions don't actually need the snippet; see PR #123877's unresolved conversations.
+                            return;
+                        };
+
+                        if let StmtKind::Let(local) = &stmt.kind {
+                            match &local.kind {
+                                LocalKind::Decl | LocalKind::Init(_) => {
+                                    // It's kinda hard to guess what the user intended, so don't make suggestions.
+                                    return;
+                                }
+
+                                LocalKind::InitElse(_, _) => {}
+                            }
+                        }
+
+                        // help: use an arm guard `if val == expr`
+                        // FIXME(guard_patterns): suggest this regardless of a match arm.
+                        if let Some(arm) = &self.arm
+                            && !is_range_bound
+                        {
+                            let (ident, ident_span) = match self.field {
+                                Some(field) => {
+                                    (field.ident.to_string(), field.ident.span.to(expr_span))
+                                }
+                                None => ("val".to_owned(), expr_span),
+                            };
+
+                            // Are parentheses required around `expr`?
+                            // HACK: a neater way would be preferable.
+                            let expr = match &err.args["expr_precedence"] {
+                                DiagArgValue::Number(expr_precedence) => {
+                                    if *expr_precedence
+                                        <= ExprPrecedence::Binary(BinOpKind::Eq).order() as i32
+                                    {
+                                        format!("({expr})")
+                                    } else {
+                                        format!("{expr}")
+                                    }
+                                }
+                                _ => unreachable!(),
+                            };
+
+                            match &arm.guard {
+                                None => {
+                                    err.subdiagnostic(
+                                        UnexpectedExpressionInPatternSugg::CreateGuard {
+                                            ident_span,
+                                            pat_hi: arm.pat.span.shrink_to_hi(),
+                                            ident,
+                                            expr,
+                                        },
+                                    );
+                                }
+                                Some(guard) => {
+                                    // Are parentheses required around the old guard?
+                                    let wrap_guard = guard.precedence().order()
+                                        <= ExprPrecedence::Binary(BinOpKind::And).order();
+
+                                    err.subdiagnostic(
+                                        UnexpectedExpressionInPatternSugg::UpdateGuard {
+                                            ident_span,
+                                            guard_lo: if wrap_guard {
+                                                Some(guard.span.shrink_to_lo())
+                                            } else {
+                                                None
+                                            },
+                                            guard_hi: guard.span.shrink_to_hi(),
+                                            guard_hi_paren: if wrap_guard { ")" } else { "" },
+                                            ident,
+                                            expr,
+                                        },
+                                    );
+                                }
+                            }
+                        }
+
+                        // help: extract the expr into a `const VAL: _ = expr`
+                        let ident = match self.field {
+                            Some(field) => field.ident.as_str().to_uppercase(),
+                            None => "VAL".to_owned(),
+                        };
+                        err.subdiagnostic(UnexpectedExpressionInPatternSugg::Const {
+                            stmt_lo: line_lo,
+                            ident_span: expr_span,
+                            expr,
+                            ident,
+                            indentation,
+                        });
+
+                        // help: wrap the expr in a `const { expr }`
+                        // FIXME(inline_const_pat): once stabilized, remove this check and remove the `(requires #[feature(inline_const_pat)])` note from the message
+                        if self.parser.psess.unstable_features.is_nightly_build() {
+                            err.subdiagnostic(UnexpectedExpressionInPatternSugg::InlineConst {
+                                start_span: expr_span.shrink_to_lo(),
+                                end_span: expr_span.shrink_to_hi(),
+                            });
+                        }
+                    },
+                );
+            }
+        }
+
+        impl<'a> Visitor<'a> for PatVisitor<'a> {
+            fn visit_arm(&mut self, a: &'a Arm) -> Self::Result {
+                self.arm = Some(a);
+                visit::walk_arm(self, a);
+                self.arm = None;
+            }
+
+            fn visit_pat_field(&mut self, fp: &'a PatField) -> Self::Result {
+                self.field = Some(fp);
+                visit::walk_pat_field(self, fp);
+                self.field = None;
+            }
+
+            fn visit_pat(&mut self, p: &'a Pat) -> Self::Result {
+                match &p.kind {
+                    // Base expression
+                    PatKind::Err(_) | PatKind::Lit(_) => {
+                        self.maybe_add_suggestions_then_emit(p.span, p.span, false)
+                    }
+
+                    // Sub-patterns
+                    // FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`)
+                    PatKind::Box(subpat) | PatKind::Ref(subpat, _)
+                        if matches!(subpat.kind, PatKind::Err(_) | PatKind::Lit(_)) =>
+                    {
+                        self.maybe_add_suggestions_then_emit(subpat.span, p.span, false)
+                    }
+
+                    // Sub-expressions
+                    PatKind::Range(start, end, _) => {
+                        if let Some(start) = start {
+                            self.maybe_add_suggestions_then_emit(start.span, start.span, true);
+                        }
+
+                        if let Some(end) = end {
+                            self.maybe_add_suggestions_then_emit(end.span, end.span, true);
+                        }
+                    }
+
+                    // Walk continuation
+                    _ => visit::walk_pat(self, p),
+                }
+            }
+        }
+
+        // Starts the visit.
+        PatVisitor { parser: self, stmt, arm: None, field: None }.visit_stmt(stmt);
+    }
+
     /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
     /// allowed).
     fn parse_pat_with_range_pat(
@@ -845,7 +1054,7 @@ impl<'a> Parser<'a> {
                     self.0 = true;
                     *m = Mutability::Mut;
                 }
-                walk_pat(self, pat);
+                mut_visit::walk_pat(self, pat);
             }
         }
 
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 26ad39e06cd..92fba89d28a 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -29,6 +29,9 @@ use crate::{errors, maybe_whole};
 impl<'a> Parser<'a> {
     /// Parses a statement. This stops just before trailing semicolons on everything but items.
     /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
+    ///
+    /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
+    /// whether or not we have attributes.
     // Public for rustfmt usage.
     pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
         Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
@@ -66,7 +69,7 @@ impl<'a> Parser<'a> {
             });
         }
 
-        Ok(Some(if self.token.is_keyword(kw::Let) {
+        let stmt = if self.token.is_keyword(kw::Let) {
             self.collect_tokens(None, attrs, force_collect, |this, attrs| {
                 this.expect_keyword(kw::Let)?;
                 let local = this.parse_local(attrs)?;
@@ -163,7 +166,10 @@ impl<'a> Parser<'a> {
         } else {
             self.error_outer_attrs(attrs);
             return Ok(None);
-        }))
+        };
+
+        self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
+        Ok(Some(stmt))
     }
 
     fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
index c14021e009b..9831348de75 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
@@ -3,6 +3,17 @@ error: expected a pattern range bound, found an expression
    |
 LL |             0..5+1 => errors_only.push(x),
    |                ^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +         const VAL: /* Type */ = 5+1;
+LL ~         match x as i32 {
+LL ~             0..VAL => errors_only.push(x),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |             0..const { 5+1 } => errors_only.push(x),
+   |                +++++++     +
 
 error[E0408]: variable `n` is not bound in all patterns
   --> $DIR/range_pat_interactions1.rs:10:25
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
index 136296fa5b0..1b5e875cccb 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
@@ -1,9 +1,3 @@
-error: expected a pattern range bound, found an expression
-  --> $DIR/range_pat_interactions2.rs:10:18
-   |
-LL |             0..=(5+1) => errors_only.push(x),
-   |                  ^^^ arbitrary expressions are not allowed in patterns
-
 error: range pattern bounds cannot have parentheses
   --> $DIR/range_pat_interactions2.rs:10:17
    |
@@ -16,6 +10,23 @@ LL -             0..=(5+1) => errors_only.push(x),
 LL +             0..=5+1 => errors_only.push(x),
    |
 
+error: expected a pattern range bound, found an expression
+  --> $DIR/range_pat_interactions2.rs:10:18
+   |
+LL |             0..=(5+1) => errors_only.push(x),
+   |                  ^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +         const VAL: /* Type */ = 5+1;
+LL ~         match x as i32 {
+LL ~             0..=(VAL) => errors_only.push(x),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |             0..=(const { 5+1 }) => errors_only.push(x),
+   |                  +++++++     +
+
 error[E0658]: inline-const in pattern position is experimental
   --> $DIR/range_pat_interactions2.rs:15:20
    |
diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr
index e6ef07d13fd..a25c277d78a 100644
--- a/tests/ui/parser/issues/issue-24375.stderr
+++ b/tests/ui/parser/issues/issue-24375.stderr
@@ -3,6 +3,21 @@ error: expected a pattern, found an expression
    |
 LL |         tmp[0] => {}
    |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == tmp[0] => {}
+   |         ~~~ ++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = tmp[0];
+LL ~     match z {
+LL ~         VAL => {}
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { tmp[0] } => {}
+   |         +++++++        +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr
index 10c638a63e4..892883c4aed 100644
--- a/tests/ui/parser/pat-lt-bracket-6.stderr
+++ b/tests/ui/parser/pat-lt-bracket-6.stderr
@@ -1,8 +1,8 @@
 error: expected a pattern, found an expression
-  --> $DIR/pat-lt-bracket-6.rs:5:15
+  --> $DIR/pat-lt-bracket-6.rs:5:14
    |
 LL |     let Test(&desc[..]) = x;
-   |               ^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |              ^^^^^^^^^ arbitrary expressions are not allowed in patterns
 
 error[E0308]: mismatched types
   --> $DIR/pat-lt-bracket-6.rs:10:30
diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs
index 031e49cb8eb..e5e25df0c01 100644
--- a/tests/ui/parser/recover/recover-pat-exprs.rs
+++ b/tests/ui/parser/recover/recover-pat-exprs.rs
@@ -53,13 +53,21 @@ fn type_cast() {
     }
 }
 
-// ArithmeticOrLogicalExpression
+// ArithmeticOrLogicalExpression, also check if parentheses are added as needed
 fn operator() {
     match 0 {
         1 + 1 => (), //~ error: expected a pattern, found an expression
         (1 + 2) * 3 => (),
         //~^ error: expected a pattern, found an expression
         //~| error: expected a pattern, found an expression
+        x.0 > 2 => (), //~ error: expected a pattern, found an expression
+        x.0 == 2 => (), //~ error: expected a pattern, found an expression
+    }
+
+    // preexisting match arm guard
+    match (0, 0) {
+        (x, y.0 > 2) if x != 0 => (), //~ error: expected a pattern, found an expression
+        (x, y.0 > 2) if x != 0 || x != 1 => (), //~ error: expected a pattern, found an expression
     }
 }
 
diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr
index ea8d1458d27..63956f35c07 100644
--- a/tests/ui/parser/recover/recover-pat-exprs.stderr
+++ b/tests/ui/parser/recover/recover-pat-exprs.stderr
@@ -3,30 +3,117 @@ error: expected a pattern, found an expression
    |
 LL |         x.y => (),
    |         ^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.y => (),
+   |         ~~~ +++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.y;
+LL ~     match 0 {
+LL |         x => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.y } => (),
+   |         +++++++     +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:6:9
    |
 LL |         x.0 => (),
    |         ^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.0 => (),
+   |         ~~~ +++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.0;
+LL ~     match 0 {
+LL |         x => (),
+LL |         x.y => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.0 } => (),
+   |         +++++++     +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:7:9
    |
 LL |         x._0 => (),
    |         ^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x._0 => (),
+   |         ~~~ ++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x._0;
+LL ~     match 0 {
+LL |         x => (),
+LL |         x.y => (),
+LL |         x.0 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x._0 } => (),
+   |         +++++++      +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:8:9
    |
 LL |         x.0.1 => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.0.1 => (),
+   |         ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.0.1;
+LL ~     match 0 {
+LL |         x => (),
+...
+LL |         x._0 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.0.1 } => (),
+   |         +++++++       +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:9:9
    |
 LL |         x.4.y.17.__z => (),
    |         ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.4.y.17.__z => (),
+   |         ~~~ ++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.4.y.17.__z;
+LL ~     match 0 {
+LL |         x => (),
+...
+LL |         x.0.1 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.4.y.17.__z } => (),
+   |         +++++++              +
 
 error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.`
   --> $DIR/recover-pat-exprs.rs:12:12
@@ -63,12 +150,43 @@ error: expected a pattern, found an expression
    |
 LL |         x[0] => (),
    |         ^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x[0] => (),
+   |         ~~~ ++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x[0];
+LL ~     match 0 {
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x[0] } => (),
+   |         +++++++      +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:24:9
    |
 LL |         x[..] => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x[..] => (),
+   |         ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x[..];
+LL ~     match 0 {
+LL |         x[0] => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x[..] } => (),
+   |         +++++++       +
 
 error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
   --> $DIR/recover-pat-exprs.rs:27:12
@@ -102,99 +220,365 @@ error: expected a pattern, found an expression
    |
 LL |         x.f() => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.f() => (),
+   |         ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.f();
+LL ~     match 0 {
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.f() } => (),
+   |         +++++++       +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:38:9
    |
 LL |         x._f() => (),
    |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x._f() => (),
+   |         ~~~ ++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x._f();
+LL ~     match 0 {
+LL |         x.f() => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x._f() } => (),
+   |         +++++++        +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:39:9
    |
 LL |         x? => (),
    |         ^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x? => (),
+   |         ~~~ ++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x?;
+LL ~     match 0 {
+LL |         x.f() => (),
+LL |         x._f() => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x? } => (),
+   |         +++++++    +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:40:9
    |
 LL |         ().f() => (),
    |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == ().f() => (),
+   |         ~~~ ++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = ().f();
+LL ~     match 0 {
+LL |         x.f() => (),
+LL |         x._f() => (),
+LL |         x? => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { ().f() } => (),
+   |         +++++++        +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:41:9
    |
 LL |         (0, x)?.f() => (),
    |         ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == (0, x)?.f() => (),
+   |         ~~~ +++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = (0, x)?.f();
+LL ~     match 0 {
+LL |         x.f() => (),
+...
+LL |         ().f() => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { (0, x)?.f() } => (),
+   |         +++++++             +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:42:9
    |
 LL |         x.f().g() => (),
    |         ^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.f().g() => (),
+   |         ~~~ +++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.f().g();
+LL ~     match 0 {
+LL |         x.f() => (),
+...
+LL |         (0, x)?.f() => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.f().g() } => (),
+   |         +++++++           +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:43:9
    |
 LL |         0.f()?.g()?? => (),
    |         ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == 0.f()?.g()?? => (),
+   |         ~~~ ++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 0.f()?.g()??;
+LL ~     match 0 {
+LL |         x.f() => (),
+...
+LL |         x.f().g() => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { 0.f()?.g()?? } => (),
+   |         +++++++              +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:50:9
    |
 LL |         x as usize => (),
    |         ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x as usize => (),
+   |         ~~~ ++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x as usize;
+LL ~     match 0 {
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x as usize } => (),
+   |         +++++++            +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:51:9
    |
 LL |         0 as usize => (),
    |         ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == 0 as usize => (),
+   |         ~~~ ++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 0 as usize;
+LL ~     match 0 {
+LL |         x as usize => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { 0 as usize } => (),
+   |         +++++++            +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:52:9
    |
 LL |         x.f().0.4 as f32 => (),
    |         ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == x.f().0.4 as f32 => (),
+   |         ~~~ ++++++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.f().0.4 as f32;
+LL ~     match 0 {
+LL |         x as usize => (),
+LL |         0 as usize => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.f().0.4 as f32 } => (),
+   |         +++++++                  +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:59:9
    |
 LL |         1 + 1 => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
-
-error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:60:10
    |
-LL |         (1 + 2) * 3 => (),
-   |          ^^^^^ arbitrary expressions are not allowed in patterns
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == 1 + 1 => (),
+   |         ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 + 1;
+LL ~     match 0 {
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { 1 + 1 } => (),
+   |         +++++++       +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:60:9
    |
 LL |         (1 + 2) * 3 => (),
    |         ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == (1 + 2) * 3 => (),
+   |         ~~~ +++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = (1 + 2) * 3;
+LL ~     match 0 {
+LL |         1 + 1 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { (1 + 2) * 3 } => (),
+   |         +++++++             +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:67:5
+  --> $DIR/recover-pat-exprs.rs:63:9
    |
-LL |     1 + 2 * PI.cos() => 2,
-   |     ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+LL |         x.0 > 2 => (),
+   |         ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == (x.0 > 2) => (),
+   |         ~~~ +++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.0 > 2;
+LL ~     match 0 {
+LL |         1 + 1 => (),
+...
+LL |
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.0 > 2 } => (),
+   |         +++++++         +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:73:9
+  --> $DIR/recover-pat-exprs.rs:64:9
    |
-LL |         u8::MAX.abs() => (),
-   |         ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+LL |         x.0 == 2 => (),
+   |         ^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == (x.0 == 2) => (),
+   |         ~~~ ++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = x.0 == 2;
+LL ~     match 0 {
+LL |         1 + 1 => (),
+...
+LL |         x.0 > 2 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { x.0 == 2 } => (),
+   |         +++++++          +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:75:9
+  --> $DIR/recover-pat-exprs.rs:69:13
    |
-LL |         x.sqrt() @ .. => (),
-   |         ^^^^^^^^ arbitrary expressions are not allowed in patterns
+LL |         (x, y.0 > 2) if x != 0 => (),
+   |             ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to the match arm guard
+   |
+LL |         (x, val) if x != 0 && val == (y.0 > 2) => (),
+   |             ~~~            +++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = y.0 > 2;
+LL ~     match (0, 0) {
+LL ~         (x, VAL) if x != 0 => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (x, const { y.0 > 2 }) if x != 0 => (),
+   |             +++++++         +
+
+error: expected a pattern, found an expression
+  --> $DIR/recover-pat-exprs.rs:70:13
+   |
+LL |         (x, y.0 > 2) if x != 0 || x != 1 => (),
+   |             ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to the match arm guard
+   |
+LL |         (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (),
+   |             ~~~     +                +++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = y.0 > 2;
+LL ~     match (0, 0) {
+LL |         (x, y.0 > 2) if x != 0 => (),
+LL ~         (x, VAL) if x != 0 || x != 1 => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (x, const { y.0 > 2 }) if x != 0 || x != 1 => (),
+   |             +++++++         +
 
 error: left-hand side of `@` must be a binding
-  --> $DIR/recover-pat-exprs.rs:75:9
+  --> $DIR/recover-pat-exprs.rs:83:9
    |
 LL |         x.sqrt() @ .. => (),
    |         --------^^^--
@@ -204,50 +588,161 @@ LL |         x.sqrt() @ .. => (),
    |
    = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
 
+error: expected one of `)`, `,`, or `|`, found `+`
+  --> $DIR/recover-pat-exprs.rs:97:12
+   |
+LL |         (_ + 1) => (),
+   |            ^ expected one of `)`, `,`, or `|`
+
+error: expected a pattern, found an expression
+  --> $DIR/recover-pat-exprs.rs:81:9
+   |
+LL |         u8::MAX.abs() => (),
+   |         ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == u8::MAX.abs() => (),
+   |         ~~~ +++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = u8::MAX.abs();
+LL ~     match u8::MAX {
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { u8::MAX.abs() } => (),
+   |         +++++++               +
+
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:78:17
+  --> $DIR/recover-pat-exprs.rs:86:17
    |
 LL |         z @ w @ v.u() => (),
    |                 ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         z @ w @ val if val == v.u() => (),
+   |                 ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = v.u();
+LL ~     match u8::MAX {
+LL |         u8::MAX.abs() => (),
+...
+LL |
+LL ~         z @ w @ VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         z @ w @ const { v.u() } => (),
+   |                 +++++++       +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:80:9
+  --> $DIR/recover-pat-exprs.rs:88:9
    |
 LL |         y.ilog(3) => (),
    |         ^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == y.ilog(3) => (),
+   |         ~~~ +++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = y.ilog(3);
+LL ~     match u8::MAX {
+LL |         u8::MAX.abs() => (),
+...
+LL |
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { y.ilog(3) } => (),
+   |         +++++++           +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:82:9
+  --> $DIR/recover-pat-exprs.rs:90:9
    |
 LL |         n + 1 => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == n + 1 => (),
+   |         ~~~ +++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = n + 1;
+LL ~     match u8::MAX {
+LL |         u8::MAX.abs() => (),
+...
+LL |
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { n + 1 } => (),
+   |         +++++++       +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:84:10
+  --> $DIR/recover-pat-exprs.rs:92:10
    |
 LL |         ("".f() + 14 * 8) => (),
    |          ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         (val) if val == "".f() + 14 * 8 => (),
+   |          ~~~  +++++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = "".f() + 14 * 8;
+LL ~     match u8::MAX {
+LL |         u8::MAX.abs() => (),
+...
+LL |
+LL ~         (VAL) => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (const { "".f() + 14 * 8 }) => (),
+   |          +++++++                 +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:87:9
+  --> $DIR/recover-pat-exprs.rs:95:9
    |
 LL |         f?() => (),
    |         ^^^^ arbitrary expressions are not allowed in patterns
-
-error: expected one of `)`, `,`, or `|`, found `+`
-  --> $DIR/recover-pat-exprs.rs:89:12
    |
-LL |         (_ + 1) => (),
-   |            ^ expected one of `)`, `,`, or `|`
+help: consider moving the expression to a match arm guard
+   |
+LL |         val if val == f?() => (),
+   |         ~~~ ++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = f?();
+LL ~     match u8::MAX {
+LL |         u8::MAX.abs() => (),
+...
+LL |         0 | ((1) | 2) | 3 => (),
+LL ~         VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { f?() } => (),
+   |         +++++++      +
 
 error: expected a pattern, found an expression
-  --> $DIR/recover-pat-exprs.rs:93:9
+  --> $DIR/recover-pat-exprs.rs:101:9
    |
 LL |     let 1 + 1 = 2;
    |         ^^^^^ arbitrary expressions are not allowed in patterns
 
 error: expected one of `)`, `,`, `@`, or `|`, found `*`
-  --> $DIR/recover-pat-exprs.rs:96:28
+  --> $DIR/recover-pat-exprs.rs:104:28
    |
 LL |     let b = matches!(x, (x * x | x.f()) | x[0]);
    |                            ^ expected one of `)`, `,`, `@`, or `|`
@@ -255,5 +750,23 @@ LL |     let b = matches!(x, (x * x | x.f()) | x[0]);
    |
    = note: while parsing argument for this `pat` macro fragment
 
-error: aborting due to 41 previous errors
+error: expected a pattern, found an expression
+  --> $DIR/recover-pat-exprs.rs:60:10
+   |
+LL |         (1 + 2) * 3 => (),
+   |          ^^^^^ arbitrary expressions are not allowed in patterns
+
+error: expected a pattern, found an expression
+  --> $DIR/recover-pat-exprs.rs:75:5
+   |
+LL |     1 + 2 * PI.cos() => 2,
+   |     ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+
+error: expected a pattern, found an expression
+  --> $DIR/recover-pat-exprs.rs:83:9
+   |
+LL |         x.sqrt() @ .. => (),
+   |         ^^^^^^^^ arbitrary expressions are not allowed in patterns
+
+error: aborting due to 45 previous errors
 
diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr
index 793091bb1f1..596bff21395 100644
--- a/tests/ui/parser/recover/recover-pat-issues.stderr
+++ b/tests/ui/parser/recover/recover-pat-issues.stderr
@@ -3,36 +3,111 @@ error: expected a pattern, found an expression
    |
 LL |         Foo("hi".to_owned()) => true,
    |             ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         Foo(val) if val == "hi".to_owned() => true,
+   |             ~~~  +++++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = "hi".to_owned();
+LL ~     match foo {
+LL ~         Foo(VAL) => true,
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         Foo(const { "hi".to_owned() }) => true,
+   |             +++++++                 +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:14:20
    |
 LL |         Bar { baz: "hi".to_owned() } => true,
    |                    ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         Bar { baz } if baz == "hi".to_owned() => true,
+   |               ~~~   +++++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const BAZ: /* Type */ = "hi".to_owned();
+LL ~     match bar {
+LL ~         Bar { baz: BAZ } => true,
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         Bar { baz: const { "hi".to_owned() } } => true,
+   |                    +++++++                 +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:25:11
    |
 LL |         &["foo".to_string()] => {}
    |           ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider moving the expression to a match arm guard
+   |
+LL |         &[val] if val == "foo".to_string() => {}
+   |           ~~~  +++++++++++++++++++++++++++
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = "foo".to_string();
+LL ~     match foo.as_slice() {
+LL ~         &[VAL] => {}
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         &[const { "foo".to_string() }] => {}
+   |           +++++++                   +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:36:17
    |
 LL |     if let Some(MAGIC.0 as usize) = None::<usize> {}
    |                 ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = MAGIC.0 as usize;
+LL ~     if let Some(VAL) = None::<usize> {}
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |     if let Some(const { MAGIC.0 as usize }) = None::<usize> {}
+   |                 +++++++                  +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:41:13
    |
 LL |     if let (-1.some(4)) = (0, Some(4)) {}
    |             ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = -1.some(4);
+LL ~     if let (VAL) = (0, Some(4)) {}
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |     if let (const { -1.some(4) }) = (0, Some(4)) {}
+   |             +++++++            +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:44:13
    |
 LL |     if let (-1.Some(4)) = (0, Some(4)) {}
    |             ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = -1.Some(4);
+LL ~     if let (VAL) = (0, Some(4)) {}
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |     if let (const { -1.Some(4) }) = (0, Some(4)) {}
+   |             +++++++            +
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr
index b1d2a9b96d9..e54586b0924 100644
--- a/tests/ui/parser/recover/recover-pat-lets.stderr
+++ b/tests/ui/parser/recover/recover-pat-lets.stderr
@@ -21,12 +21,32 @@ error: expected a pattern, found an expression
    |
 LL |     let Some(1 + 1) = x else {
    |              ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 + 1;
+LL ~     let Some(VAL) = x else {
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |     let Some(const { 1 + 1 }) = x else {
+   |              +++++++       +
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:17:17
    |
 LL |     if let Some(1 + 1) = x {
    |                 ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 + 1;
+LL ~     if let Some(VAL) = x {
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |     if let Some(const { 1 + 1 }) = x {
+   |                 +++++++       +
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr
index 62ef7a8a908..088f83b0ccb 100644
--- a/tests/ui/parser/recover/recover-pat-ranges.stderr
+++ b/tests/ui/parser/recover/recover-pat-ranges.stderr
@@ -46,12 +46,6 @@ LL -         (0)..=(-4) => (),
 LL +         (0)..=-4 => (),
    |
 
-error: expected a pattern range bound, found an expression
-  --> $DIR/recover-pat-ranges.rs:11:12
-   |
-LL |         ..=1 + 2 => (),
-   |            ^^^^^ arbitrary expressions are not allowed in patterns
-
 error: range pattern bounds cannot have parentheses
   --> $DIR/recover-pat-ranges.rs:13:9
    |
@@ -64,12 +58,6 @@ LL -         (4).. => (),
 LL +         4.. => (),
    |
 
-error: expected a pattern range bound, found an expression
-  --> $DIR/recover-pat-ranges.rs:15:10
-   |
-LL |         (-4 + 0).. => (),
-   |          ^^^^^^ arbitrary expressions are not allowed in patterns
-
 error: range pattern bounds cannot have parentheses
   --> $DIR/recover-pat-ranges.rs:15:9
    |
@@ -82,12 +70,6 @@ LL -         (-4 + 0).. => (),
 LL +         -4 + 0.. => (),
    |
 
-error: expected a pattern range bound, found an expression
-  --> $DIR/recover-pat-ranges.rs:18:10
-   |
-LL |         (1 + 4)...1 * 2 => (),
-   |          ^^^^^ arbitrary expressions are not allowed in patterns
-
 error: range pattern bounds cannot have parentheses
   --> $DIR/recover-pat-ranges.rs:18:9
    |
@@ -101,22 +83,124 @@ LL +         1 + 4...1 * 2 => (),
    |
 
 error: expected a pattern range bound, found an expression
+  --> $DIR/recover-pat-ranges.rs:11:12
+   |
+LL |         ..=1 + 2 => (),
+   |            ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 + 2;
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         ..=VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         ..=const { 1 + 2 } => (),
+   |            +++++++       +
+
+error: expected a pattern range bound, found an expression
+  --> $DIR/recover-pat-ranges.rs:15:10
+   |
+LL |         (-4 + 0).. => (),
+   |          ^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = -4 + 0;
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         (VAL).. => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (const { -4 + 0 }).. => (),
+   |          +++++++        +
+
+error: expected a pattern range bound, found an expression
+  --> $DIR/recover-pat-ranges.rs:18:10
+   |
+LL |         (1 + 4)...1 * 2 => (),
+   |          ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 + 4;
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         (VAL)...1 * 2 => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (const { 1 + 4 })...1 * 2 => (),
+   |          +++++++       +
+
+error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:18:19
    |
 LL |         (1 + 4)...1 * 2 => (),
    |                   ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 1 * 2;
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         (1 + 4)...VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         (1 + 4)...const { 1 * 2 } => (),
+   |                   +++++++       +
 
 error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:24:9
    |
 LL |         0.x()..="y".z() => (),
    |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 0.x();
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         VAL..="y".z() => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         const { 0.x() }..="y".z() => (),
+   |         +++++++       +
 
 error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:24:17
    |
 LL |         0.x()..="y".z() => (),
    |                 ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = "y".z();
+LL ~     match -1 {
+LL |         0..=1 => (),
+...
+LL |
+LL ~         0.x()..=VAL => (),
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         0.x()..=const { "y".z() } => (),
+   |                 +++++++         +
 
 warning: `...` range patterns are deprecated
   --> $DIR/recover-pat-ranges.rs:18:16
diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr
index 64d477fdeb2..30307726a97 100644
--- a/tests/ui/parser/recover/recover-pat-wildcards.stderr
+++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr
@@ -59,12 +59,6 @@ error: expected one of `=>`, `if`, or `|`, found `(`
 LL |         ..(_) => ()
    |           ^ expected one of `=>`, `if`, or `|`
 
-error: expected a pattern range bound, found an expression
-  --> $DIR/recover-pat-wildcards.rs:55:14
-   |
-LL |         4..=(2 + _) => ()
-   |              ^^^^^ arbitrary expressions are not allowed in patterns
-
 error: range pattern bounds cannot have parentheses
   --> $DIR/recover-pat-wildcards.rs:55:13
    |
@@ -77,6 +71,23 @@ LL -         4..=(2 + _) => ()
 LL +         4..=2 + _ => ()
    |
 
+error: expected a pattern range bound, found an expression
+  --> $DIR/recover-pat-wildcards.rs:55:14
+   |
+LL |         4..=(2 + _) => ()
+   |              ^^^^^ arbitrary expressions are not allowed in patterns
+   |
+help: consider extracting the expression into a `const`
+   |
+LL +     const VAL: /* Type */ = 2 + _;
+LL ~     match 9 {
+LL ~         4..=(VAL) => ()
+   |
+help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`)
+   |
+LL |         4..=(const { 2 + _ }) => ()
+   |              +++++++       +
+
 error: aborting due to 11 previous errors
 
 For more information about this error, try `rustc --explain E0586`.