@@ -367,6 +367,7 @@ impl<'a> Parser<'a> {
367
367
368
368
let pat = self . mk_pat ( lo. to ( self . prev_span ) , pat) ;
369
369
let pat = self . maybe_recover_from_bad_qpath ( pat, true ) ?;
370
+ let pat = self . recover_intersection_pat ( pat) ?;
370
371
371
372
if !allow_range_pat {
372
373
self . ban_pat_range_if_ambiguous ( & pat) ?
@@ -375,6 +376,65 @@ impl<'a> Parser<'a> {
375
376
Ok ( pat)
376
377
}
377
378
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 to the right" )
421
+ . span_label ( rhs. span , "binding on the right, should be to the left" )
422
+ . span_suggestion ( sp, "switch the order" , pprust:: pat_to_string ( & rhs) , applicability)
423
+ . emit ( ) ;
424
+
425
+ rhs. span = sp;
426
+ return Ok ( rhs) ;
427
+ }
428
+
429
+ // The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`.
430
+ let mut err = self . struct_span_err ( sp, "left-hand side of `@` must be a binding pattern" ) ;
431
+ err. span_label ( lhs. span , "interpreted as a pattern, not a binding" )
432
+ . span_label ( rhs. span , "also a pattern" )
433
+ . note ( "bindings are `x`, `mut x`, `ref x`, and `ref mut x`" ) ;
434
+ // FIXME(Centril): Introduce `PatKind::Err` and use that instead.
435
+ Err ( err)
436
+ }
437
+
378
438
/// Ban a range pattern if it has an ambiguous interpretation.
379
439
fn ban_pat_range_if_ambiguous ( & self , pat : & Pat ) -> PResult < ' a , ( ) > {
380
440
match pat. kind {
0 commit comments