@@ -17,6 +17,8 @@ use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
17
17
use log:: { debug, trace} ;
18
18
use std:: mem;
19
19
20
+ const TURBOFISH : & ' static str = "use the \" turbofish\" `::<...>` instead of `<...>` to specify \
21
+ type arguments";
20
22
/// Creates a placeholder argument.
21
23
crate fn dummy_arg ( ident : Ident ) -> Param {
22
24
let pat = P ( Pat {
@@ -544,10 +546,20 @@ impl<'a> Parser<'a> {
544
546
545
547
/// Produces an error if comparison operators are chained (RFC #558).
546
548
/// 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`) .
548
550
///
549
551
/// 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
551
563
crate fn check_no_chained_comparison (
552
564
& mut self ,
553
565
lhs : & Expr ,
@@ -560,30 +572,36 @@ impl<'a> Parser<'a> {
560
572
) ;
561
573
match lhs. kind {
562
574
ExprKind :: Binary ( op, _, _) if op. node . is_comparison ( ) => {
563
-
564
575
// Respan to include both operators.
565
576
let op_span = op. span . to ( self . prev_span ) ;
566
577
let mut err = self . struct_span_err (
567
578
op_span,
568
579
"chained comparison operators require parentheses" ,
569
580
) ;
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
+
570
591
if op. node == BinOpKind :: Lt &&
571
592
* outer_op == AssocOp :: Less || // Include `<` to provide this recommendation
572
593
* outer_op == AssocOp :: Greater // even in a case like the following:
573
594
{ // Foo<Bar<Baz<Qux, ()>>>
574
- let msg = "use `::<...>` instead of `<...>` if you meant to specify type \
575
- arguments";
576
595
if * outer_op == AssocOp :: Less {
577
596
let snapshot = self . clone ( ) ;
578
597
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 = [
581
600
( token:: Lt , 1 ) ,
582
601
( token:: Gt , -1 ) ,
583
602
( token:: BinOp ( token:: Shr ) , -2 ) ,
584
603
] ;
585
- let early_return = vec ! [ token:: Eof ] ;
586
- self . consume_tts ( 1 , & modifiers[ ..] , & early_return[ ..] ) ;
604
+ self . consume_tts ( 1 , & modifiers[ ..] , & [ ] ) ;
587
605
588
606
if !& [
589
607
token:: OpenDelim ( token:: Paren ) ,
@@ -597,16 +615,11 @@ impl<'a> Parser<'a> {
597
615
if token:: ModSep == self . token . kind {
598
616
// We have some certainty that this was a bad turbofish at this point.
599
617
// `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) ;
606
619
607
620
let snapshot = self . clone ( ) ;
608
-
609
621
self . bump ( ) ; // `::`
622
+
610
623
// Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
611
624
match self . parse_expr ( ) {
612
625
Ok ( _) => {
@@ -621,8 +634,8 @@ impl<'a> Parser<'a> {
621
634
ThinVec :: new ( ) ,
622
635
) ) ) ;
623
636
}
624
- Err ( mut err ) => {
625
- err . cancel ( ) ;
637
+ Err ( mut expr_err ) => {
638
+ expr_err . cancel ( ) ;
626
639
// Not entirely sure now, but we bubble the error up with the
627
640
// suggestion.
628
641
mem:: replace ( self , snapshot) ;
@@ -632,45 +645,39 @@ impl<'a> Parser<'a> {
632
645
} else if token:: OpenDelim ( token:: Paren ) == self . token . kind {
633
646
// We have high certainty that this was a bad turbofish at this point.
634
647
// `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) ;
641
649
642
650
let snapshot = self . clone ( ) ;
643
651
self . bump ( ) ; // `(`
644
652
645
653
// Consume the fn call arguments.
646
- let modifiers = vec ! [
654
+ let modifiers = [
647
655
( token:: OpenDelim ( token:: Paren ) , 1 ) ,
648
656
( token:: CloseDelim ( token:: Paren ) , -1 ) ,
649
657
] ;
650
- let early_return = vec ! [ token:: Eof ] ;
651
- self . consume_tts ( 1 , & modifiers[ ..] , & early_return[ ..] ) ;
658
+ self . consume_tts ( 1 , & modifiers[ ..] , & [ ] ) ;
652
659
653
- if self . token . kind == token:: Eof {
660
+ return if self . token . kind == token:: Eof {
654
661
// Not entirely sure now, but we bubble the error up with the
655
662
// suggestion.
656
663
mem:: replace ( self , snapshot) ;
657
- return Err ( err) ;
664
+ Err ( err)
658
665
} else {
659
666
// 99% certain that the suggestion is correct, continue parsing.
660
667
err. emit ( ) ;
661
668
// FIXME: actually check that the two expressions in the binop are
662
669
// paths and resynthesize new fn call expression instead of using
663
670
// `ExprKind::Err` placeholder.
664
- return Ok ( Some ( self . mk_expr (
671
+ Ok ( Some ( self . mk_expr (
665
672
lhs. span . to ( self . prev_span ) ,
666
673
ExprKind :: Err ,
667
674
ThinVec :: new ( ) ,
668
- ) ) ) ;
675
+ ) ) )
669
676
}
670
677
} else {
671
678
// All we know is that this is `foo < bar >` and *nothing* else. Try to
672
679
// be helpful, but don't attempt to recover.
673
- err. help ( msg ) ;
680
+ err. help ( TURBOFISH ) ;
674
681
err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
675
682
// These cases cause too many knock-down errors, bail out (#61329).
676
683
}
@@ -1459,15 +1466,15 @@ impl<'a> Parser<'a> {
1459
1466
1460
1467
fn consume_tts (
1461
1468
& 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
1464
1471
early_return : & [ token:: TokenKind ] , // `token::TokenKind: !Eq + !Hash`.
1465
1472
) {
1466
1473
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 ) {
1468
1475
acc += * val;
1469
1476
}
1470
- if early_return. contains ( & self . token . kind ) {
1477
+ if self . token . kind == token :: Eof || early_return. contains ( & self . token . kind ) {
1471
1478
break ;
1472
1479
}
1473
1480
self . bump ( ) ;
0 commit comments