@@ -459,9 +459,18 @@ impl<'a> Parser<'a> {
459
459
err : & mut DiagnosticBuilder < ' _ > ,
460
460
inner_op : & Expr ,
461
461
outer_op : & Spanned < AssocOp > ,
462
- ) {
462
+ ) -> bool /* recover */ {
463
463
if let ExprKind :: Binary ( op, ref l1, ref r1) = inner_op. kind {
464
- match ( op. node , & outer_op. node ) {
464
+ if let ExprKind :: Field ( _, ident) = l1. kind {
465
+ if ident. as_str ( ) . parse :: < i32 > ( ) . is_err ( ) && !matches ! ( r1. kind, ExprKind :: Lit ( _) ) {
466
+ // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
467
+ // suggestion being the only one to apply is high.
468
+ return false ;
469
+ }
470
+ }
471
+ return match ( op. node , & outer_op. node ) {
472
+ // `x == y == z`
473
+ ( BinOpKind :: Eq , AssocOp :: Equal ) |
465
474
// `x < y < z` and friends.
466
475
( BinOpKind :: Lt , AssocOp :: Less ) | ( BinOpKind :: Lt , AssocOp :: LessEqual ) |
467
476
( BinOpKind :: Le , AssocOp :: LessEqual ) | ( BinOpKind :: Le , AssocOp :: Less ) |
@@ -472,35 +481,65 @@ impl<'a> Parser<'a> {
472
481
self . span_to_snippet ( e. span )
473
482
. unwrap_or_else ( |_| pprust:: expr_to_string ( & e) )
474
483
} ;
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
- ) ,
484
+ err. span_suggestion_verbose (
485
+ inner_op. span . shrink_to_hi ( ) ,
486
+ "split the comparison into two" ,
487
+ format ! ( " && {}" , expr_to_str( & r1) ) ,
486
488
Applicability :: MaybeIncorrect ,
487
489
) ;
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
- ) ,
490
+ false // Keep the current parse behavior, where the AST is `(x < y) < z`.
491
+ }
492
+ // `x == y < z`
493
+ ( BinOpKind :: Eq , AssocOp :: Less ) | ( BinOpKind :: Eq , AssocOp :: LessEqual ) |
494
+ ( BinOpKind :: Eq , AssocOp :: Greater ) | ( BinOpKind :: Eq , AssocOp :: GreaterEqual ) => {
495
+ // Consume `/`z`/outer-op-rhs.
496
+ let snapshot = self . clone ( ) ;
497
+ match self . parse_expr ( ) {
498
+ Ok ( r2) => {
499
+ err. multipart_suggestion (
500
+ "parenthesize the comparison" ,
501
+ vec ! [
502
+ ( r1. span. shrink_to_lo( ) , "(" . to_string( ) ) ,
503
+ ( r2. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
504
+ ] ,
505
+ Applicability :: MaybeIncorrect ,
506
+ ) ;
507
+ true
508
+ }
509
+ Err ( mut expr_err) => {
510
+ expr_err. cancel ( ) ;
511
+ mem:: replace ( self , snapshot) ;
512
+ false
513
+ }
514
+ }
515
+ }
516
+ // `x > y == z`
517
+ ( BinOpKind :: Lt , AssocOp :: Equal ) | ( BinOpKind :: Le , AssocOp :: Equal ) |
518
+ ( BinOpKind :: Gt , AssocOp :: Equal ) | ( BinOpKind :: Ge , AssocOp :: Equal ) => {
519
+ let snapshot = self . clone ( ) ;
520
+ err. multipart_suggestion (
521
+ "parenthesize the comparison" ,
522
+ vec ! [
523
+ ( l1. span. shrink_to_lo( ) , "(" . to_string( ) ) ,
524
+ ( r1. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
525
+ ] ,
498
526
Applicability :: MaybeIncorrect ,
499
527
) ;
528
+ match self . parse_expr ( ) {
529
+ Ok ( _) => {
530
+ true
531
+ }
532
+ Err ( mut expr_err) => {
533
+ expr_err. cancel ( ) ;
534
+ mem:: replace ( self , snapshot) ;
535
+ false
536
+ }
537
+ }
500
538
}
501
- _ => { }
502
- }
539
+ _ => false ,
540
+ } ;
503
541
}
542
+ false
504
543
}
505
544
506
545
/// Produces an error if comparison operators are chained (RFC #558).
@@ -534,31 +573,26 @@ impl<'a> Parser<'a> {
534
573
|this : & Self , span| Ok ( Some ( this. mk_expr ( span, ExprKind :: Err , AttrVec :: new ( ) ) ) ) ;
535
574
536
575
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) ;
576
+ ExprKind :: Binary ( op, ref l1, ref r1) if op. node . is_comparison ( ) => {
577
+ let mut err = self . struct_span_err (
578
+ vec ! [ op. span, self . prev_token. span] ,
579
+ "comparison operators cannot be chained" ,
580
+ ) ;
546
581
547
582
let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
548
583
err. span_suggestion_verbose (
549
- op_span . shrink_to_lo ( ) ,
584
+ op . span . shrink_to_lo ( ) ,
550
585
TURBOFISH ,
551
586
"::" . to_string ( ) ,
552
587
Applicability :: MaybeIncorrect ,
553
588
) ;
554
589
} ;
555
590
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:
591
+ if op. node == BinOpKind :: Lt && outer_op. node == AssocOp :: Less
592
+ || outer_op. node == AssocOp :: Greater
560
593
{
561
- // Foo<Bar<Baz<Qux, ()>>>
594
+ // Include `<` to provide this recommendation
595
+ // even in a case like `Foo<Bar<Baz<Qux, ()>>>`
562
596
if outer_op. node == AssocOp :: Less {
563
597
let snapshot = self . clone ( ) ;
564
598
self . bump ( ) ;
@@ -617,15 +651,33 @@ impl<'a> Parser<'a> {
617
651
}
618
652
}
619
653
} 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)
654
+ if !matches ! ( l1. kind, ExprKind :: Lit ( _) )
655
+ && !matches ! ( r1. kind, ExprKind :: Lit ( _) )
656
+ {
657
+ // All we know is that this is `foo < bar >` and *nothing* else. Try to
658
+ // be helpful, but don't attempt to recover.
659
+ err. help ( TURBOFISH ) ;
660
+ err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
661
+ }
662
+
663
+ // If it looks like a genuine attempt to chain operators (as opposed to a
664
+ // misformatted turbofish, for instance), suggest a correct form.
665
+ if self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op)
666
+ {
667
+ err. emit ( ) ;
668
+ mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) )
669
+ } else {
670
+ // These cases cause too many knock-down errors, bail out (#61329).
671
+ Err ( err)
672
+ }
626
673
} ;
627
674
}
675
+ let recover =
676
+ self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op) ;
628
677
err. emit ( ) ;
678
+ if recover {
679
+ return mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) ) ;
680
+ }
629
681
}
630
682
_ => { }
631
683
}
0 commit comments