From fe30d9e3af02ec0c045ed733c91bba1f8fabfb72 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 22 Dec 2025 20:23:27 +0000 Subject: [PATCH] feat: Add if-matches syntax sugar This adds sugar for `if let` expressions, allowing `if opt matches Some(x)`. Co-authored-by: agent --- compiler/rustc_parse/src/parser/expr.rs | 36 ++++++++++++++++++++++++- tests/ui/parser/if-matches.rs | 19 +++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/ui/parser/if-matches.rs diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 35b987cf50fa2..867abab786216 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -155,7 +155,41 @@ impl<'a> Parser<'a> { } self.expected_token_types.insert(TokenType::Operator); - while let Some(op) = self.check_assoc_op() { + // `matches` is parsed as a "trailing let" pseudo-expression in conditions: + // + // if matches { ... } + // + // This reuses `ExprKind::Let(, , ..)`, i.e. it is equivalent to: + // + // if let = { ... } + // + // To avoid breaking valid code that uses `matches` as an identifier, we only + // treat it specially when `Restrictions::ALLOW_LET` is enabled (which is only + // the case when parsing `if`/`while` conditions via `parse_expr_cond`). + loop { + if self.restrictions.contains(Restrictions::ALLOW_LET) { + if let Some((ident, IdentIsRaw::No)) = self.token.ident() + && ident.name.as_str() == "matches" + { + let lo = lhs.span; + self.bump(); // Eat `matches` + let pat = self.parse_pat_no_top_guard( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?; + let span = lo.to(pat.span); + lhs = self.mk_expr(span, ExprKind::Let(pat, lhs, span, Recovered::No)); + parsed_something = true; + // Continue parsing, to allow `&&` let-chains etc. + continue; + } + } + + let Some(op) = self.check_assoc_op() else { + break; + }; let lhs_span = self.interpolated_or_expr_span(&lhs); let cur_op_span = self.token.span; let restrictions = if op.node.is_assign_like() { diff --git a/tests/ui/parser/if-matches.rs b/tests/ui/parser/if-matches.rs new file mode 100644 index 0000000000000..2dcbd97c12570 --- /dev/null +++ b/tests/ui/parser/if-matches.rs @@ -0,0 +1,19 @@ +//@ check-pass + +fn main() { + let opt = Some(1); + + // New "natural" matching syntax (sugar for `if let Some(x) = opt { .. }`). + if opt matches Some(x) { + let _ = x + 1; + } else { + unreachable!(); + } + + // Still allowed: `matches` as an identifier. + let matches = true; + if matches { + let _ = 0; + } +} +