Skip to content

Commit 1868c8f

Browse files
authoredDec 8, 2024
Rollup merge of #133424 - Nadrieril:guard-patterns-parsing, r=fee1-dead
Parse guard patterns This implements the parsing of [RFC3637 Guard Patterns](https://rust-lang.github.io/rfcs/3637-guard-patterns.html) (see also [tracking issue](#129967)). This PR is extracted from #129996 with minor modifications. cc `@max-niederman`
2 parents f33a8c6 + 2459dbb commit 1868c8f

File tree

31 files changed

+328
-81
lines changed

31 files changed

+328
-81
lines changed
 

‎compiler/rustc_ast/src/ast.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,11 @@ impl Pat {
627627
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
628628

629629
// Trivial wrappers over inner patterns.
630-
PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => {
631-
s.walk(it)
632-
}
630+
PatKind::Box(s)
631+
| PatKind::Deref(s)
632+
| PatKind::Ref(s, _)
633+
| PatKind::Paren(s)
634+
| PatKind::Guard(s, _) => s.walk(it),
633635

634636
// These patterns do not contain subpatterns, skip.
635637
PatKind::Wild
@@ -839,6 +841,9 @@ pub enum PatKind {
839841
// A never pattern `!`.
840842
Never,
841843

844+
/// A guard pattern (e.g., `x if guard(x)`).
845+
Guard(P<Pat>, P<Expr>),
846+
842847
/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
843848
Paren(P<Pat>),
844849

‎compiler/rustc_ast/src/mut_visit.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
15251525
visit_opt(e2, |e| vis.visit_expr(e));
15261526
vis.visit_span(span);
15271527
}
1528+
PatKind::Guard(p, e) => {
1529+
vis.visit_pat(p);
1530+
vis.visit_expr(e);
1531+
}
15281532
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
15291533
visit_thin_vec(elems, |elem| vis.visit_pat(elem))
15301534
}

‎compiler/rustc_ast/src/visit.rs

+4
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
682682
visit_opt!(visitor, visit_expr, lower_bound);
683683
visit_opt!(visitor, visit_expr, upper_bound);
684684
}
685+
PatKind::Guard(subpattern, guard_condition) => {
686+
try_visit!(visitor.visit_pat(subpattern));
687+
try_visit!(visitor.visit_expr(guard_condition));
688+
}
685689
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
686690
PatKind::Err(_guar) => {}
687691
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {

‎compiler/rustc_ast_lowering/src/pat.rs

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
114114
self.lower_range_end(end, e2.is_some()),
115115
);
116116
}
117+
// FIXME(guard_patterns): lower pattern guards to HIR
118+
PatKind::Guard(inner, _) => pattern = inner,
117119
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
118120
PatKind::Rest => {
119121
// If we reach here the `..` pattern is not semantically allowed.

‎compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
556556
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
557557
gate_all!(explicit_tail_calls, "`become` expression is experimental");
558558
gate_all!(generic_const_items, "generic const items are experimental");
559+
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
559560
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
560561
gate_all!(postfix_match, "postfix match is experimental");
561562
gate_all!(mut_ref, "mutable by-reference bindings are experimental");

‎compiler/rustc_ast_pretty/src/pprust/state.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,14 @@ impl<'a> State<'a> {
17091709
self.print_expr(e, FixupContext::default());
17101710
}
17111711
}
1712+
PatKind::Guard(subpat, condition) => {
1713+
self.popen();
1714+
self.print_pat(subpat);
1715+
self.space();
1716+
self.word_space("if");
1717+
self.print_expr(condition, FixupContext::default());
1718+
self.pclose();
1719+
}
17121720
PatKind::Slice(elts) => {
17131721
self.word("[");
17141722
self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));

‎compiler/rustc_expand/src/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>(
990990
}
991991
}
992992
AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?),
993-
AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt(
993+
AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard(
994994
None,
995995
RecoverComma::No,
996996
RecoverColon::Yes,

‎compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ declare_features! (
505505
(incomplete, generic_const_items, "1.73.0", Some(113521)),
506506
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
507507
(unstable, global_registration, "1.80.0", Some(125119)),
508+
/// Allows using guards in patterns.
509+
(incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)),
508510
/// Allows using `..=X` as a patterns in slices.
509511
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
510512
/// Allows `if let` guard in match arms.

‎compiler/rustc_lint/src/unused.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens {
12351235
self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
12361236
},
12371237
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
1238-
Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1238+
Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
12391239
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
12401240
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
12411241
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),

‎compiler/rustc_parse/src/parser/expr.rs

+26-32
Original file line numberDiff line numberDiff line change
@@ -2631,7 +2631,7 @@ impl<'a> Parser<'a> {
26312631
};
26322632
self.bump(); // Eat `let` token
26332633
let lo = self.prev_token.span;
2634-
let pat = self.parse_pat_allow_top_alt(
2634+
let pat = self.parse_pat_no_top_guard(
26352635
None,
26362636
RecoverComma::Yes,
26372637
RecoverColon::Yes,
@@ -2778,7 +2778,7 @@ impl<'a> Parser<'a> {
27782778
};
27792779
// Try to parse the pattern `for ($PAT) in $EXPR`.
27802780
let pat = match (
2781-
self.parse_pat_allow_top_alt(
2781+
self.parse_pat_allow_top_guard(
27822782
None,
27832783
RecoverComma::Yes,
27842784
RecoverColon::Yes,
@@ -3241,7 +3241,7 @@ impl<'a> Parser<'a> {
32413241
// then we should recover.
32423242
let mut snapshot = this.create_snapshot_for_diagnostic();
32433243
let pattern_follows = snapshot
3244-
.parse_pat_allow_top_alt(
3244+
.parse_pat_no_top_guard(
32453245
None,
32463246
RecoverComma::Yes,
32473247
RecoverColon::Yes,
@@ -3315,43 +3315,37 @@ impl<'a> Parser<'a> {
33153315

33163316
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
33173317
if self.token == token::OpenDelim(Delimiter::Parenthesis) {
3318-
// Detect and recover from `($pat if $cond) => $arm`.
33193318
let left = self.token.span;
3320-
match self.parse_pat_allow_top_alt(
3319+
let pat = self.parse_pat_no_top_guard(
33213320
None,
33223321
RecoverComma::Yes,
33233322
RecoverColon::Yes,
33243323
CommaRecoveryMode::EitherTupleOrPipe,
3325-
) {
3326-
Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)),
3327-
Err(err)
3328-
if let prev_sp = self.prev_token.span
3329-
&& let true = self.eat_keyword(kw::If) =>
3330-
{
3331-
// We know for certain we've found `($pat if` so far.
3332-
let mut cond = match self.parse_match_guard_condition() {
3333-
Ok(cond) => cond,
3334-
Err(cond_err) => {
3335-
cond_err.cancel();
3336-
return Err(err);
3337-
}
3338-
};
3339-
err.cancel();
3340-
CondChecker::new(self).visit_expr(&mut cond);
3341-
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
3342-
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
3343-
let right = self.prev_token.span;
3344-
self.dcx().emit_err(errors::ParenthesesInMatchPat {
3345-
span: vec![left, right],
3346-
sugg: errors::ParenthesesInMatchPatSugg { left, right },
3347-
});
3348-
Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond)))
3349-
}
3350-
Err(err) => Err(err),
3324+
)?;
3325+
if let ast::PatKind::Paren(subpat) = &pat.kind
3326+
&& let ast::PatKind::Guard(..) = &subpat.kind
3327+
{
3328+
// Detect and recover from `($pat if $cond) => $arm`.
3329+
// FIXME(guard_patterns): convert this to a normal guard instead
3330+
let span = pat.span;
3331+
let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() };
3332+
let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else {
3333+
unreachable!()
3334+
};
3335+
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
3336+
CondChecker::new(self).visit_expr(&mut cond);
3337+
let right = self.prev_token.span;
3338+
self.dcx().emit_err(errors::ParenthesesInMatchPat {
3339+
span: vec![left, right],
3340+
sugg: errors::ParenthesesInMatchPatSugg { left, right },
3341+
});
3342+
Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
3343+
} else {
3344+
Ok((pat, self.parse_match_arm_guard()?))
33513345
}
33523346
} else {
33533347
// Regular parser flow:
3354-
let pat = self.parse_pat_allow_top_alt(
3348+
let pat = self.parse_pat_no_top_guard(
33553349
None,
33563350
RecoverComma::Yes,
33573351
RecoverColon::Yes,

‎compiler/rustc_parse/src/parser/nonterminal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<'a> Parser<'a> {
174174
NonterminalKind::Pat(pat_kind) => {
175175
NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
176176
PatParam { .. } => this.parse_pat_no_top_alt(None, None),
177-
PatWithOr => this.parse_pat_allow_top_alt(
177+
PatWithOr => this.parse_pat_no_top_guard(
178178
None,
179179
RecoverComma::No,
180180
RecoverColon::No,

‎compiler/rustc_parse/src/parser/pat.rs

+40-14
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,34 @@ pub enum PatternLocation {
9999
impl<'a> Parser<'a> {
100100
/// Parses a pattern.
101101
///
102-
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
103-
/// at the top level. Used when parsing the parameters of lambda expressions,
104-
/// functions, function pointers, and `pat` macro fragments.
102+
/// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level.
103+
/// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor
104+
/// `PatternNoTopAlt` (see below) are used.
105+
pub fn parse_pat_allow_top_guard(
106+
&mut self,
107+
expected: Option<Expected>,
108+
rc: RecoverComma,
109+
ra: RecoverColon,
110+
rt: CommaRecoveryMode,
111+
) -> PResult<'a, P<Pat>> {
112+
let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?;
113+
114+
if self.eat_keyword(kw::If) {
115+
let cond = self.parse_expr()?;
116+
// Feature-gate guard patterns
117+
self.psess.gated_spans.gate(sym::guard_patterns, cond.span);
118+
let span = pat.span.to(cond.span);
119+
Ok(self.mk_pat(span, PatKind::Guard(pat, cond)))
120+
} else {
121+
Ok(pat)
122+
}
123+
}
124+
125+
/// Parses a pattern.
126+
///
127+
/// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns
128+
/// or guard patterns at the top level. Used when parsing the parameters of lambda
129+
/// expressions, functions, function pointers, and `pat_param` macro fragments.
105130
pub fn parse_pat_no_top_alt(
106131
&mut self,
107132
expected: Option<Expected>,
@@ -112,25 +137,26 @@ impl<'a> Parser<'a> {
112137

113138
/// Parses a pattern.
114139
///
115-
/// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
116-
/// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used.
140+
/// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not
141+
/// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until
142+
/// the next edition) and `let`, `if let`, and `while let` expressions.
117143
///
118144
/// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>,
119145
/// a leading vert is allowed in nested or-patterns, too. This allows us to
120146
/// simplify the grammar somewhat.
121-
pub fn parse_pat_allow_top_alt(
147+
pub fn parse_pat_no_top_guard(
122148
&mut self,
123149
expected: Option<Expected>,
124150
rc: RecoverComma,
125151
ra: RecoverColon,
126152
rt: CommaRecoveryMode,
127153
) -> PResult<'a, P<Pat>> {
128-
self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
154+
self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
129155
}
130156

131157
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
132158
/// recovered).
133-
fn parse_pat_allow_top_alt_inner(
159+
fn parse_pat_no_top_guard_inner(
134160
&mut self,
135161
expected: Option<Expected>,
136162
rc: RecoverComma,
@@ -231,7 +257,7 @@ impl<'a> Parser<'a> {
231257
// We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
232258
// or-patterns so that we can detect when a user tries to use it. This allows us to print a
233259
// better error message.
234-
let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner(
260+
let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner(
235261
expected,
236262
rc,
237263
RecoverColon::No,
@@ -696,7 +722,7 @@ impl<'a> Parser<'a> {
696722
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
697723
// Parse `[pat, pat,...]` as a slice pattern.
698724
let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| {
699-
p.parse_pat_allow_top_alt(
725+
p.parse_pat_allow_top_guard(
700726
None,
701727
RecoverComma::No,
702728
RecoverColon::No,
@@ -944,7 +970,7 @@ impl<'a> Parser<'a> {
944970
let open_paren = self.token.span;
945971

946972
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
947-
p.parse_pat_allow_top_alt(
973+
p.parse_pat_allow_top_guard(
948974
None,
949975
RecoverComma::No,
950976
RecoverColon::No,
@@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> {
13611387
path: Path,
13621388
) -> PResult<'a, PatKind> {
13631389
let (fields, _) = self.parse_paren_comma_seq(|p| {
1364-
p.parse_pat_allow_top_alt(
1390+
p.parse_pat_allow_top_guard(
13651391
None,
13661392
RecoverComma::No,
13671393
RecoverColon::No,
@@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> {
13961422
self.parse_builtin(|self_, _lo, ident| {
13971423
Ok(match ident.name {
13981424
// builtin#deref(PAT)
1399-
sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt(
1425+
sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard(
14001426
None,
14011427
RecoverComma::Yes,
14021428
RecoverColon::Yes,
@@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> {
16711697
// Parsing a pattern of the form `fieldname: pat`.
16721698
let fieldname = self.parse_field_name()?;
16731699
self.bump();
1674-
let pat = self.parse_pat_allow_top_alt(
1700+
let pat = self.parse_pat_allow_top_guard(
16751701
None,
16761702
RecoverComma::No,
16771703
RecoverColon::No,

‎compiler/rustc_parse/src/parser/path.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ impl<'a> Parser<'a> {
469469
PathStyle::Pat
470470
if let Ok(_) = self
471471
.parse_paren_comma_seq(|p| {
472-
p.parse_pat_allow_top_alt(
472+
p.parse_pat_allow_top_guard(
473473
None,
474474
RecoverComma::No,
475475
RecoverColon::No,

‎compiler/rustc_passes/src/input_stats.rs

+1
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
555555
Slice,
556556
Rest,
557557
Never,
558+
Guard,
558559
Paren,
559560
MacCall,
560561
Err

‎compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,7 @@ symbols! {
999999
global_registration,
10001000
globs,
10011001
gt,
1002+
guard_patterns,
10021003
half_open_range_patterns,
10031004
half_open_range_patterns_in_slices,
10041005
hash,

‎src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
234234
// In the case of only two patterns, replacement adds net characters.
235235
| Ref(_, Mutability::Not)
236236
// Dealt with elsewhere.
237-
| Or(_) | Paren(_) | Deref(_) => false,
237+
| Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
238238
// Transform `box x | ... | box y` into `box (x | y)`.
239239
//
240240
// The cases below until `Slice(...)` deal with *singleton* products.

‎src/tools/rustfmt/src/patterns.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
4848
| ast::PatKind::MacCall(..)
4949
| ast::PatKind::Slice(..)
5050
| ast::PatKind::Path(..)
51-
| ast::PatKind::Range(..) => false,
51+
| ast::PatKind::Range(..)
52+
| ast::PatKind::Guard(..) => false,
5253
ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1,
5354
ast::PatKind::TupleStruct(_, ref path, ref subpats) => {
5455
path.segments.len() <= 1 && subpats.len() <= 1
@@ -338,8 +339,9 @@ impl Rewrite for Pat {
338339
.max_width_error(shape.width, self.span)?,
339340
)
340341
.map(|inner_pat| format!("({})", inner_pat)),
341-
PatKind::Err(_) => Err(RewriteError::Unknown),
342+
PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()),
342343
PatKind::Deref(_) => Err(RewriteError::Unknown),
344+
PatKind::Err(_) => Err(RewriteError::Unknown),
343345
}
344346
}
345347
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(guard_patterns)]
2+
3+
fn main() {
4+
match user.subscription_plan() {
5+
(Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => {
6+
// Complete the transaction.
7+
}
8+
_ => {
9+
// The user doesn't have enough credit, return an error message.
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
fn match_guards_still_work() {
2+
match 0 {
3+
0 if guard(0) => {},
4+
_ => {},
5+
}
6+
}
7+
8+
fn other_guards_dont() {
9+
match 0 {
10+
(0 if guard(0)) => {},
11+
//~^ ERROR unexpected parentheses surrounding `match` arm pattern
12+
_ => {},
13+
}
14+
15+
match 0 {
16+
(0 if guard(0)) | 1 => {},
17+
//~^ ERROR: guard patterns are experimental
18+
_ => {},
19+
}
20+
21+
let ((x if guard(x)) | x) = 0;
22+
//~^ ERROR: guard patterns are experimental
23+
//~| ERROR: cannot find value `x`
24+
25+
if let (x if guard(x)) = 0 {}
26+
//~^ ERROR: guard patterns are experimental
27+
//~| WARN: irrefutable
28+
29+
while let (x if guard(x)) = 0 {}
30+
//~^ ERROR: guard patterns are experimental
31+
//~| WARN: irrefutable
32+
33+
#[cfg(FALSE)]
34+
while let (x if guard(x)) = 0 {}
35+
//~^ ERROR: guard patterns are experimental
36+
}
37+
38+
fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
39+
//~^ ERROR: guard patterns are experimental
40+
//~| ERROR: cannot find value `x`
41+
42+
fn guard<T>(x: T) -> bool {
43+
unimplemented!()
44+
}
45+
46+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
error: unexpected parentheses surrounding `match` arm pattern
2+
--> $DIR/feature-gate-guard-patterns.rs:10:9
3+
|
4+
LL | (0 if guard(0)) => {},
5+
| ^ ^
6+
|
7+
help: remove parentheses surrounding the pattern
8+
|
9+
LL - (0 if guard(0)) => {},
10+
LL + 0 if guard(0) => {},
11+
|
12+
13+
error[E0425]: cannot find value `x` in this scope
14+
--> $DIR/feature-gate-guard-patterns.rs:21:22
15+
|
16+
LL | let ((x if guard(x)) | x) = 0;
17+
| ^ not found in this scope
18+
19+
error[E0425]: cannot find value `x` in this scope
20+
--> $DIR/feature-gate-guard-patterns.rs:38:45
21+
|
22+
LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
23+
| ^
24+
|
25+
help: the binding `x` is available in a different scope in the same function
26+
--> $DIR/feature-gate-guard-patterns.rs:21:11
27+
|
28+
LL | let ((x if guard(x)) | x) = 0;
29+
| ^
30+
31+
error[E0658]: guard patterns are experimental
32+
--> $DIR/feature-gate-guard-patterns.rs:16:15
33+
|
34+
LL | (0 if guard(0)) | 1 => {},
35+
| ^^^^^^^^
36+
|
37+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
38+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
39+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
40+
= help: consider using match arm guards
41+
42+
error[E0658]: guard patterns are experimental
43+
--> $DIR/feature-gate-guard-patterns.rs:21:16
44+
|
45+
LL | let ((x if guard(x)) | x) = 0;
46+
| ^^^^^^^^
47+
|
48+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
49+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
50+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
51+
= help: consider using match arm guards
52+
53+
error[E0658]: guard patterns are experimental
54+
--> $DIR/feature-gate-guard-patterns.rs:25:18
55+
|
56+
LL | if let (x if guard(x)) = 0 {}
57+
| ^^^^^^^^
58+
|
59+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
60+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
61+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
62+
= help: consider using match arm guards
63+
64+
error[E0658]: guard patterns are experimental
65+
--> $DIR/feature-gate-guard-patterns.rs:29:21
66+
|
67+
LL | while let (x if guard(x)) = 0 {}
68+
| ^^^^^^^^
69+
|
70+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
71+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
72+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
73+
= help: consider using match arm guards
74+
75+
error[E0658]: guard patterns are experimental
76+
--> $DIR/feature-gate-guard-patterns.rs:34:21
77+
|
78+
LL | while let (x if guard(x)) = 0 {}
79+
| ^^^^^^^^
80+
|
81+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
82+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
83+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
84+
= help: consider using match arm guards
85+
86+
error[E0658]: guard patterns are experimental
87+
--> $DIR/feature-gate-guard-patterns.rs:38:39
88+
|
89+
LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
90+
| ^^^^^^^^
91+
|
92+
= note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
93+
= help: add `#![feature(guard_patterns)]` to the crate attributes to enable
94+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
95+
= help: consider using match arm guards
96+
97+
warning: irrefutable `if let` pattern
98+
--> $DIR/feature-gate-guard-patterns.rs:25:8
99+
|
100+
LL | if let (x if guard(x)) = 0 {}
101+
| ^^^^^^^^^^^^^^^^^^^^^^^
102+
|
103+
= note: this pattern will always match, so the `if let` is useless
104+
= help: consider replacing the `if let` with a `let`
105+
= note: `#[warn(irrefutable_let_patterns)]` on by default
106+
107+
warning: irrefutable `while let` pattern
108+
--> $DIR/feature-gate-guard-patterns.rs:29:11
109+
|
110+
LL | while let (x if guard(x)) = 0 {}
111+
| ^^^^^^^^^^^^^^^^^^^^^^^
112+
|
113+
= note: this pattern will always match, so the loop will never exit
114+
= help: consider instead using a `loop { ... }` with a `let` inside it
115+
116+
error: aborting due to 9 previous errors; 2 warnings emitted
117+
118+
Some errors have detailed explanations: E0425, E0658.
119+
For more information about an error, try `rustc --explain E0425`.

‎tests/ui/parser/issues/issue-72373.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ fn foo(c: &[u32], n: u32) -> u32 {
33
[h, ..] if h > n => 0,
44
[h, ..] if h == n => 1,
55
[h, ref ts..] => foo(c, n - h) + foo(ts, n),
6-
//~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..`
6+
//~^ ERROR expected one of `,`, `@`, `]`, `if`, or `|`, found `..`
77
[] => 0,
88
}
99
}

‎tests/ui/parser/issues/issue-72373.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `,`, `@`, `]`, or `|`, found `..`
1+
error: expected one of `,`, `@`, `]`, `if`, or `|`, found `..`
22
--> $DIR/issue-72373.rs:5:19
33
|
44
LL | [h, ref ts..] => foo(c, n - h) + foo(ts, n),
5-
| ^^ expected one of `,`, `@`, `]`, or `|`
5+
| ^^ expected one of `,`, `@`, `]`, `if`, or `|`
66
|
77
help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@`
88
|

‎tests/ui/parser/misspelled-keywords/ref.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `)`, `,`, `@`, or `|`, found `list`
1+
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `list`
22
--> $DIR/ref.rs:4:19
33
|
44
LL | Some(refe list) => println!("{list:?}"),
5-
| ^^^^ expected one of `)`, `,`, `@`, or `|`
5+
| ^^^^ expected one of `)`, `,`, `@`, `if`, or `|`
66
|
77
help: there is a keyword `ref` with a similar name
88
|

‎tests/ui/parser/pat-lt-bracket-7.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ fn main() {
33
let foo = core::iter::empty();
44

55
for Thing(x[]) in foo {}
6-
//~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
6+
//~^ ERROR: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
77
}
88

99
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types

‎tests/ui/parser/pat-lt-bracket-7.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: expected one of `)`, `,`, `@`, or `|`, found `[`
1+
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
22
--> $DIR/pat-lt-bracket-7.rs:5:16
33
|
44
LL | for Thing(x[]) in foo {}
55
| ^
66
| |
7-
| expected one of `)`, `,`, `@`, or `|`
7+
| expected one of `)`, `,`, `@`, `if`, or `|`
88
| help: missing `,`
99

1010
error[E0308]: mismatched types

‎tests/ui/parser/recover/recover-pat-exprs.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn array_indexing() {
2727
{ let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
2828
{ let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
2929
{ let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
30-
{ let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[`
30+
{ let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
3131
//~^ missing `,`
3232
}
3333

@@ -95,12 +95,12 @@ fn main() {
9595
f?() => (),
9696
//~^ error: expected a pattern, found an expression
9797
(_ + 1) => (),
98-
//~^ error: expected one of `)`, `,`, or `|`, found `+`
98+
//~^ error: expected one of `)`, `,`, `if`, or `|`, found `+`
9999
}
100100

101101
let 1 + 1 = 2;
102102
//~^ error: expected a pattern, found an expression
103103

104104
let b = matches!(x, (x * x | x.f()) | x[0]);
105-
//~^ error: expected one of `)`, `,`, `@`, or `|`, found `*`
105+
//~^ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*`
106106
}

‎tests/ui/parser/recover/recover-pat-exprs.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
213213
LL | { let x[]; }
214214
| ^ expected one of `:`, `;`, `=`, `@`, or `|`
215215

216-
error: expected one of `)`, `,`, `@`, or `|`, found `[`
216+
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
217217
--> $DIR/recover-pat-exprs.rs:30:13
218218
|
219219
LL | { let (x[]); }
220220
| ^
221221
| |
222-
| expected one of `)`, `,`, `@`, or `|`
222+
| expected one of `)`, `,`, `@`, `if`, or `|`
223223
| help: missing `,`
224224

225225
error: expected a pattern, found an expression
@@ -611,11 +611,11 @@ LL | x.sqrt() @ .. => (),
611611
|
612612
= note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
613613

614-
error: expected one of `)`, `,`, or `|`, found `+`
614+
error: expected one of `)`, `,`, `if`, or `|`, found `+`
615615
--> $DIR/recover-pat-exprs.rs:97:12
616616
|
617617
LL | (_ + 1) => (),
618-
| ^ expected one of `)`, `,`, or `|`
618+
| ^ expected one of `)`, `,`, `if`, or `|`
619619

620620
error: expected a pattern, found an expression
621621
--> $DIR/recover-pat-exprs.rs:81:9
@@ -772,11 +772,11 @@ LL | let 1 + 1 = 2;
772772
|
773773
= note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>
774774

775-
error: expected one of `)`, `,`, `@`, or `|`, found `*`
775+
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*`
776776
--> $DIR/recover-pat-exprs.rs:104:28
777777
|
778778
LL | let b = matches!(x, (x * x | x.f()) | x[0]);
779-
| ^ expected one of `)`, `,`, `@`, or `|`
779+
| ^ expected one of `)`, `,`, `@`, `if`, or `|`
780780
--> $SRC_DIR/core/src/macros/mod.rs:LL:COL
781781
|
782782
= note: while parsing argument for this `pat` macro fragment

‎tests/ui/parser/recover/recover-pat-wildcards.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn a() {
88

99
fn b() {
1010
match 2 {
11-
(_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%`
11+
(_ % 4) => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `%`
1212
}
1313
}
1414

@@ -40,7 +40,7 @@ fn f() {
4040

4141
fn g() {
4242
match 7 {
43-
(_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*`
43+
(_ * 0)..5 => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `*`
4444
}
4545
}
4646

‎tests/ui/parser/recover/recover-pat-wildcards.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error: expected one of `=>`, `if`, or `|`, found `+`
44
LL | _ + 1 => ()
55
| ^ expected one of `=>`, `if`, or `|`
66

7-
error: expected one of `)`, `,`, or `|`, found `%`
7+
error: expected one of `)`, `,`, `if`, or `|`, found `%`
88
--> $DIR/recover-pat-wildcards.rs:11:12
99
|
1010
LL | (_ % 4) => ()
11-
| ^ expected one of `)`, `,`, or `|`
11+
| ^ expected one of `)`, `,`, `if`, or `|`
1212

1313
error: expected one of `=>`, `if`, or `|`, found `.`
1414
--> $DIR/recover-pat-wildcards.rs:17:10
@@ -47,11 +47,11 @@ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_`
4747
LL | 0..._ => ()
4848
| ^ expected one of `=>`, `if`, or `|`
4949

50-
error: expected one of `)`, `,`, or `|`, found `*`
50+
error: expected one of `)`, `,`, `if`, or `|`, found `*`
5151
--> $DIR/recover-pat-wildcards.rs:43:12
5252
|
5353
LL | (_ * 0)..5 => ()
54-
| ^ expected one of `)`, `,`, or `|`
54+
| ^ expected one of `)`, `,`, `if`, or `|`
5555

5656
error: expected one of `=>`, `if`, or `|`, found `(`
5757
--> $DIR/recover-pat-wildcards.rs:49:11

‎tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ LL | let a: u8 @ b = 0;
66
| |
77
| while parsing the type for `a`
88

9-
error: expected one of `)`, `,`, `@`, or `|`, found `:`
9+
error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:`
1010
--> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15
1111
|
1212
LL | let a @ (b: u8);
13-
| ^ expected one of `)`, `,`, `@`, or `|`
13+
| ^ expected one of `)`, `,`, `@`, `if`, or `|`
1414
|
1515
= note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
1616

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ run-pass
2+
//! Tests that the addition of guard patterns does not change the behavior of the `pat` macro
3+
//! fragment.
4+
#![feature(guard_patterns)]
5+
#![allow(incomplete_features)]
6+
7+
macro_rules! has_guard {
8+
($p:pat) => {
9+
false
10+
};
11+
($p:pat if $e:expr) => {
12+
true
13+
};
14+
}
15+
16+
fn main() {
17+
assert_eq!(has_guard!(Some(_)), false);
18+
assert_eq!(has_guard!(Some(_) if true), true);
19+
assert_eq!(has_guard!((Some(_) if true)), false);
20+
}

0 commit comments

Comments
 (0)
Please sign in to comment.