@@ -17,6 +17,7 @@ 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 `::<...>` instead of `<...>` to specify type arguments" ;
20
21
/// Creates a placeholder argument.
21
22
crate fn dummy_arg ( ident : Ident ) -> Param {
22
23
let pat = P ( Pat {
@@ -543,35 +544,154 @@ impl<'a> Parser<'a> {
543
544
}
544
545
545
546
/// Produces an error if comparison operators are chained (RFC #558).
546
- /// We only need to check the LHS, not the RHS, because all comparison ops
547
- /// have same precedence and are left-associative.
548
- crate fn check_no_chained_comparison ( & self , lhs : & Expr , outer_op : & AssocOp ) -> PResult < ' a , ( ) > {
549
- debug_assert ! ( outer_op. is_comparison( ) ,
550
- "check_no_chained_comparison: {:?} is not comparison" ,
551
- outer_op) ;
547
+ /// We only need to check the LHS, not the RHS, because all comparison ops have same
548
+ /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
549
+ ///
550
+ /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
551
+ /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
552
+ /// case.
553
+ ///
554
+ /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
555
+ /// associative we can infer that we have:
556
+ ///
557
+ /// outer_op
558
+ /// / \
559
+ /// inner_op r2
560
+ /// / \
561
+ /// l1 r1
562
+ crate fn check_no_chained_comparison (
563
+ & mut self ,
564
+ lhs : & Expr ,
565
+ outer_op : & AssocOp ,
566
+ ) -> PResult < ' a , Option < P < Expr > > > {
567
+ debug_assert ! (
568
+ outer_op. is_comparison( ) ,
569
+ "check_no_chained_comparison: {:?} is not comparison" ,
570
+ outer_op,
571
+ ) ;
572
+
573
+ let mk_err_expr = |this : & Self , span| {
574
+ Ok ( Some ( this. mk_expr ( span, ExprKind :: Err , ThinVec :: new ( ) ) ) )
575
+ } ;
576
+
552
577
match lhs. kind {
553
578
ExprKind :: Binary ( op, _, _) if op. node . is_comparison ( ) => {
554
579
// Respan to include both operators.
555
- let op_span = op. span . to ( self . token . span ) ;
580
+ let op_span = op. span . to ( self . prev_span ) ;
556
581
let mut err = self . struct_span_err (
557
582
op_span,
558
583
"chained comparison operators require parentheses" ,
559
584
) ;
585
+
586
+ let suggest = |err : & mut DiagnosticBuilder < ' _ > | {
587
+ err. span_suggestion_verbose (
588
+ op_span. shrink_to_lo ( ) ,
589
+ TURBOFISH ,
590
+ "::" . to_string ( ) ,
591
+ Applicability :: MaybeIncorrect ,
592
+ ) ;
593
+ } ;
594
+
560
595
if op. node == BinOpKind :: Lt &&
561
596
* outer_op == AssocOp :: Less || // Include `<` to provide this recommendation
562
597
* outer_op == AssocOp :: Greater // even in a case like the following:
563
598
{ // Foo<Bar<Baz<Qux, ()>>>
564
- err. help (
565
- "use `::<...>` instead of `<...>` if you meant to specify type arguments" ) ;
566
- err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
567
- // These cases cause too many knock-down errors, bail out (#61329).
568
- return Err ( err) ;
599
+ if * outer_op == AssocOp :: Less {
600
+ let snapshot = self . clone ( ) ;
601
+ self . bump ( ) ;
602
+ // So far we have parsed `foo<bar<`, consume the rest of the type args.
603
+ let modifiers = [
604
+ ( token:: Lt , 1 ) ,
605
+ ( token:: Gt , -1 ) ,
606
+ ( token:: BinOp ( token:: Shr ) , -2 ) ,
607
+ ] ;
608
+ self . consume_tts ( 1 , & modifiers[ ..] ) ;
609
+
610
+ if !& [
611
+ token:: OpenDelim ( token:: Paren ) ,
612
+ token:: ModSep ,
613
+ ] . contains ( & self . token . kind ) {
614
+ // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
615
+ // parser and bail out.
616
+ mem:: replace ( self , snapshot. clone ( ) ) ;
617
+ }
618
+ }
619
+ return if token:: ModSep == self . token . kind {
620
+ // We have some certainty that this was a bad turbofish at this point.
621
+ // `foo< bar >::`
622
+ suggest ( & mut err) ;
623
+
624
+ let snapshot = self . clone ( ) ;
625
+ self . bump ( ) ; // `::`
626
+
627
+ // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
628
+ match self . parse_expr ( ) {
629
+ Ok ( _) => {
630
+ // 99% certain that the suggestion is correct, continue parsing.
631
+ err. emit ( ) ;
632
+ // FIXME: actually check that the two expressions in the binop are
633
+ // paths and resynthesize new fn call expression instead of using
634
+ // `ExprKind::Err` placeholder.
635
+ mk_err_expr ( self , lhs. span . to ( self . prev_span ) )
636
+ }
637
+ Err ( mut expr_err) => {
638
+ expr_err. cancel ( ) ;
639
+ // Not entirely sure now, but we bubble the error up with the
640
+ // suggestion.
641
+ mem:: replace ( self , snapshot) ;
642
+ Err ( err)
643
+ }
644
+ }
645
+ } else if token:: OpenDelim ( token:: Paren ) == self . token . kind {
646
+ // We have high certainty that this was a bad turbofish at this point.
647
+ // `foo< bar >(`
648
+ suggest ( & mut err) ;
649
+ // Consume the fn call arguments.
650
+ match self . consume_fn_args ( ) {
651
+ Err ( ( ) ) => Err ( err) ,
652
+ Ok ( ( ) ) => {
653
+ err. emit ( ) ;
654
+ // FIXME: actually check that the two expressions in the binop are
655
+ // paths and resynthesize new fn call expression instead of using
656
+ // `ExprKind::Err` placeholder.
657
+ mk_err_expr ( self , lhs. span . to ( self . prev_span ) )
658
+ }
659
+ }
660
+ } else {
661
+ // All we know is that this is `foo < bar >` and *nothing* else. Try to
662
+ // be helpful, but don't attempt to recover.
663
+ err. help ( TURBOFISH ) ;
664
+ err. help ( "or use `(...)` if you meant to specify fn arguments" ) ;
665
+ // These cases cause too many knock-down errors, bail out (#61329).
666
+ Err ( err)
667
+ } ;
569
668
}
570
669
err. emit ( ) ;
571
670
}
572
671
_ => { }
573
672
}
574
- Ok ( ( ) )
673
+ Ok ( None )
674
+ }
675
+
676
+ fn consume_fn_args ( & mut self ) -> Result < ( ) , ( ) > {
677
+ let snapshot = self . clone ( ) ;
678
+ self . bump ( ) ; // `(`
679
+
680
+ // Consume the fn call arguments.
681
+ let modifiers = [
682
+ ( token:: OpenDelim ( token:: Paren ) , 1 ) ,
683
+ ( token:: CloseDelim ( token:: Paren ) , -1 ) ,
684
+ ] ;
685
+ self . consume_tts ( 1 , & modifiers[ ..] ) ;
686
+
687
+ if self . token . kind == token:: Eof {
688
+ // Not entirely sure that what we consumed were fn arguments, rollback.
689
+ mem:: replace ( self , snapshot) ;
690
+ Err ( ( ) )
691
+ } else {
692
+ // 99% certain that the suggestion is correct, continue parsing.
693
+ Ok ( ( ) )
694
+ }
575
695
}
576
696
577
697
crate fn maybe_report_ambiguous_plus (
@@ -1364,6 +1484,23 @@ impl<'a> Parser<'a> {
1364
1484
err
1365
1485
}
1366
1486
1487
+ fn consume_tts (
1488
+ & mut self ,
1489
+ mut acc : i64 , // `i64` because malformed code can have more closing delims than opening.
1490
+ // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`.
1491
+ modifier : & [ ( token:: TokenKind , i64 ) ] ,
1492
+ ) {
1493
+ while acc > 0 {
1494
+ if let Some ( ( _, val) ) = modifier. iter ( ) . find ( |( t, _) | * t == self . token . kind ) {
1495
+ acc += * val;
1496
+ }
1497
+ if self . token . kind == token:: Eof {
1498
+ break ;
1499
+ }
1500
+ self . bump ( ) ;
1501
+ }
1502
+ }
1503
+
1367
1504
/// Replace duplicated recovered parameters with `_` pattern to avoid unecessary errors.
1368
1505
///
1369
1506
/// This is necessary because at this point we don't know whether we parsed a function with
0 commit comments