@@ -2757,6 +2757,8 @@ impl<'a> Parser<'a> {
2757
2757
// Assuming we have just parsed `.`, continue parsing into an expression.
2758
2758
fn parse_dot_suffix ( & mut self , self_arg : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
2759
2759
let segment = self . parse_path_segment ( PathStyle :: Expr , true ) ?;
2760
+ self . check_trailing_angle_brackets ( & segment) ;
2761
+
2760
2762
Ok ( match self . token {
2761
2763
token:: OpenDelim ( token:: Paren ) => {
2762
2764
// Method call `expr.f()`
@@ -2784,6 +2786,101 @@ impl<'a> Parser<'a> {
2784
2786
} )
2785
2787
}
2786
2788
2789
+ /// This function checks if there are trailing angle brackets and produces
2790
+ /// a diagnostic to suggest removing them.
2791
+ ///
2792
+ /// ```ignore (diagnostic)
2793
+ /// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
2794
+ /// ^^ help: remove extra angle brackets
2795
+ /// ```
2796
+ fn check_trailing_angle_brackets ( & mut self , segment : & PathSegment ) {
2797
+ // This function is intended to be invoked from `parse_dot_suffix` where there are two
2798
+ // cases:
2799
+ //
2800
+ // - A field access (eg. `x.foo`)
2801
+ // - A method call (eg. `x.foo()`)
2802
+ //
2803
+ // This function is called after parsing `.foo` and before parsing any parenthesis (if
2804
+ // present). This includes any angle bracket arguments, such as `.foo::<u32>`.
2805
+
2806
+ // We only care about trailing angle brackets if we previously parsed angle bracket
2807
+ // arguments. This helps stop us incorrectly suggesting that extra angle brackets be
2808
+ // removed in this case:
2809
+ //
2810
+ // `x.foo >> (3)` (where `x.foo` is a `u32` for example)
2811
+ //
2812
+ // This case is particularly tricky as we won't notice it just looking at the tokens -
2813
+ // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
2814
+ // have already been parsed):
2815
+ //
2816
+ // `x.foo::<u32>>>(3)`
2817
+ let parsed_angle_bracket_args = segment. args
2818
+ . as_ref ( )
2819
+ . map ( |args| args. is_angle_bracketed ( ) )
2820
+ . unwrap_or ( false ) ;
2821
+
2822
+ debug ! (
2823
+ "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}" ,
2824
+ parsed_angle_bracket_args,
2825
+ ) ;
2826
+ if !parsed_angle_bracket_args {
2827
+ return ;
2828
+ }
2829
+
2830
+ // Keep the span at the start so we can highlight the sequence of `>` characters to be
2831
+ // removed.
2832
+ let lo = self . span ;
2833
+
2834
+ // We need to look-ahead to see if we have `>` characters without moving the cursor forward
2835
+ // (since we might have the field access case and the characters we're eating are
2836
+ // actual operators and not trailing characters - ie `x.foo >> 3`).
2837
+ let mut position = 0 ;
2838
+
2839
+ // The first tokens we will encounter are shift right tokens (`>>`) since pairs of `>`
2840
+ // characters will have been grouped together by the tokenizer.
2841
+ let mut number_of_shr = 0 ;
2842
+ while self . look_ahead ( position, |t| * t == token:: BinOp ( token:: BinOpToken :: Shr ) ) {
2843
+ number_of_shr += 1 ;
2844
+ position += 1 ;
2845
+ }
2846
+
2847
+ // Afterwards, there will be at most one `>` character remaining (more than one and it'd
2848
+ // have shown up as a `>>`).
2849
+ let encountered_gt = self . look_ahead ( position, |t| * t == token:: Gt ) ;
2850
+ if encountered_gt {
2851
+ position += 1 ;
2852
+ }
2853
+
2854
+ // If we didn't find any trailing `>>` characters or a trailing `>`, then we have
2855
+ // nothing to error about.
2856
+ debug ! (
2857
+ "check_trailing_angle_brackets: encountered_gt={:?} number_of_shr={:?}" ,
2858
+ encountered_gt, number_of_shr,
2859
+ ) ;
2860
+ if !encountered_gt && number_of_shr < 1 {
2861
+ return ;
2862
+ }
2863
+
2864
+ // Finally, double check that we have a left parenthesis next as otherwise this is the
2865
+ // field case.
2866
+ if self . look_ahead ( position, |t| * t == token:: OpenDelim ( token:: Paren ) ) {
2867
+ // Eat from where we started until the left parenthesis so that parsing can continue
2868
+ // as if we didn't have those extra angle brackets.
2869
+ self . eat_to_tokens ( & [ & token:: OpenDelim ( token:: Paren ) ] ) ;
2870
+ let span = lo. until ( self . span ) ;
2871
+
2872
+ self . diagnostic ( )
2873
+ . struct_span_err ( span, "unmatched angle bracket" )
2874
+ . span_suggestion_with_applicability (
2875
+ span,
2876
+ "remove extra angle bracket" ,
2877
+ String :: new ( ) ,
2878
+ Applicability :: MachineApplicable ,
2879
+ )
2880
+ . emit ( ) ;
2881
+ }
2882
+ }
2883
+
2787
2884
fn parse_dot_or_call_expr_with_ ( & mut self , e0 : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
2788
2885
let mut e = e0;
2789
2886
let mut hi;
0 commit comments