@@ -459,9 +459,18 @@ impl<'a> Parser<'a> {
459459 err : & mut DiagnosticBuilder < ' _ > ,
460460 inner_op : & Expr ,
461461 outer_op : & Spanned < AssocOp > ,
462- ) {
462+ ) -> bool /* recover */ {
463463 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 ) |
465474 // `x < y < z` and friends.
466475 ( BinOpKind :: Lt , AssocOp :: Less ) | ( BinOpKind :: Lt , AssocOp :: LessEqual ) |
467476 ( BinOpKind :: Le , AssocOp :: LessEqual ) | ( BinOpKind :: Le , AssocOp :: Less ) |
@@ -472,35 +481,65 @@ impl<'a> Parser<'a> {
472481 self . span_to_snippet ( e. span )
473482 . unwrap_or_else ( |_| pprust:: expr_to_string ( & e) )
474483 } ;
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) ) ,
486488 Applicability :: MaybeIncorrect ,
487489 ) ;
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+ ] ,
498526 Applicability :: MaybeIncorrect ,
499527 ) ;
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+ }
500538 }
501- _ => { }
502- }
539+ _ => false ,
540+ } ;
503541 }
542+ false
504543 }
505544
506545 /// Produces an error if comparison operators are chained (RFC #558).
@@ -534,31 +573,26 @@ impl<'a> Parser<'a> {
534573 |this : & Self , span| Ok ( Some ( this. mk_expr ( span, ExprKind :: Err , AttrVec :: new ( ) ) ) ) ;
535574
536575 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+ ) ;
546581
547582 let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
548583 err. span_suggestion_verbose (
549- op_span . shrink_to_lo ( ) ,
584+ op . span . shrink_to_lo ( ) ,
550585 TURBOFISH ,
551586 "::" . to_string ( ) ,
552587 Applicability :: MaybeIncorrect ,
553588 ) ;
554589 } ;
555590
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
560593 {
561- // Foo<Bar<Baz<Qux, ()>>>
594+ // Include `<` to provide this recommendation
595+ // even in a case like `Foo<Bar<Baz<Qux, ()>>>`
562596 if outer_op. node == AssocOp :: Less {
563597 let snapshot = self . clone ( ) ;
564598 self . bump ( ) ;
@@ -617,15 +651,33 @@ impl<'a> Parser<'a> {
617651 }
618652 }
619653 } 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+ }
626673 } ;
627674 }
675+ let recover =
676+ self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op) ;
628677 err. emit ( ) ;
678+ if recover {
679+ return mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) ) ;
680+ }
629681 }
630682 _ => { }
631683 }
0 commit comments