@@ -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 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+
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 {
0 commit comments