syntax: add recovery for intersection patterns p1 @ p2
This commit is contained in:
parent
aa2ae564d3
commit
29fb07d245
1 changed files with 60 additions and 0 deletions
|
@ -367,6 +367,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let pat = self.mk_pat(lo.to(self.prev_span), pat);
|
let pat = self.mk_pat(lo.to(self.prev_span), pat);
|
||||||
let pat = self.maybe_recover_from_bad_qpath(pat, true)?;
|
let pat = self.maybe_recover_from_bad_qpath(pat, true)?;
|
||||||
|
let pat = self.recover_intersection_pat(pat)?;
|
||||||
|
|
||||||
if !allow_range_pat {
|
if !allow_range_pat {
|
||||||
self.ban_pat_range_if_ambiguous(&pat)?
|
self.ban_pat_range_if_ambiguous(&pat)?
|
||||||
|
@ -375,6 +376,65 @@ impl<'a> Parser<'a> {
|
||||||
Ok(pat)
|
Ok(pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
|
||||||
|
///
|
||||||
|
/// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
|
||||||
|
/// should already have been parsed by now at this point,
|
||||||
|
/// if the next token is `@` then we can try to parse the more general form.
|
||||||
|
///
|
||||||
|
/// Consult `parse_pat_ident` for the `binding` grammar.
|
||||||
|
///
|
||||||
|
/// The notion of intersection patterns are found in
|
||||||
|
/// e.g. [F#][and] where they are called AND-patterns.
|
||||||
|
///
|
||||||
|
/// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
|
||||||
|
fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> {
|
||||||
|
if self.token.kind != token::At {
|
||||||
|
// Next token is not `@` so it's not going to be an intersection pattern.
|
||||||
|
return Ok(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we attempt to parse `@ $pat_rhs` and emit an error.
|
||||||
|
self.bump(); // `@`
|
||||||
|
let mut rhs = self.parse_pat(None)?;
|
||||||
|
let sp = lhs.span.to(rhs.span);
|
||||||
|
|
||||||
|
if let PatKind::Ident(_, _, ref mut sub @ None) = rhs.kind {
|
||||||
|
// The user inverted the order, so help them fix that.
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
lhs.walk(&mut |p| match p.kind {
|
||||||
|
// `check_match` is unhappy if the subpattern has a binding anywhere.
|
||||||
|
PatKind::Ident(..) => {
|
||||||
|
applicability = Applicability::MaybeIncorrect;
|
||||||
|
false // Short-circuit.
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let lhs_span = lhs.span;
|
||||||
|
// Move the LHS into the RHS as a subpattern.
|
||||||
|
// The RHS is now the full pattern.
|
||||||
|
*sub = Some(lhs);
|
||||||
|
|
||||||
|
self.struct_span_err(sp, "pattern on wrong side of `@`")
|
||||||
|
.span_label(lhs_span, "pattern on the left, should be to the right")
|
||||||
|
.span_label(rhs.span, "binding on the right, should be to the left")
|
||||||
|
.span_suggestion(sp, "switch the order", pprust::pat_to_string(&rhs), applicability)
|
||||||
|
.emit();
|
||||||
|
|
||||||
|
rhs.span = sp;
|
||||||
|
return Ok(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`.
|
||||||
|
let mut err = self.struct_span_err(sp, "left-hand side of `@` must be a binding pattern");
|
||||||
|
err.span_label(lhs.span, "interpreted as a pattern, not a binding")
|
||||||
|
.span_label(rhs.span, "also a pattern")
|
||||||
|
.note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`");
|
||||||
|
// FIXME(Centril): Introduce `PatKind::Err` and use that instead.
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
/// Ban a range pattern if it has an ambiguous interpretation.
|
/// Ban a range pattern if it has an ambiguous interpretation.
|
||||||
fn ban_pat_range_if_ambiguous(&self, pat: &Pat) -> PResult<'a, ()> {
|
fn ban_pat_range_if_ambiguous(&self, pat: &Pat) -> PResult<'a, ()> {
|
||||||
match pat.kind {
|
match pat.kind {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue