Skip to content

Commit 1b18237

Browse files
authored
Rollup merge of #65410 - Centril:intersection-pat-recover, r=davidtwco,varkor
syntax: add parser recovery for intersection- / and-patterns `p1 @ p2` Fixes #65400. The recovery comes in two flavors: 1. We know that `p2` is a binding so we can invert as `p2 @ p1`: ```rust error: pattern on wrong side of `@` --> $DIR/intersection-patterns.rs:13:9 | LL | Some(x) @ y => {} | -------^^^- | | | | | binding on the right, should be to the left | pattern on the left, should be to the right | help: switch the order: `y @ Some(x)` ``` 2. Otherwise we emit a generic diagnostic for the lack of support for intersection patterns: ```rust error: left-hand side of `@` must be a binding --> $DIR/intersection-patterns.rs:23:9 | LL | Some(x) @ Some(y) => {} | -------^^^------- | | | | | also a pattern | interpreted as a pattern, not a binding | = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` ``` For more on and-patterns, see e.g. https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching#and-pattern. r? @davidtwco cc @varkor @lzutao
2 parents a14e35f + 16266a5 commit 1b18237

File tree

4 files changed

+135
-1
lines changed

4 files changed

+135
-1
lines changed

src/libsyntax/parse/parser/pat.rs

+60
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ impl<'a> Parser<'a> {
367367

368368
let pat = self.mk_pat(lo.to(self.prev_span), pat);
369369
let pat = self.maybe_recover_from_bad_qpath(pat, true)?;
370+
let pat = self.recover_intersection_pat(pat)?;
370371

371372
if !allow_range_pat {
372373
self.ban_pat_range_if_ambiguous(&pat)?
@@ -375,6 +376,65 @@ impl<'a> Parser<'a> {
375376
Ok(pat)
376377
}
377378

379+
/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
380+
///
381+
/// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
382+
/// should already have been parsed by now at this point,
383+
/// if the next token is `@` then we can try to parse the more general form.
384+
///
385+
/// Consult `parse_pat_ident` for the `binding` grammar.
386+
///
387+
/// The notion of intersection patterns are found in
388+
/// e.g. [F#][and] where they are called AND-patterns.
389+
///
390+
/// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
391+
fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> {
392+
if self.token.kind != token::At {
393+
// Next token is not `@` so it's not going to be an intersection pattern.
394+
return Ok(lhs);
395+
}
396+
397+
// At this point we attempt to parse `@ $pat_rhs` and emit an error.
398+
self.bump(); // `@`
399+
let mut rhs = self.parse_pat(None)?;
400+
let sp = lhs.span.to(rhs.span);
401+
402+
if let PatKind::Ident(_, _, ref mut sub @ None) = rhs.kind {
403+
// The user inverted the order, so help them fix that.
404+
let mut applicability = Applicability::MachineApplicable;
405+
lhs.walk(&mut |p| match p.kind {
406+
// `check_match` is unhappy if the subpattern has a binding anywhere.
407+
PatKind::Ident(..) => {
408+
applicability = Applicability::MaybeIncorrect;
409+
false // Short-circuit.
410+
},
411+
_ => true,
412+
});
413+
414+
let lhs_span = lhs.span;
415+
// Move the LHS into the RHS as a subpattern.
416+
// The RHS is now the full pattern.
417+
*sub = Some(lhs);
418+
419+
self.struct_span_err(sp, "pattern on wrong side of `@`")
420+
.span_label(lhs_span, "pattern on the left, should be on the right")
421+
.span_label(rhs.span, "binding on the right, should be on the left")
422+
.span_suggestion(sp, "switch the order", pprust::pat_to_string(&rhs), applicability)
423+
.emit();
424+
} else {
425+
// The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`.
426+
rhs.kind = PatKind::Wild;
427+
self.struct_span_err(sp, "left-hand side of `@` must be a binding")
428+
.span_label(lhs.span, "interpreted as a pattern, not a binding")
429+
.span_label(rhs.span, "also a pattern")
430+
.note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`")
431+
.emit();
432+
}
433+
434+
rhs.span = sp;
435+
Ok(rhs)
436+
}
437+
378438
/// Ban a range pattern if it has an ambiguous interpretation.
379439
fn ban_pat_range_if_ambiguous(&self, pat: &Pat) -> PResult<'a, ()> {
380440
match pat.kind {

src/libsyntax/print/pprust.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2381,7 +2381,8 @@ impl<'a> State<'a> {
23812381
}
23822382
self.print_ident(ident);
23832383
if let Some(ref p) = *sub {
2384-
self.s.word("@");
2384+
self.s.space();
2385+
self.s.word_space("@");
23852386
self.print_pat(p);
23862387
}
23872388
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// This tests the parser recovery in `recover_intersection_pat`
2+
// and serves as a regression test for the diagnostics issue #65400.
3+
//
4+
// The general idea is that for `$pat_lhs @ $pat_rhs` where
5+
// `$pat_lhs` is not generated by `ref? mut? $ident` we want
6+
// to suggest either switching the order or note that intersection
7+
// patterns are not allowed.
8+
9+
fn main() {
10+
let s: Option<u8> = None;
11+
12+
match s {
13+
Some(x) @ y => {}
14+
//~^ ERROR pattern on wrong side of `@`
15+
//~| pattern on the left, should be on the right
16+
//~| binding on the right, should be on the left
17+
//~| HELP switch the order
18+
//~| SUGGESTION y @ Some(x)
19+
_ => {}
20+
}
21+
22+
match s {
23+
Some(x) @ Some(y) => {}
24+
//~^ ERROR left-hand side of `@` must be a binding
25+
//~| interpreted as a pattern, not a binding
26+
//~| also a pattern
27+
//~| NOTE bindings are `x`, `mut x`, `ref x`, and `ref mut x`
28+
_ => {}
29+
}
30+
31+
match 2 {
32+
1 ..= 5 @ e => {}
33+
//~^ ERROR pattern on wrong side of `@`
34+
//~| pattern on the left, should be on the right
35+
//~| binding on the right, should be on the left
36+
//~| HELP switch the order
37+
//~| SUGGESTION e @ 1 ..=5
38+
_ => {}
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: pattern on wrong side of `@`
2+
--> $DIR/intersection-patterns.rs:13:9
3+
|
4+
LL | Some(x) @ y => {}
5+
| -------^^^-
6+
| | |
7+
| | binding on the right, should be on the left
8+
| pattern on the left, should be on the right
9+
| help: switch the order: `y @ Some(x)`
10+
11+
error: left-hand side of `@` must be a binding
12+
--> $DIR/intersection-patterns.rs:23:9
13+
|
14+
LL | Some(x) @ Some(y) => {}
15+
| -------^^^-------
16+
| | |
17+
| | also a pattern
18+
| interpreted as a pattern, not a binding
19+
|
20+
= note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
21+
22+
error: pattern on wrong side of `@`
23+
--> $DIR/intersection-patterns.rs:32:9
24+
|
25+
LL | 1 ..= 5 @ e => {}
26+
| -------^^^-
27+
| | |
28+
| | binding on the right, should be on the left
29+
| pattern on the left, should be on the right
30+
| help: switch the order: `e @ 1 ..=5`
31+
32+
error: aborting due to 3 previous errors
33+

0 commit comments

Comments
 (0)