@@ -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,108 @@ impl<'a> Parser<'a> {
384384 } )
385385 }
386386
387+ /// Parse a mutable binding with the `mut` token already eaten.
388+ fn parse_pat_ident_mut ( & mut self ) -> PResult < ' a , PatKind > {
389+ let mut_span = self . prev_span ;
390+
391+ if self . eat_keyword ( kw:: Ref ) {
392+ return self . recover_mut_ref_ident ( mut_span)
393+ }
394+
395+ self . recover_additional_muts ( ) ;
396+
397+ // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`.
398+ if let token:: Interpolated ( ref nt) = self . token . kind {
399+ if let token:: NtPat ( _) = * * nt {
400+ self . expected_ident_found ( ) . emit ( ) ;
401+ }
402+ }
403+
404+ // Parse the pattern we hope to be an identifier.
405+ let mut pat = self . parse_pat ( Some ( "identifier" ) ) ?;
406+
407+ // Add `mut` to any binding in the parsed pattern.
408+ let changed_any_binding = Self :: make_all_value_bindings_mutable ( & 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, changed_any_binding) ,
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 )
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+ /// Turn all by-value immutable bindings in a pattern into mutable bindings.
437+ /// Returns `true` if any change was made.
438+ fn make_all_value_bindings_mutable ( pat : & mut P < Pat > ) -> bool {
439+ struct AddMut ( bool ) ;
440+ impl MutVisitor for AddMut {
441+ fn visit_pat ( & mut self , pat : & mut P < Pat > ) {
442+ if let PatKind :: Ident ( BindingMode :: ByValue ( ref mut m @ Mutability :: Immutable ) , ..)
443+ = pat. node
444+ {
445+ * m = Mutability :: Mutable ;
446+ self . 0 = true ;
447+ }
448+ noop_visit_pat ( pat, self ) ;
449+ }
450+ }
451+
452+ let mut add_mut = AddMut ( false ) ;
453+ add_mut. visit_pat ( pat) ;
454+ add_mut. 0
455+ }
456+
457+ /// Error on `mut $pat` where `$pat` is not an ident.
458+ fn ban_mut_general_pat ( & self , lo : Span , pat : & Pat , changed_any_binding : bool ) {
459+ let span = lo. to ( pat. span ) ;
460+ let fix = pprust:: pat_to_string ( & pat) ;
461+ let ( problem, suggestion) = if changed_any_binding {
462+ ( "`mut` must be attached to each individual binding" , "add `mut` to each binding" )
401463 } else {
402- BindingMode :: ByValue ( Mutability :: Mutable )
464+ ( "`mut` must be followed by a named binding" , "remove the `mut` prefix" )
403465 } ;
404- self . parse_pat_ident ( binding_mode)
466+ self . struct_span_err ( span, problem)
467+ . span_suggestion ( span, suggestion, fix, Applicability :: MachineApplicable )
468+ . note ( "`mut` may be followed by `variable` and `variable @ pattern`" )
469+ . emit ( )
470+ }
471+
472+ /// Eat any extraneous `mut`s and error + recover if we ate any.
473+ fn recover_additional_muts ( & mut self ) {
474+ let lo = self . token . span ;
475+ while self . eat_keyword ( kw:: Mut ) { }
476+ if lo == self . token . span {
477+ return ;
478+ }
479+
480+ let span = lo. to ( self . prev_span ) ;
481+ self . struct_span_err ( span, "`mut` on a binding may not be repeated" )
482+ . span_suggestion (
483+ span,
484+ "remove the additional `mut`s" ,
485+ String :: new ( ) ,
486+ Applicability :: MachineApplicable ,
487+ )
488+ . emit ( ) ;
405489 }
406490
407491 /// Parse macro invocation
@@ -479,17 +563,6 @@ impl<'a> Parser<'a> {
479563 Err ( err)
480564 }
481565
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-
493566 /// Is the current token suitable as the start of a range patterns end?
494567 fn is_pat_range_end_start ( & self ) -> bool {
495568 self . token . is_path_start ( ) // e.g. `MY_CONST`;
@@ -563,6 +636,30 @@ impl<'a> Parser<'a> {
563636 }
564637 }
565638
639+ /// Is this the start of a pattern beginning with a path?
640+ fn is_start_of_pat_with_path ( & mut self ) -> bool {
641+ self . check_path ( )
642+ // Just for recovery (see `can_be_ident`).
643+ || self . token . is_ident ( ) && !self . token . is_bool_lit ( ) && !self . token . is_keyword ( kw:: In )
644+ }
645+
646+ /// Would `parse_pat_ident` be appropriate here?
647+ fn can_be_ident_pat ( & mut self ) -> bool {
648+ self . check_ident ( )
649+ && !self . token . is_bool_lit ( ) // Avoid `true` or `false` as a binding as it is a literal.
650+ && !self . token . is_path_segment_keyword ( ) // Avoid e.g. `Self` as it is a path.
651+ // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
652+ && !self . token . is_keyword ( kw:: In )
653+ && self . look_ahead ( 1 , |t| match t. kind { // Try to do something more complex?
654+ token:: OpenDelim ( token:: Paren ) // A tuple struct pattern.
655+ | token:: OpenDelim ( token:: Brace ) // A struct pattern.
656+ | token:: DotDotDot | token:: DotDotEq | token:: DotDot // A range pattern.
657+ | token:: ModSep // A tuple / struct variant pattern.
658+ | token:: Not => false , // A macro expanding to a pattern.
659+ _ => true ,
660+ } )
661+ }
662+
566663 /// Parses `ident` or `ident @ pat`.
567664 /// Used by the copy foo and ref foo patterns to give a good
568665 /// error message when parsing mistakes like `ref foo(a, b)`.
0 commit comments