@@ -2757,6 +2757,8 @@ impl<'a> Parser<'a> {
27572757 // Assuming we have just parsed `.`, continue parsing into an expression.
27582758 fn parse_dot_suffix ( & mut self , self_arg : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
27592759 let segment = self . parse_path_segment ( PathStyle :: Expr , true ) ?;
2760+ self . check_trailing_angle_brackets ( & segment) ;
2761+
27602762 Ok ( match self . token {
27612763 token:: OpenDelim ( token:: Paren ) => {
27622764 // Method call `expr.f()`
@@ -2784,6 +2786,101 @@ impl<'a> Parser<'a> {
27842786 } )
27852787 }
27862788
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+
27872884 fn parse_dot_or_call_expr_with_ ( & mut self , e0 : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
27882885 let mut e = e0;
27892886 let mut hi;
0 commit comments