@@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
4
4
use crate :: ptr:: P ;
5
5
use crate :: ast:: { self , Attribute , Pat , PatKind , FieldPat , RangeEnd , RangeSyntax , Mac } ;
6
6
use crate :: ast:: { BindingMode , Ident , Mutability , Path , QSelf , Expr , ExprKind } ;
7
+ use crate :: mut_visit:: { noop_visit_pat, MutVisitor } ;
7
8
use crate :: parse:: token:: { self } ;
8
9
use crate :: print:: pprust;
9
10
use crate :: source_map:: { respan, Span , Spanned } ;
@@ -273,21 +274,20 @@ impl<'a> Parser<'a> {
273
274
// Parse _
274
275
PatKind :: Wild
275
276
} else if self . eat_keyword ( kw:: Mut ) {
276
- self . recover_pat_ident_mut_first ( ) ?
277
+ self . parse_pat_ident_mut ( ) ?
277
278
} else if self . eat_keyword ( kw:: Ref ) {
278
279
// Parse ref ident @ pat / ref mut ident @ pat
279
280
let mutbl = self . parse_mutability ( ) ;
280
281
self . parse_pat_ident ( BindingMode :: ByRef ( mutbl) ) ?
281
282
} else if self . eat_keyword ( kw:: Box ) {
282
283
// Parse `box pat`
283
284
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 ( ) {
286
286
// Parse `ident @ pat`
287
287
// This can give false positives and parse nullary enums,
288
288
// they are dealt with later in resolve.
289
289
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 ( ) {
291
291
// Parse pattern starting with a path
292
292
let ( qself, path) = if self . eat_lt ( ) {
293
293
// Parse a qualified path
@@ -384,24 +384,108 @@ impl<'a> Parser<'a> {
384
384
} )
385
385
}
386
386
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
+
387
420
/// Recover on `mut ref? ident @ pat` and suggest
388
421
/// 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" )
401
463
} else {
402
- BindingMode :: ByValue ( Mutability :: Mutable )
464
+ ( "`mut` must be followed by a named binding" , "remove the `mut` prefix" )
403
465
} ;
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 ( ) ;
405
489
}
406
490
407
491
/// Parse macro invocation
@@ -479,17 +563,6 @@ impl<'a> Parser<'a> {
479
563
Err ( err)
480
564
}
481
565
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
-
493
566
/// Is the current token suitable as the start of a range patterns end?
494
567
fn is_pat_range_end_start ( & self ) -> bool {
495
568
self . token . is_path_start ( ) // e.g. `MY_CONST`;
@@ -563,6 +636,30 @@ impl<'a> Parser<'a> {
563
636
}
564
637
}
565
638
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
+
566
663
/// Parses `ident` or `ident @ pat`.
567
664
/// Used by the copy foo and ref foo patterns to give a good
568
665
/// error message when parsing mistakes like `ref foo(a, b)`.
0 commit comments