@@ -17,7 +17,6 @@ use rustc_span::symbol::kw;
17
17
use rustc_span:: { MultiSpan , Span , SpanSnippetError , DUMMY_SP } ;
18
18
19
19
use log:: { debug, trace} ;
20
- use std:: mem;
21
20
22
21
const TURBOFISH : & str = "use `::<...>` instead of `<...>` to specify type arguments" ;
23
22
@@ -459,9 +458,28 @@ impl<'a> Parser<'a> {
459
458
err : & mut DiagnosticBuilder < ' _ > ,
460
459
inner_op : & Expr ,
461
460
outer_op : & Spanned < AssocOp > ,
462
- ) {
461
+ ) -> bool /* advanced the cursor */ {
463
462
if let ExprKind :: Binary ( op, ref l1, ref r1) = inner_op. kind {
464
- match ( op. node , & outer_op. node ) {
463
+ if let ExprKind :: Field ( _, ident) = l1. kind {
464
+ if ident. as_str ( ) . parse :: < i32 > ( ) . is_err ( ) && !matches ! ( r1. kind, ExprKind :: Lit ( _) ) {
465
+ // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
466
+ // suggestion being the only one to apply is high.
467
+ return false ;
468
+ }
469
+ }
470
+ let mut enclose = |left : Span , right : Span | {
471
+ err. multipart_suggestion (
472
+ "parenthesize the comparison" ,
473
+ vec ! [
474
+ ( left. shrink_to_lo( ) , "(" . to_string( ) ) ,
475
+ ( right. shrink_to_hi( ) , ")" . to_string( ) ) ,
476
+ ] ,
477
+ Applicability :: MaybeIncorrect ,
478
+ ) ;
479
+ } ;
480
+ return match ( op. node , & outer_op. node ) {
481
+ // `x == y == z`
482
+ ( BinOpKind :: Eq , AssocOp :: Equal ) |
465
483
// `x < y < z` and friends.
466
484
( BinOpKind :: Lt , AssocOp :: Less ) | ( BinOpKind :: Lt , AssocOp :: LessEqual ) |
467
485
( BinOpKind :: Le , AssocOp :: LessEqual ) | ( BinOpKind :: Le , AssocOp :: Less ) |
@@ -472,35 +490,55 @@ impl<'a> Parser<'a> {
472
490
self . span_to_snippet ( e. span )
473
491
. unwrap_or_else ( |_| pprust:: expr_to_string ( & e) )
474
492
} ;
475
- err. span_suggestion (
476
- inner_op. span . to ( outer_op. span ) ,
477
- "split the comparison into two..." ,
478
- format ! (
479
- "{} {} {} && {} {}" ,
480
- expr_to_str( & l1) ,
481
- op. node. to_string( ) ,
482
- expr_to_str( & r1) ,
483
- expr_to_str( & r1) ,
484
- outer_op. node. to_ast_binop( ) . unwrap( ) . to_string( ) ,
485
- ) ,
486
- Applicability :: MaybeIncorrect ,
487
- ) ;
488
- err. span_suggestion (
489
- inner_op. span . to ( outer_op. span ) ,
490
- "...or parenthesize one of the comparisons" ,
491
- format ! (
492
- "({} {} {}) {}" ,
493
- expr_to_str( & l1) ,
494
- op. node. to_string( ) ,
495
- expr_to_str( & r1) ,
496
- outer_op. node. to_ast_binop( ) . unwrap( ) . to_string( ) ,
497
- ) ,
493
+ err. span_suggestion_verbose (
494
+ inner_op. span . shrink_to_hi ( ) ,
495
+ "split the comparison into two" ,
496
+ format ! ( " && {}" , expr_to_str( & r1) ) ,
498
497
Applicability :: MaybeIncorrect ,
499
498
) ;
499
+ false // Keep the current parse behavior, where the AST is `(x < y) < z`.
500
500
}
501
- _ => { }
502
- }
501
+ // `x == y < z`
502
+ ( BinOpKind :: Eq , AssocOp :: Less ) | ( BinOpKind :: Eq , AssocOp :: LessEqual ) |
503
+ ( BinOpKind :: Eq , AssocOp :: Greater ) | ( BinOpKind :: Eq , AssocOp :: GreaterEqual ) => {
504
+ // Consume `z`/outer-op-rhs.
505
+ let snapshot = self . clone ( ) ;
506
+ match self . parse_expr ( ) {
507
+ Ok ( r2) => {
508
+ // We are sure that outer-op-rhs could be consumed, the suggestion is
509
+ // likely correct.
510
+ enclose ( r1. span , r2. span ) ;
511
+ true
512
+ }
513
+ Err ( mut expr_err) => {
514
+ expr_err. cancel ( ) ;
515
+ * self = snapshot;
516
+ false
517
+ }
518
+ }
519
+ }
520
+ // `x > y == z`
521
+ ( BinOpKind :: Lt , AssocOp :: Equal ) | ( BinOpKind :: Le , AssocOp :: Equal ) |
522
+ ( BinOpKind :: Gt , AssocOp :: Equal ) | ( BinOpKind :: Ge , AssocOp :: Equal ) => {
523
+ let snapshot = self . clone ( ) ;
524
+ // At this point it is always valid to enclose the lhs in parentheses, no
525
+ // further checks are necessary.
526
+ match self . parse_expr ( ) {
527
+ Ok ( _) => {
528
+ enclose ( l1. span , r1. span ) ;
529
+ true
530
+ }
531
+ Err ( mut expr_err) => {
532
+ expr_err. cancel ( ) ;
533
+ * self = snapshot;
534
+ false
535
+ }
536
+ }
537
+ }
538
+ _ => false ,
539
+ } ;
503
540
}
541
+ false
504
542
}
505
543
506
544
/// Produces an error if comparison operators are chained (RFC #558).
@@ -534,31 +572,26 @@ impl<'a> Parser<'a> {
534
572
|this : & Self , span| Ok ( Some ( this. mk_expr ( span, ExprKind :: Err , AttrVec :: new ( ) ) ) ) ;
535
573
536
574
match inner_op. kind {
537
- ExprKind :: Binary ( op, _, _) if op. node . is_comparison ( ) => {
538
- // Respan to include both operators.
539
- let op_span = op. span . to ( self . prev_token . span ) ;
540
- let mut err =
541
- self . struct_span_err ( op_span, "comparison operators cannot be chained" ) ;
542
-
543
- // If it looks like a genuine attempt to chain operators (as opposed to a
544
- // misformatted turbofish, for instance), suggest a correct form.
545
- self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op) ;
575
+ ExprKind :: Binary ( op, ref l1, ref r1) if op. node . is_comparison ( ) => {
576
+ let mut err = self . struct_span_err (
577
+ vec ! [ op. span, self . prev_token. span] ,
578
+ "comparison operators cannot be chained" ,
579
+ ) ;
546
580
547
581
let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
548
582
err. span_suggestion_verbose (
549
- op_span . shrink_to_lo ( ) ,
583
+ op . span . shrink_to_lo ( ) ,
550
584
TURBOFISH ,
551
585
"::" . to_string ( ) ,
552
586
Applicability :: MaybeIncorrect ,
553
587
) ;
554
588
} ;
555
589
556
- if op . node == BinOpKind :: Lt &&
557
- outer_op . node == AssocOp :: Less || // Include `<` to provide this recommendation
558
- outer_op. node == AssocOp :: Greater
559
- // even in a case like the following:
590
+ // Include `<` to provide this recommendation even in a case like
591
+ // `Foo<Bar<Baz<Qux, ()>>>`
592
+ if op . node == BinOpKind :: Lt && outer_op. node == AssocOp :: Less
593
+ || outer_op . node == AssocOp :: Greater
560
594
{
561
- // Foo<Bar<Baz<Qux, ()>>>
562
595
if outer_op. node == AssocOp :: Less {
563
596
let snapshot = self . clone ( ) ;
564
597
self . bump ( ) ;
@@ -572,7 +605,7 @@ impl<'a> Parser<'a> {
572
605
{
573
606
// We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
574
607
// parser and bail out.
575
- mem :: replace ( self , snapshot. clone ( ) ) ;
608
+ * self = snapshot. clone ( ) ;
576
609
}
577
610
}
578
611
return if token:: ModSep == self . token . kind {
@@ -597,7 +630,7 @@ impl<'a> Parser<'a> {
597
630
expr_err. cancel ( ) ;
598
631
// Not entirely sure now, but we bubble the error up with the
599
632
// suggestion.
600
- mem :: replace ( self , snapshot) ;
633
+ * self = snapshot;
601
634
Err ( err)
602
635
}
603
636
}
@@ -617,15 +650,33 @@ impl<'a> Parser<'a> {
617
650
}
618
651
}
619
652
} else {
620
- // All we know is that this is `foo < bar >` and *nothing* else. Try to
621
- // be helpful, but don't attempt to recover.
622
- err. help ( TURBOFISH ) ;
623
- err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
624
- // These cases cause too many knock-down errors, bail out (#61329).
625
- Err ( err)
653
+ if !matches ! ( l1. kind, ExprKind :: Lit ( _) )
654
+ && !matches ! ( r1. kind, ExprKind :: Lit ( _) )
655
+ {
656
+ // All we know is that this is `foo < bar >` and *nothing* else. Try to
657
+ // be helpful, but don't attempt to recover.
658
+ err. help ( TURBOFISH ) ;
659
+ err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
660
+ }
661
+
662
+ // If it looks like a genuine attempt to chain operators (as opposed to a
663
+ // misformatted turbofish, for instance), suggest a correct form.
664
+ if self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op)
665
+ {
666
+ err. emit ( ) ;
667
+ mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) )
668
+ } else {
669
+ // These cases cause too many knock-down errors, bail out (#61329).
670
+ Err ( err)
671
+ }
626
672
} ;
627
673
}
674
+ let recover =
675
+ self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op) ;
628
676
err. emit ( ) ;
677
+ if recover {
678
+ return mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) ) ;
679
+ }
629
680
}
630
681
_ => { }
631
682
}
@@ -643,7 +694,7 @@ impl<'a> Parser<'a> {
643
694
644
695
if self . token . kind == token:: Eof {
645
696
// Not entirely sure that what we consumed were fn arguments, rollback.
646
- mem :: replace ( self , snapshot) ;
697
+ * self = snapshot;
647
698
Err ( ( ) )
648
699
} else {
649
700
// 99% certain that the suggestion is correct, continue parsing.
0 commit comments