@@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
44use crate :: ptr:: P ;
55use crate :: ast:: { self , Attribute , Pat , PatKind , FieldPat , RangeEnd , RangeSyntax , Mac } ;
66use crate :: ast:: { BindingMode , Ident , Mutability , Path , QSelf , Expr , ExprKind } ;
7+ use crate :: mut_visit:: { noop_visit_pat, MutVisitor } ;
78use crate :: parse:: token:: { self } ;
89use crate :: print:: pprust;
910use crate :: source_map:: { respan, Span , Spanned } ;
@@ -273,21 +274,20 @@ impl<'a> Parser<'a> {
273274 // Parse _
274275 PatKind :: Wild
275276 } else if self . eat_keyword ( kw:: Mut ) {
276- self . recover_pat_ident_mut_first ( ) ?
277+ self . parse_pat_ident_mut ( ) ?
277278 } else if self . eat_keyword ( kw:: Ref ) {
278279 // Parse ref ident @ pat / ref mut ident @ pat
279280 let mutbl = self . parse_mutability ( ) ;
280281 self . parse_pat_ident ( BindingMode :: ByRef ( mutbl) ) ?
281282 } else if self . eat_keyword ( kw:: Box ) {
282283 // Parse `box pat`
283284 PatKind :: Box ( self . parse_pat_with_range_pat ( false , None ) ?)
284- } else if self . token . is_ident ( ) && !self . token . is_reserved_ident ( ) &&
285- self . parse_as_ident ( ) {
285+ } else if self . can_be_ident_pat ( ) {
286286 // Parse `ident @ pat`
287287 // This can give false positives and parse nullary enums,
288288 // they are dealt with later in resolve.
289289 self . parse_pat_ident ( BindingMode :: ByValue ( Mutability :: Immutable ) ) ?
290- } else if self . token . is_path_start ( ) {
290+ } else if self . is_start_of_pat_with_path ( ) {
291291 // Parse pattern starting with a path
292292 let ( qself, path) = if self . eat_lt ( ) {
293293 // Parse a qualified path
@@ -384,24 +384,85 @@ impl<'a> Parser<'a> {
384384 } )
385385 }
386386
387+ fn parse_pat_ident_mut ( & mut self ) -> PResult < ' a , PatKind > {
388+ let mut_span = self . prev_span ;
389+
390+ if self . eat_keyword ( kw:: Ref ) {
391+ return self . recover_mut_ref_ident ( mut_span)
392+ }
393+
394+ self . recover_additional_muts ( ) ;
395+
396+ let mut pat = self . parse_pat ( Some ( "identifier" ) ) ?;
397+
398+ // Add `mut` to any binding in the parsed pattern.
399+ struct AddMut ;
400+ impl MutVisitor for AddMut {
401+ fn visit_pat ( & mut self , pat : & mut P < Pat > ) {
402+ if let PatKind :: Ident ( BindingMode :: ByValue ( ref mut m) , ..) = pat. node {
403+ * m = Mutability :: Mutable ;
404+ }
405+ noop_visit_pat ( pat, self ) ;
406+ }
407+ }
408+ AddMut . visit_pat ( & mut pat) ;
409+
410+ // Unwrap; If we don't have `mut $ident`, error.
411+ let pat = pat. into_inner ( ) ;
412+ match & pat. node {
413+ PatKind :: Ident ( ..) => { }
414+ _ => self . ban_mut_general_pat ( mut_span, & pat) ,
415+ }
416+
417+ Ok ( pat. node )
418+ }
419+
387420 /// Recover on `mut ref? ident @ pat` and suggest
388421 /// that the order of `mut` and `ref` is incorrect.
389- fn recover_pat_ident_mut_first ( & mut self ) -> PResult < ' a , PatKind > {
390- let mutref_span = self . prev_span . to ( self . token . span ) ;
391- let binding_mode = if self . eat_keyword ( kw:: Ref ) {
392- self . struct_span_err ( mutref_span, "the order of `mut` and `ref` is incorrect" )
393- . span_suggestion (
394- mutref_span,
395- "try switching the order" ,
396- "ref mut" . into ( ) ,
397- Applicability :: MachineApplicable
398- )
399- . emit ( ) ;
400- BindingMode :: ByRef ( Mutability :: Mutable )
401- } else {
402- BindingMode :: ByValue ( Mutability :: Mutable )
403- } ;
404- self . parse_pat_ident ( binding_mode)
422+ fn recover_mut_ref_ident ( & mut self , lo : Span ) -> PResult < ' a , PatKind > {
423+ let mutref_span = lo. to ( self . prev_span ) ;
424+ self . struct_span_err ( mutref_span, "the order of `mut` and `ref` is incorrect" )
425+ . span_suggestion (
426+ mutref_span,
427+ "try switching the order" ,
428+ "ref mut" . into ( ) ,
429+ Applicability :: MachineApplicable
430+ )
431+ . emit ( ) ;
432+
433+ self . parse_pat_ident ( BindingMode :: ByRef ( Mutability :: Mutable ) )
434+ }
435+
436+ /// Error on `mut $pat` where `$pat` is not an ident.
437+ fn ban_mut_general_pat ( & self , lo : Span , pat : & Pat ) {
438+ let span = lo. to ( pat. span ) ;
439+ self . struct_span_err ( span, "`mut` must be attached to each individual binding" )
440+ . span_suggestion (
441+ span,
442+ "add `mut` to each binding" ,
443+ pprust:: pat_to_string ( & pat) ,
444+ Applicability :: MachineApplicable ,
445+ )
446+ . emit ( ) ;
447+ }
448+
449+ /// Eat any extraneous `mut`s and error + recover if we ate any.
450+ fn recover_additional_muts ( & mut self ) {
451+ let lo = self . token . span ;
452+ while self . eat_keyword ( kw:: Mut ) { }
453+ if lo == self . token . span {
454+ return ;
455+ }
456+
457+ let span = lo. to ( self . prev_span ) ;
458+ self . struct_span_err ( span, "`mut` on a binding may not be repeated" )
459+ . span_suggestion (
460+ span,
461+ "remove the additional `mut`s" ,
462+ String :: new ( ) ,
463+ Applicability :: MachineApplicable ,
464+ )
465+ . emit ( ) ;
405466 }
406467
407468 /// Parse macro invocation
@@ -479,17 +540,6 @@ impl<'a> Parser<'a> {
479540 Err ( err)
480541 }
481542
482- // Helper function to decide whether to parse as ident binding
483- // or to try to do something more complex like range patterns.
484- fn parse_as_ident ( & mut self ) -> bool {
485- self . look_ahead ( 1 , |t| match t. kind {
486- token:: OpenDelim ( token:: Paren ) | token:: OpenDelim ( token:: Brace ) |
487- token:: DotDotDot | token:: DotDotEq | token:: DotDot |
488- token:: ModSep | token:: Not => false ,
489- _ => true ,
490- } )
491- }
492-
493543 /// Is the current token suitable as the start of a range patterns end?
494544 fn is_pat_range_end_start ( & self ) -> bool {
495545 self . token . is_path_start ( ) // e.g. `MY_CONST`;
@@ -563,6 +613,30 @@ impl<'a> Parser<'a> {
563613 }
564614 }
565615
616+ /// Is this the start of a pattern beginning with a path?
617+ fn is_start_of_pat_with_path ( & mut self ) -> bool {
618+ self . check_path ( )
619+ // Just for recovery (see `can_be_ident`).
620+ || self . token . is_ident ( ) && !self . token . is_bool_lit ( ) && !self . token . is_keyword ( kw:: In )
621+ }
622+
623+ /// Would `parse_pat_ident` be appropriate here?
624+ fn can_be_ident_pat ( & mut self ) -> bool {
625+ self . check_ident ( )
626+ && !self . token . is_bool_lit ( ) // Avoid `true` or `false` as a binding as it is a literal.
627+ && !self . token . is_path_segment_keyword ( ) // Avoid e.g. `Self` as it is a path.
628+ // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
629+ && !self . token . is_keyword ( kw:: In )
630+ && self . look_ahead ( 1 , |t| match t. kind { // Try to do something more complex?
631+ token:: OpenDelim ( token:: Paren ) // A tuple struct pattern.
632+ | token:: OpenDelim ( token:: Brace ) // A struct pattern.
633+ | token:: DotDotDot | token:: DotDotEq | token:: DotDot // A range pattern.
634+ | token:: ModSep // A tuple / struct variant pattern.
635+ | token:: Not => false , // A macro expanding to a pattern.
636+ _ => true ,
637+ } )
638+ }
639+
566640 /// Parses `ident` or `ident @ pat`.
567641 /// Used by the copy foo and ref foo patterns to give a good
568642 /// error message when parsing mistakes like `ref foo(a, b)`.
0 commit comments