@@ -17,6 +17,8 @@ use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
1717use log:: { debug, trace} ;
1818use std:: mem;
1919
20+ const TURBOFISH : & ' static str = "use the \" turbofish\" `::<...>` instead of `<...>` to specify \
21+ type arguments";
2022/// Creates a placeholder argument.
2123crate fn dummy_arg ( ident : Ident ) -> Param {
2224 let pat = P ( Pat {
@@ -544,10 +546,20 @@ impl<'a> Parser<'a> {
544546
545547 /// Produces an error if comparison operators are chained (RFC #558).
546548 /// We only need to check the LHS, not the RHS, because all comparison ops have same
547- /// precedence and are left-associative.
549+ /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`) .
548550 ///
549551 /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
550- /// the turbofish syntax. We attempt some heuristic recovery if that is the case.
552+ /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
553+ /// case.
554+ ///
555+ /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
556+ /// associative we can infer that we have:
557+ ///
558+ /// outer_op
559+ /// / \
560+ /// inner_op r2
561+ /// / \
562+ /// l1 r1
551563 crate fn check_no_chained_comparison (
552564 & mut self ,
553565 lhs : & Expr ,
@@ -560,30 +572,36 @@ impl<'a> Parser<'a> {
560572 ) ;
561573 match lhs. kind {
562574 ExprKind :: Binary ( op, _, _) if op. node . is_comparison ( ) => {
563-
564575 // Respan to include both operators.
565576 let op_span = op. span . to ( self . prev_span ) ;
566577 let mut err = self . struct_span_err (
567578 op_span,
568579 "chained comparison operators require parentheses" ,
569580 ) ;
581+
582+ let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
583+ err. span_suggestion (
584+ op_span. shrink_to_lo ( ) ,
585+ TURBOFISH ,
586+ "::" . to_string ( ) ,
587+ Applicability :: MaybeIncorrect ,
588+ ) ;
589+ } ;
590+
570591 if op. node == BinOpKind :: Lt &&
571592 * outer_op == AssocOp :: Less || // Include `<` to provide this recommendation
572593 * outer_op == AssocOp :: Greater // even in a case like the following:
573594 { // Foo<Bar<Baz<Qux, ()>>>
574- let msg = "use `::<...>` instead of `<...>` if you meant to specify type \
575- arguments";
576595 if * outer_op == AssocOp :: Less {
577596 let snapshot = self . clone ( ) ;
578597 self . bump ( ) ;
579- // So far we have parsed `foo<bar<`, consume the rest of the type params
580- let modifiers = vec ! [
598+ // So far we have parsed `foo<bar<`, consume the rest of the type args.
599+ let modifiers = [
581600 ( token:: Lt , 1 ) ,
582601 ( token:: Gt , -1 ) ,
583602 ( token:: BinOp ( token:: Shr ) , -2 ) ,
584603 ] ;
585- let early_return = vec ! [ token:: Eof ] ;
586- self . consume_tts ( 1 , & modifiers[ ..] , & early_return[ ..] ) ;
604+ self . consume_tts ( 1 , & modifiers[ ..] , & [ ] ) ;
587605
588606 if !& [
589607 token:: OpenDelim ( token:: Paren ) ,
@@ -597,16 +615,11 @@ impl<'a> Parser<'a> {
597615 if token:: ModSep == self . token . kind {
598616 // We have some certainty that this was a bad turbofish at this point.
599617 // `foo< bar >::`
600- err. span_suggestion (
601- op_span. shrink_to_lo ( ) ,
602- msg,
603- "::" . to_string ( ) ,
604- Applicability :: MaybeIncorrect ,
605- ) ;
618+ suggest ( & mut err) ;
606619
607620 let snapshot = self . clone ( ) ;
608-
609621 self . bump ( ) ; // `::`
622+
610623 // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
611624 match self . parse_expr ( ) {
612625 Ok ( _) => {
@@ -621,8 +634,8 @@ impl<'a> Parser<'a> {
621634 ThinVec :: new ( ) ,
622635 ) ) ) ;
623636 }
624- Err ( mut err ) => {
625- err . cancel ( ) ;
637+ Err ( mut expr_err ) => {
638+ expr_err . cancel ( ) ;
626639 // Not entirely sure now, but we bubble the error up with the
627640 // suggestion.
628641 mem:: replace ( self , snapshot) ;
@@ -632,45 +645,39 @@ impl<'a> Parser<'a> {
632645 } else if token:: OpenDelim ( token:: Paren ) == self . token . kind {
633646 // We have high certainty that this was a bad turbofish at this point.
634647 // `foo< bar >(`
635- err. span_suggestion (
636- op_span. shrink_to_lo ( ) ,
637- msg,
638- "::" . to_string ( ) ,
639- Applicability :: MaybeIncorrect ,
640- ) ;
648+ suggest ( & mut err) ;
641649
642650 let snapshot = self . clone ( ) ;
643651 self . bump ( ) ; // `(`
644652
645653 // Consume the fn call arguments.
646- let modifiers = vec ! [
654+ let modifiers = [
647655 ( token:: OpenDelim ( token:: Paren ) , 1 ) ,
648656 ( token:: CloseDelim ( token:: Paren ) , -1 ) ,
649657 ] ;
650- let early_return = vec ! [ token:: Eof ] ;
651- self . consume_tts ( 1 , & modifiers[ ..] , & early_return[ ..] ) ;
658+ self . consume_tts ( 1 , & modifiers[ ..] , & [ ] ) ;
652659
653- if self . token . kind == token:: Eof {
660+ return if self . token . kind == token:: Eof {
654661 // Not entirely sure now, but we bubble the error up with the
655662 // suggestion.
656663 mem:: replace ( self , snapshot) ;
657- return Err ( err) ;
664+ Err ( err)
658665 } else {
659666 // 99% certain that the suggestion is correct, continue parsing.
660667 err. emit ( ) ;
661668 // FIXME: actually check that the two expressions in the binop are
662669 // paths and resynthesize new fn call expression instead of using
663670 // `ExprKind::Err` placeholder.
664- return Ok ( Some ( self . mk_expr (
671+ Ok ( Some ( self . mk_expr (
665672 lhs. span . to ( self . prev_span ) ,
666673 ExprKind :: Err ,
667674 ThinVec :: new ( ) ,
668- ) ) ) ;
675+ ) ) )
669676 }
670677 } else {
671678 // All we know is that this is `foo < bar >` and *nothing* else. Try to
672679 // be helpful, but don't attempt to recover.
673- err. help ( msg ) ;
680+ err. help ( TURBOFISH ) ;
674681 err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
675682 // These cases cause too many knock-down errors, bail out (#61329).
676683 }
@@ -1459,15 +1466,15 @@ impl<'a> Parser<'a> {
14591466
14601467 fn consume_tts (
14611468 & mut self ,
1462- mut acc : i64 ,
1463- modifier : & [ ( token:: TokenKind , i64 ) ] , // Not using FxHasMap and FxHashSet due to
1469+ mut acc : i64 , // `i64` because malformed code can have more closing delims than opening.
1470+ modifier : & [ ( token:: TokenKind , i64 ) ] , // Not using `FxHashMap` and ` FxHashSet` due to
14641471 early_return : & [ token:: TokenKind ] , // `token::TokenKind: !Eq + !Hash`.
14651472 ) {
14661473 while acc > 0 {
1467- if let Some ( ( _, val) ) = modifier. iter ( ) . filter ( |( t, _) | * t == self . token . kind ) . next ( ) {
1474+ if let Some ( ( _, val) ) = modifier. iter ( ) . find ( |( t, _) | * t == self . token . kind ) {
14681475 acc += * val;
14691476 }
1470- if early_return. contains ( & self . token . kind ) {
1477+ if self . token . kind == token :: Eof || early_return. contains ( & self . token . kind ) {
14711478 break ;
14721479 }
14731480 self . bump ( ) ;
0 commit comments