@@ -17,7 +17,6 @@ use rustc_span::symbol::kw;
1717use rustc_span:: { MultiSpan , Span , SpanSnippetError , DUMMY_SP } ;
1818
1919use log:: { debug, trace} ;
20- use std:: mem;
2120
2221const TURBOFISH : & str = "use `::<...>` instead of `<...>` to specify type arguments" ;
2322
@@ -459,9 +458,28 @@ impl<'a> Parser<'a> {
459458 err : & mut DiagnosticBuilder < ' _ > ,
460459 inner_op : & Expr ,
461460 outer_op : & Spanned < AssocOp > ,
462- ) {
461+ ) -> bool /* advanced the cursor */ {
463462 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 ) |
465483 // `x < y < z` and friends.
466484 ( BinOpKind :: Lt , AssocOp :: Less ) | ( BinOpKind :: Lt , AssocOp :: LessEqual ) |
467485 ( BinOpKind :: Le , AssocOp :: LessEqual ) | ( BinOpKind :: Le , AssocOp :: Less ) |
@@ -472,35 +490,55 @@ impl<'a> Parser<'a> {
472490 self . span_to_snippet ( e. span )
473491 . unwrap_or_else ( |_| pprust:: expr_to_string ( & e) )
474492 } ;
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) ) ,
498497 Applicability :: MaybeIncorrect ,
499498 ) ;
499+ false // Keep the current parse behavior, where the AST is `(x < y) < z`.
500500 }
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+ } ;
503540 }
541+ false
504542 }
505543
506544 /// Produces an error if comparison operators are chained (RFC #558).
@@ -534,31 +572,26 @@ impl<'a> Parser<'a> {
534572 |this : & Self , span| Ok ( Some ( this. mk_expr ( span, ExprKind :: Err , AttrVec :: new ( ) ) ) ) ;
535573
536574 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+ ) ;
546580
547581 let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
548582 err. span_suggestion_verbose (
549- op_span . shrink_to_lo ( ) ,
583+ op . span . shrink_to_lo ( ) ,
550584 TURBOFISH ,
551585 "::" . to_string ( ) ,
552586 Applicability :: MaybeIncorrect ,
553587 ) ;
554588 } ;
555589
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
560594 {
561- // Foo<Bar<Baz<Qux, ()>>>
562595 if outer_op. node == AssocOp :: Less {
563596 let snapshot = self . clone ( ) ;
564597 self . bump ( ) ;
@@ -572,7 +605,7 @@ impl<'a> Parser<'a> {
572605 {
573606 // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
574607 // parser and bail out.
575- mem :: replace ( self , snapshot. clone ( ) ) ;
608+ * self = snapshot. clone ( ) ;
576609 }
577610 }
578611 return if token:: ModSep == self . token . kind {
@@ -597,7 +630,7 @@ impl<'a> Parser<'a> {
597630 expr_err. cancel ( ) ;
598631 // Not entirely sure now, but we bubble the error up with the
599632 // suggestion.
600- mem :: replace ( self , snapshot) ;
633+ * self = snapshot;
601634 Err ( err)
602635 }
603636 }
@@ -617,15 +650,33 @@ impl<'a> Parser<'a> {
617650 }
618651 }
619652 } 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+ }
626672 } ;
627673 }
674+ let recover =
675+ self . attempt_chained_comparison_suggestion ( & mut err, inner_op, outer_op) ;
628676 err. emit ( ) ;
677+ if recover {
678+ return mk_err_expr ( self , inner_op. span . to ( self . prev_token . span ) ) ;
679+ }
629680 }
630681 _ => { }
631682 }
@@ -643,7 +694,7 @@ impl<'a> Parser<'a> {
643694
644695 if self . token . kind == token:: Eof {
645696 // Not entirely sure that what we consumed were fn arguments, rollback.
646- mem :: replace ( self , snapshot) ;
697+ * self = snapshot;
647698 Err ( ( ) )
648699 } else {
649700 // 99% certain that the suggestion is correct, continue parsing.
0 commit comments