@@ -2188,7 +2188,27 @@ impl<'a> Parser<'a> {
2188
2188
enable_warning : bool )
2189
2189
-> PResult < ' a , ( ) > {
2190
2190
loop {
2191
- segments. push ( self . parse_path_segment ( style, enable_warning) ?) ;
2191
+ let segment = self . parse_path_segment ( style, enable_warning) ?;
2192
+ if style == PathStyle :: Expr {
2193
+ // In order to check for trailing angle brackets, we must have finished
2194
+ // recursing (`parse_path_segment` can indirectly call this function),
2195
+ // that is, the next token must be the highlighted part of the below example:
2196
+ //
2197
+ // `Foo::<Bar as Baz<T>>::Qux`
2198
+ // ^ here
2199
+ //
2200
+ // As opposed to the below highlight (if we had only finished the first
2201
+ // recursion):
2202
+ //
2203
+ // `Foo::<Bar as Baz<T>>::Qux`
2204
+ // ^ here
2205
+ //
2206
+ // `PathStyle::Expr` is only provided at the root invocation and never in
2207
+ // `parse_path_segment` to recurse and therefore can be checked to maintain
2208
+ // this invariant.
2209
+ self . check_trailing_angle_brackets ( & segment, token:: ModSep ) ;
2210
+ }
2211
+ segments. push ( segment) ;
2192
2212
2193
2213
if self . is_import_coupler ( ) || !self . eat ( & token:: ModSep ) {
2194
2214
return Ok ( ( ) ) ;
@@ -2821,6 +2841,8 @@ impl<'a> Parser<'a> {
2821
2841
// Assuming we have just parsed `.`, continue parsing into an expression.
2822
2842
fn parse_dot_suffix ( & mut self , self_arg : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
2823
2843
let segment = self . parse_path_segment ( PathStyle :: Expr , true ) ?;
2844
+ self . check_trailing_angle_brackets ( & segment, token:: OpenDelim ( token:: Paren ) ) ;
2845
+
2824
2846
Ok ( match self . token {
2825
2847
token:: OpenDelim ( token:: Paren ) => {
2826
2848
// Method call `expr.f()`
@@ -2848,6 +2870,116 @@ impl<'a> Parser<'a> {
2848
2870
} )
2849
2871
}
2850
2872
2873
+ /// This function checks if there are trailing angle brackets and produces
2874
+ /// a diagnostic to suggest removing them.
2875
+ ///
2876
+ /// ```ignore (diagnostic)
2877
+ /// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
2878
+ /// ^^ help: remove extra angle brackets
2879
+ /// ```
2880
+ fn check_trailing_angle_brackets ( & mut self , segment : & PathSegment , end : token:: Token ) {
2881
+ // This function is intended to be invoked after parsing a path segment where there are two
2882
+ // cases:
2883
+ //
2884
+ // 1. A specific token is expected after the path segment.
2885
+ // eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),
2886
+ // `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).
2887
+ // 2. No specific token is expected after the path segment.
2888
+ // eg. `x.foo` (field access)
2889
+ //
2890
+ // This function is called after parsing `.foo` and before parsing the token `end` (if
2891
+ // present). This includes any angle bracket arguments, such as `.foo::<u32>` or
2892
+ // `Foo::<Bar>`.
2893
+
2894
+ // We only care about trailing angle brackets if we previously parsed angle bracket
2895
+ // arguments. This helps stop us incorrectly suggesting that extra angle brackets be
2896
+ // removed in this case:
2897
+ //
2898
+ // `x.foo >> (3)` (where `x.foo` is a `u32` for example)
2899
+ //
2900
+ // This case is particularly tricky as we won't notice it just looking at the tokens -
2901
+ // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
2902
+ // have already been parsed):
2903
+ //
2904
+ // `x.foo::<u32>>>(3)`
2905
+ let parsed_angle_bracket_args = segment. args
2906
+ . as_ref ( )
2907
+ . map ( |args| args. is_angle_bracketed ( ) )
2908
+ . unwrap_or ( false ) ;
2909
+
2910
+ debug ! (
2911
+ "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}" ,
2912
+ parsed_angle_bracket_args,
2913
+ ) ;
2914
+ if !parsed_angle_bracket_args {
2915
+ return ;
2916
+ }
2917
+
2918
+ // Keep the span at the start so we can highlight the sequence of `>` characters to be
2919
+ // removed.
2920
+ let lo = self . span ;
2921
+
2922
+ // We need to look-ahead to see if we have `>` characters without moving the cursor forward
2923
+ // (since we might have the field access case and the characters we're eating are
2924
+ // actual operators and not trailing characters - ie `x.foo >> 3`).
2925
+ let mut position = 0 ;
2926
+
2927
+ // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how
2928
+ // many of each (so we can correctly pluralize our error messages) and continue to
2929
+ // advance.
2930
+ let mut number_of_shr = 0 ;
2931
+ let mut number_of_gt = 0 ;
2932
+ while self . look_ahead ( position, |t| {
2933
+ trace ! ( "check_trailing_angle_brackets: t={:?}" , t) ;
2934
+ if * t == token:: BinOp ( token:: BinOpToken :: Shr ) {
2935
+ number_of_shr += 1 ;
2936
+ true
2937
+ } else if * t == token:: Gt {
2938
+ number_of_gt += 1 ;
2939
+ true
2940
+ } else {
2941
+ false
2942
+ }
2943
+ } ) {
2944
+ position += 1 ;
2945
+ }
2946
+
2947
+ // If we didn't find any trailing `>` characters, then we have nothing to error about.
2948
+ debug ! (
2949
+ "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}" ,
2950
+ number_of_gt, number_of_shr,
2951
+ ) ;
2952
+ if number_of_gt < 1 && number_of_shr < 1 {
2953
+ return ;
2954
+ }
2955
+
2956
+ // Finally, double check that we have our end token as otherwise this is the
2957
+ // second case.
2958
+ if self . look_ahead ( position, |t| {
2959
+ trace ! ( "check_trailing_angle_brackets: t={:?}" , t) ;
2960
+ * t == end
2961
+ } ) {
2962
+ // Eat from where we started until the end token so that parsing can continue
2963
+ // as if we didn't have those extra angle brackets.
2964
+ self . eat_to_tokens ( & [ & end] ) ;
2965
+ let span = lo. until ( self . span ) ;
2966
+
2967
+ let plural = number_of_gt > 1 || number_of_shr >= 1 ;
2968
+ self . diagnostic ( )
2969
+ . struct_span_err (
2970
+ span,
2971
+ & format ! ( "unmatched angle bracket{}" , if plural { "s" } else { "" } ) ,
2972
+ )
2973
+ . span_suggestion_with_applicability (
2974
+ span,
2975
+ & format ! ( "remove extra angle bracket{}" , if plural { "s" } else { "" } ) ,
2976
+ String :: new ( ) ,
2977
+ Applicability :: MachineApplicable ,
2978
+ )
2979
+ . emit ( ) ;
2980
+ }
2981
+ }
2982
+
2851
2983
fn parse_dot_or_call_expr_with_ ( & mut self , e0 : P < Expr > , lo : Span ) -> PResult < ' a , P < Expr > > {
2852
2984
let mut e = e0;
2853
2985
let mut hi;
0 commit comments