From f8e50d87368099032e2ae04a1e3c8fca9fdfeee8 Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Thu, 22 Aug 2024 16:48:12 -0700 Subject: [PATCH 1/8] add `guard_patterns` unstable feature, without unstable book chapter for now --- compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index cf0f2a7e48c93..2d577f3f35d18 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -504,6 +504,8 @@ declare_features! ( (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3d0ec2afa2b73..105f24d95cc28 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -985,6 +985,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, From 35bbc45f1651024579ff9091698684ef4a1609ca Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Thu, 22 Aug 2024 22:05:48 -0700 Subject: [PATCH 2/8] refactor pat parser method names/doc-comments to agree with RFC 3637 --- compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 10 +++---- .../rustc_parse/src/parser/nonterminal.rs | 2 +- compiler/rustc_parse/src/parser/pat.rs | 29 ++++++++++--------- compiler/rustc_parse/src/parser/path.rs | 2 +- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 19c2d466f7ca6..3b49d1b90d6b7 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index aa5e9586daf96..a6c7c4e3bba34 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2630,7 +2630,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2776,7 +2776,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3239,7 +3239,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3315,7 +3315,7 @@ impl<'a> Parser<'a> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + match self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3349,7 +3349,7 @@ impl<'a> Parser<'a> { } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 8fb6f85d0dd8f..752a52b382b38 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c4326427f67b0..a33960d027792 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -97,9 +97,9 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option, @@ -110,25 +110,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments and + /// `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in , /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option, rc: RecoverComma, @@ -229,7 +230,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -696,7 +697,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -944,7 +945,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1359,7 +1360,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1394,7 +1395,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1669,7 +1670,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20b4..9587b53a44b12 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, From 9b8bfed73b7ff55cbc3041c73283c12279221cbf Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Thu, 22 Aug 2024 16:49:45 -0700 Subject: [PATCH 3/8] add guard pattern AST node --- compiler/rustc_ast/src/ast.rs | 11 ++++++++--- compiler/rustc_ast/src/mut_visit.rs | 4 ++++ compiler/rustc_ast/src/visit.rs | 4 ++++ compiler/rustc_ast_lowering/src/pat.rs | 2 ++ compiler/rustc_ast_pretty/src/pprust/state.rs | 6 ++++++ compiler/rustc_lint/src/unused.rs | 2 +- compiler/rustc_passes/src/input_stats.rs | 1 + 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 792de77e9d4c1..ab1605a92cb5a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -632,9 +632,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -844,6 +846,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P, P), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 198e1bca77443..ae0664239dd95 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1520,6 +1520,10 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 20ac9fa02bb53..4dca4c78aef13 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -679,6 +679,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ace7bfb5c73f2..c4bae084a3f8c 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + // FIXME(guard_patterns): lower pattern guards to HIR + PatKind::Guard(inner, _) => pattern = inner, PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index d7c531f37608d..32eea2befbfd8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1709,6 +1709,12 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5ec920d39f49f..adf157fd0bf58 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index db34189be2a93..dcc36334248b2 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -556,6 +556,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err From a3a29f50ef9f59ccd2e9178ff439d9825d79522f Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Sun, 6 Oct 2024 23:09:30 -0700 Subject: [PATCH 4/8] cover guard patterns in rustfmt --- src/tools/rustfmt/src/patterns.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 6fe2d4a8520e3..7bc699b07b0ce 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { | ast::PatKind::MacCall(..) | ast::PatKind::Slice(..) | ast::PatKind::Path(..) - | ast::PatKind::Range(..) => false, + | ast::PatKind::Range(..) + | ast::PatKind::Guard(..) => false, ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, ast::PatKind::TupleStruct(_, ref path, ref subpats) => { path.segments.len() <= 1 && subpats.len() <= 1 @@ -340,6 +341,7 @@ impl Rewrite for Pat { .map(|inner_pat| format!("({})", inner_pat)), PatKind::Err(_) => Err(RewriteError::Unknown), PatKind::Deref(_) => Err(RewriteError::Unknown), + PatKind::Guard(..) => Err(RewriteError::Unknown), } } } From f86915a6828b64abfa75936ab0f1cddf7fdf7fee Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Sat, 5 Oct 2024 22:09:20 -0700 Subject: [PATCH 5/8] cover guard patterns in clippy lints --- src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index c7c837de505e4..c649d5e5e1ee2 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // In the case of only two patterns, replacement adds net characters. | Ref(_, Mutability::Not) // Dealt with elsewhere. - | Or(_) | Paren(_) | Deref(_) => false, + | Or(_) | Paren(_) | Deref(_) | Guard(..) => false, // Transform `box x | ... | box y` into `box (x | y)`. // // The cases below until `Slice(...)` deal with *singleton* products. From 962c0140c71f07642a7c39243c8a9a314dd26ba5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 24 Nov 2024 17:36:52 +0100 Subject: [PATCH 6/8] parse guard patterns Co-authored-by: Max Niederman --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 52 ++++---- compiler/rustc_parse/src/parser/pat.rs | 39 ++++-- compiler/rustc_parse/src/parser/path.rs | 2 +- .../feature-gate-guard-patterns.rs | 46 +++++++ .../feature-gate-guard-patterns.stderr | 119 ++++++++++++++++++ tests/ui/parser/issues/issue-72373.rs | 2 +- tests/ui/parser/issues/issue-72373.stderr | 4 +- .../ui/parser/misspelled-keywords/ref.stderr | 4 +- tests/ui/parser/pat-lt-bracket-7.rs | 2 +- tests/ui/parser/pat-lt-bracket-7.stderr | 4 +- tests/ui/parser/recover/recover-pat-exprs.rs | 6 +- .../parser/recover/recover-pat-exprs.stderr | 12 +- .../parser/recover/recover-pat-wildcards.rs | 4 +- .../recover/recover-pat-wildcards.stderr | 8 +- ...pe-ascription-syntactically-invalid.stderr | 4 +- 17 files changed, 248 insertions(+), 63 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-guard-patterns.rs create mode 100644 tests/ui/feature-gates/feature-gate-guard-patterns.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8a392e4407b2c..6379cf692838c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -551,6 +551,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3b49d1b90d6b7..cbaf67ef2ab70 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_no_top_guard( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a6c7c4e3bba34..adbb6b441047e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2776,7 +2776,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_no_top_guard( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3313,39 +3313,33 @@ impl<'a> Parser<'a> { fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P, Option>)> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { - // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_no_top_guard( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, CommaRecoveryMode::EitherTupleOrPipe, - ) { - Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), - Err(err) - if let prev_sp = self.prev_token.span - && let true = self.eat_keyword(kw::If) => - { - // We know for certain we've found `($pat if` so far. - let mut cond = match self.parse_match_guard_condition() { - Ok(cond) => cond, - Err(cond_err) => { - cond_err.cancel(); - return Err(err); - } - }; - err.cancel(); - CondChecker::new(self).visit_expr(&mut cond); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let right = self.prev_token.span; - self.dcx().emit_err(errors::ParenthesesInMatchPat { - span: vec![left, right], - sugg: errors::ParenthesesInMatchPatSugg { left, right }, - }); - Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) - } - Err(err) => Err(err), + )?; + if let ast::PatKind::Paren(subpat) = &pat.kind + && let ast::PatKind::Guard(..) = &subpat.kind + { + // Detect and recover from `($pat if $cond) => $arm`. + // FIXME(guard_patterns): convert this to a normal guard instead + let span = pat.span; + let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { + unreachable!() + }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + CondChecker::new(self).visit_expr(&mut cond); + let right = self.prev_token.span; + self.dcx().emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + } else { + Ok((pat, self.parse_match_arm_guard()?)) } } else { // Regular parser flow: diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index a33960d027792..d0d0d8124d1ab 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -95,6 +95,31 @@ pub enum PatternLocation { } impl<'a> Parser<'a> { + /// Parses a pattern. + /// + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + // Feature-gate guard patterns + self.psess.gated_spans.gate(sym::guard_patterns, cond.span); + let span = pat.span.to(cond.span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + /// Parses a pattern. /// /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns @@ -111,8 +136,8 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not - /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments and - /// `let`, `if let`, and `while let` expressions. + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in , /// a leading vert is allowed in nested or-patterns, too. This allows us to @@ -697,7 +722,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_no_top_guard( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -945,7 +970,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_no_top_guard( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1360,7 +1385,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_no_top_guard( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1395,7 +1420,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_no_top_guard( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1670,7 +1695,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_no_top_guard( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 9587b53a44b12..6a7029a8f1c82 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_no_top_guard( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs new file mode 100644 index 0000000000000..929e8ef3181f9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -0,0 +1,46 @@ +fn match_guards_still_work() { + match 0 { + 0 if guard(0) => {}, + _ => {}, + } +} + +fn other_guards_dont() { + match 0 { + (0 if guard(0)) => {}, + //~^ ERROR unexpected parentheses surrounding `match` arm pattern + _ => {}, + } + + match 0 { + (0 if guard(0)) | 1 => {}, + //~^ ERROR: guard patterns are experimental + _ => {}, + } + + let ((x if guard(x)) | x) = 0; + //~^ ERROR: guard patterns are experimental + //~| ERROR: cannot find value `x` + + if let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental + //~| WARN: irrefutable + + while let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental + //~| WARN: irrefutable + + #[cfg(FALSE)] + while let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental +} + +fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} +//~^ ERROR: guard patterns are experimental +//~| ERROR: cannot find value `x` + +fn guard(x: T) -> bool { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr new file mode 100644 index 0000000000000..0613b5c95a413 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -0,0 +1,119 @@ +error: unexpected parentheses surrounding `match` arm pattern + --> $DIR/feature-gate-guard-patterns.rs:10:9 + | +LL | (0 if guard(0)) => {}, + | ^ ^ + | +help: remove parentheses surrounding the pattern + | +LL - (0 if guard(0)) => {}, +LL + 0 if guard(0) => {}, + | + +error[E0425]: cannot find value `x` in this scope + --> $DIR/feature-gate-guard-patterns.rs:21:22 + | +LL | let ((x if guard(x)) | x) = 0; + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/feature-gate-guard-patterns.rs:38:45 + | +LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} + | ^ + | +help: the binding `x` is available in a different scope in the same function + --> $DIR/feature-gate-guard-patterns.rs:21:11 + | +LL | let ((x if guard(x)) | x) = 0; + | ^ + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:16:15 + | +LL | (0 if guard(0)) | 1 => {}, + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:21:16 + | +LL | let ((x if guard(x)) | x) = 0; + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:25:18 + | +LL | if let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:29:21 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:34:21 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:38:39 + | +LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +warning: irrefutable `if let` pattern + --> $DIR/feature-gate-guard-patterns.rs:25:8 + | +LL | if let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this pattern will always match, so the `if let` is useless + = help: consider replacing the `if let` with a `let` + = note: `#[warn(irrefutable_let_patterns)]` on by default + +warning: irrefutable `while let` pattern + --> $DIR/feature-gate-guard-patterns.rs:29:11 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this pattern will always match, so the loop will never exit + = help: consider instead using a `loop { ... }` with a `let` inside it + +error: aborting due to 9 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/issues/issue-72373.rs b/tests/ui/parser/issues/issue-72373.rs index 4da6061c27fe8..ed88d53539bd4 100644 --- a/tests/ui/parser/issues/issue-72373.rs +++ b/tests/ui/parser/issues/issue-72373.rs @@ -3,7 +3,7 @@ fn foo(c: &[u32], n: u32) -> u32 { [h, ..] if h > n => 0, [h, ..] if h == n => 1, [h, ref ts..] => foo(c, n - h) + foo(ts, n), - //~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..` + //~^ ERROR expected one of `,`, `@`, `]`, `if`, or `|`, found `..` [] => 0, } } diff --git a/tests/ui/parser/issues/issue-72373.stderr b/tests/ui/parser/issues/issue-72373.stderr index c596c6abda553..d566d6f5fd138 100644 --- a/tests/ui/parser/issues/issue-72373.stderr +++ b/tests/ui/parser/issues/issue-72373.stderr @@ -1,8 +1,8 @@ -error: expected one of `,`, `@`, `]`, or `|`, found `..` +error: expected one of `,`, `@`, `]`, `if`, or `|`, found `..` --> $DIR/issue-72373.rs:5:19 | LL | [h, ref ts..] => foo(c, n - h) + foo(ts, n), - | ^^ expected one of `,`, `@`, `]`, or `|` + | ^^ expected one of `,`, `@`, `]`, `if`, or `|` | help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@` | diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr index b8b52702314c8..398d9d6bb99b1 100644 --- a/tests/ui/parser/misspelled-keywords/ref.stderr +++ b/tests/ui/parser/misspelled-keywords/ref.stderr @@ -1,8 +1,8 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `list` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `list` --> $DIR/ref.rs:4:19 | LL | Some(refe list) => println!("{list:?}"), - | ^^^^ expected one of `)`, `,`, `@`, or `|` + | ^^^^ expected one of `)`, `,`, `@`, `if`, or `|` | help: there is a keyword `ref` with a similar name | diff --git a/tests/ui/parser/pat-lt-bracket-7.rs b/tests/ui/parser/pat-lt-bracket-7.rs index 327aef5ad1570..abaeb4c83c05f 100644 --- a/tests/ui/parser/pat-lt-bracket-7.rs +++ b/tests/ui/parser/pat-lt-bracket-7.rs @@ -3,7 +3,7 @@ fn main() { let foo = core::iter::empty(); for Thing(x[]) in foo {} - //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^ ERROR: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` } const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types diff --git a/tests/ui/parser/pat-lt-bracket-7.stderr b/tests/ui/parser/pat-lt-bracket-7.stderr index 004dcfb2a7b2d..cc457a4e64e24 100644 --- a/tests/ui/parser/pat-lt-bracket-7.stderr +++ b/tests/ui/parser/pat-lt-bracket-7.stderr @@ -1,10 +1,10 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/pat-lt-bracket-7.rs:5:16 | LL | for Thing(x[]) in foo {} | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error[E0308]: mismatched types diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs index e5e25df0c01cc..a78bb82828d3a 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.rs +++ b/tests/ui/parser/recover/recover-pat-exprs.rs @@ -27,7 +27,7 @@ fn array_indexing() { { let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - { let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[` + { let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` //~^ missing `,` } @@ -95,12 +95,12 @@ fn main() { f?() => (), //~^ error: expected a pattern, found an expression (_ + 1) => (), - //~^ error: expected one of `)`, `,`, or `|`, found `+` + //~^ error: expected one of `)`, `,`, `if`, or `|`, found `+` } let 1 + 1 = 2; //~^ error: expected a pattern, found an expression let b = matches!(x, (x * x | x.f()) | x[0]); - //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` + //~^ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` } diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 041dfd647ad07..281eeced40242 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -213,13 +213,13 @@ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` LL | { let x[]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/recover-pat-exprs.rs:30:13 | LL | { let (x[]); } | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error: expected a pattern, found an expression @@ -611,11 +611,11 @@ LL | x.sqrt() @ .. => (), | = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` -error: expected one of `)`, `,`, or `|`, found `+` +error: expected one of `)`, `,`, `if`, or `|`, found `+` --> $DIR/recover-pat-exprs.rs:97:12 | LL | (_ + 1) => (), - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:81:9 @@ -772,11 +772,11 @@ LL | let 1 + 1 = 2; | = note: arbitrary expressions are not allowed in patterns: -error: expected one of `)`, `,`, `@`, or `|`, found `*` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` --> $DIR/recover-pat-exprs.rs:104:28 | LL | let b = matches!(x, (x * x | x.f()) | x[0]); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` --> $SRC_DIR/core/src/macros/mod.rs:LL:COL | = note: while parsing argument for this `pat` macro fragment diff --git a/tests/ui/parser/recover/recover-pat-wildcards.rs b/tests/ui/parser/recover/recover-pat-wildcards.rs index f506e2223d608..d4d28ce63587a 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.rs +++ b/tests/ui/parser/recover/recover-pat-wildcards.rs @@ -8,7 +8,7 @@ fn a() { fn b() { match 2 { - (_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%` + (_ % 4) => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `%` } } @@ -40,7 +40,7 @@ fn f() { fn g() { match 7 { - (_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*` + (_ * 0)..5 => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `*` } } diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index 81a9920f6a243..f939e5133700f 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -4,11 +4,11 @@ error: expected one of `=>`, `if`, or `|`, found `+` LL | _ + 1 => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `%` +error: expected one of `)`, `,`, `if`, or `|`, found `%` --> $DIR/recover-pat-wildcards.rs:11:12 | LL | (_ % 4) => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `.` --> $DIR/recover-pat-wildcards.rs:17:10 @@ -47,11 +47,11 @@ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` LL | 0..._ => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `*` +error: expected one of `)`, `,`, `if`, or `|`, found `*` --> $DIR/recover-pat-wildcards.rs:43:12 | LL | (_ * 0)..5 => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `(` --> $DIR/recover-pat-wildcards.rs:49:11 diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr index da8f4ca5f0cd7..6ce8f6d31a031 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr @@ -6,11 +6,11 @@ LL | let a: u8 @ b = 0; | | | while parsing the type for `a` -error: expected one of `)`, `,`, `@`, or `|`, found `:` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:` --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15 | LL | let a @ (b: u8); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` | = note: type ascription syntax has been removed, see issue #101728 From 483f9e258055bcd37c162cbf2af24aa7450ab0ea Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 24 Nov 2024 22:57:33 +0100 Subject: [PATCH 7/8] Fix rustfmt according to review --- src/tools/rustfmt/src/patterns.rs | 4 ++-- src/tools/rustfmt/tests/target/guard_patterns.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/tools/rustfmt/tests/target/guard_patterns.rs diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 7bc699b07b0ce..7b4730eadc854 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -339,9 +339,9 @@ impl Rewrite for Pat { .max_width_error(shape.width, self.span)?, ) .map(|inner_pat| format!("({})", inner_pat)), - PatKind::Err(_) => Err(RewriteError::Unknown), + PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()), PatKind::Deref(_) => Err(RewriteError::Unknown), - PatKind::Guard(..) => Err(RewriteError::Unknown), + PatKind::Err(_) => Err(RewriteError::Unknown), } } } diff --git a/src/tools/rustfmt/tests/target/guard_patterns.rs b/src/tools/rustfmt/tests/target/guard_patterns.rs new file mode 100644 index 0000000000000..2e4667b916cda --- /dev/null +++ b/src/tools/rustfmt/tests/target/guard_patterns.rs @@ -0,0 +1,12 @@ +#![feature(guard_patterns)] + +fn main() { + match user.subscription_plan() { + (Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => { + // Complete the transaction. + } + _ => { + // The user doesn't have enough credit, return an error message. + } + } +} From 2459dbb4bad87c38284b0a2ba4f2d6d37db99627 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 7 Dec 2024 12:49:08 +0100 Subject: [PATCH 8/8] Address review comments --- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 ++ .../rfc-3637-guard-patterns/macro-rules.rs | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 32eea2befbfd8..fd2e97a79ca9a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1710,10 +1710,12 @@ impl<'a> State<'a> { } } PatKind::Guard(subpat, condition) => { + self.popen(); self.print_pat(subpat); self.space(); self.word_space("if"); self.print_expr(condition, FixupContext::default()); + self.pclose(); } PatKind::Slice(elts) => { self.word("["); diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs new file mode 100644 index 0000000000000..76681f45bb336 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs @@ -0,0 +1,20 @@ +//@ run-pass +//! Tests that the addition of guard patterns does not change the behavior of the `pat` macro +//! fragment. +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +macro_rules! has_guard { + ($p:pat) => { + false + }; + ($p:pat if $e:expr) => { + true + }; +} + +fn main() { + assert_eq!(has_guard!(Some(_)), false); + assert_eq!(has_guard!(Some(_) if true), true); + assert_eq!(has_guard!((Some(_) if true)), false); +}