@@ -7,71 +7,120 @@ use rustc_errors::Applicability;
7
7
use rustc_hir as hir;
8
8
use rustc_hir:: PatKind ;
9
9
use rustc_lint:: LateContext ;
10
+ use rustc_middle:: ty;
10
11
use rustc_span:: { source_map:: Span , sym} ;
11
12
12
13
use super :: UNNECESSARY_FOLD ;
13
14
14
- pub ( super ) fn check (
15
+ /// Do we need to suggest turbofish when suggesting a replacement method?
16
+ /// Changing `fold` to `sum` needs it sometimes when the return type can't be
17
+ /// inferred. This checks for some common cases where it can be safely omitted
18
+ fn needs_turbofish ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
19
+ let parent = cx. tcx . hir ( ) . get_parent ( expr. hir_id ) ;
20
+
21
+ // some common cases where turbofish isn't needed:
22
+ // - assigned to a local variable with a type annotation
23
+ if let hir:: Node :: Local ( local) = parent
24
+ && local. ty . is_some ( )
25
+ {
26
+ return false ;
27
+ }
28
+
29
+ // - part of a function call argument, can be inferred from the function signature (provided that
30
+ // the parameter is not a generic type parameter)
31
+ if let hir:: Node :: Expr ( parent_expr) = parent
32
+ && let hir:: ExprKind :: Call ( recv, args) = parent_expr. kind
33
+ && let hir:: ExprKind :: Path ( ref qpath) = recv. kind
34
+ && let Some ( fn_def_id) = cx. qpath_res ( qpath, recv. hir_id ) . opt_def_id ( )
35
+ && let fn_sig = cx. tcx . fn_sig ( fn_def_id) . skip_binder ( ) . skip_binder ( )
36
+ && let Some ( arg_pos) = args. iter ( ) . position ( |arg| arg. hir_id == expr. hir_id )
37
+ && let Some ( ty) = fn_sig. inputs ( ) . get ( arg_pos)
38
+ && !matches ! ( ty. kind( ) , ty:: Param ( _) )
39
+ {
40
+ return false ;
41
+ }
42
+
43
+ // if it's neither of those, stay on the safe side and suggest turbofish,
44
+ // even if it could work!
45
+ true
46
+ }
47
+
48
+ #[ derive( Copy , Clone ) ]
49
+ struct Replacement {
50
+ method_name : & ' static str ,
51
+ has_args : bool ,
52
+ has_generic_return : bool ,
53
+ }
54
+
55
+ fn check_fold_with_op (
15
56
cx : & LateContext < ' _ > ,
16
57
expr : & hir:: Expr < ' _ > ,
17
- init : & hir:: Expr < ' _ > ,
18
58
acc : & hir:: Expr < ' _ > ,
19
59
fold_span : Span ,
60
+ op : hir:: BinOpKind ,
61
+ replacement : Replacement ,
20
62
) {
21
- fn check_fold_with_op (
22
- cx : & LateContext < ' _ > ,
23
- expr : & hir:: Expr < ' _ > ,
24
- acc : & hir:: Expr < ' _ > ,
25
- fold_span : Span ,
26
- op : hir:: BinOpKind ,
27
- replacement_method_name : & str ,
28
- replacement_has_args : bool ,
29
- ) {
30
- if_chain ! {
31
- // Extract the body of the closure passed to fold
32
- if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = acc. kind;
33
- let closure_body = cx. tcx. hir( ) . body( body) ;
34
- let closure_expr = peel_blocks( closure_body. value) ;
35
-
36
- // Check if the closure body is of the form `acc <op> some_expr(x)`
37
- if let hir:: ExprKind :: Binary ( ref bin_op, left_expr, right_expr) = closure_expr. kind;
38
- if bin_op. node == op;
39
-
40
- // Extract the names of the two arguments to the closure
41
- if let [ param_a, param_b] = closure_body. params;
42
- if let PatKind :: Binding ( _, first_arg_id, ..) = strip_pat_refs( param_a. pat) . kind;
43
- if let PatKind :: Binding ( _, second_arg_id, second_arg_ident, _) = strip_pat_refs( param_b. pat) . kind;
44
-
45
- if path_to_local_id( left_expr, first_arg_id) ;
46
- if replacement_has_args || path_to_local_id( right_expr, second_arg_id) ;
47
-
48
- then {
49
- let mut applicability = Applicability :: MachineApplicable ;
50
- let sugg = if replacement_has_args {
51
- format!(
52
- "{replacement_method_name}(|{second_arg_ident}| {r})" ,
53
- r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
54
- )
55
- } else {
56
- format!(
57
- "{replacement_method_name}()" ,
58
- )
59
- } ;
60
-
61
- span_lint_and_sugg(
62
- cx,
63
- UNNECESSARY_FOLD ,
64
- fold_span. with_hi( expr. span. hi( ) ) ,
65
- // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
66
- "this `.fold` can be written more succinctly using another method" ,
67
- "try" ,
68
- sugg,
69
- applicability,
70
- ) ;
71
- }
63
+ if_chain ! {
64
+ // Extract the body of the closure passed to fold
65
+ if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = acc. kind;
66
+ let closure_body = cx. tcx. hir( ) . body( body) ;
67
+ let closure_expr = peel_blocks( closure_body. value) ;
68
+
69
+ // Check if the closure body is of the form `acc <op> some_expr(x)`
70
+ if let hir:: ExprKind :: Binary ( ref bin_op, left_expr, right_expr) = closure_expr. kind;
71
+ if bin_op. node == op;
72
+
73
+ // Extract the names of the two arguments to the closure
74
+ if let [ param_a, param_b] = closure_body. params;
75
+ if let PatKind :: Binding ( _, first_arg_id, ..) = strip_pat_refs( param_a. pat) . kind;
76
+ if let PatKind :: Binding ( _, second_arg_id, second_arg_ident, _) = strip_pat_refs( param_b. pat) . kind;
77
+
78
+ if path_to_local_id( left_expr, first_arg_id) ;
79
+ if replacement. has_args || path_to_local_id( right_expr, second_arg_id) ;
80
+
81
+ then {
82
+ let mut applicability = Applicability :: MachineApplicable ;
83
+
84
+ let turbofish = if replacement. has_generic_return {
85
+ format!( "::<{}>" , cx. typeck_results( ) . expr_ty_adjusted( right_expr) . peel_refs( ) )
86
+ } else {
87
+ String :: new( )
88
+ } ;
89
+
90
+ let sugg = if replacement. has_args {
91
+ format!(
92
+ "{method}{turbofish}(|{second_arg_ident}| {r})" ,
93
+ method = replacement. method_name,
94
+ r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
95
+ )
96
+ } else {
97
+ format!(
98
+ "{method}{turbofish}()" ,
99
+ method = replacement. method_name,
100
+ )
101
+ } ;
102
+
103
+ span_lint_and_sugg(
104
+ cx,
105
+ UNNECESSARY_FOLD ,
106
+ fold_span. with_hi( expr. span. hi( ) ) ,
107
+ // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
108
+ "this `.fold` can be written more succinctly using another method" ,
109
+ "try" ,
110
+ sugg,
111
+ applicability,
112
+ ) ;
72
113
}
73
114
}
115
+ }
74
116
117
+ pub ( super ) fn check (
118
+ cx : & LateContext < ' _ > ,
119
+ expr : & hir:: Expr < ' _ > ,
120
+ init : & hir:: Expr < ' _ > ,
121
+ acc : & hir:: Expr < ' _ > ,
122
+ fold_span : Span ,
123
+ ) {
75
124
// Check that this is a call to Iterator::fold rather than just some function called fold
76
125
if !is_trait_method ( cx, expr, sym:: Iterator ) {
77
126
return ;
@@ -80,11 +129,59 @@ pub(super) fn check(
80
129
// Check if the first argument to .fold is a suitable literal
81
130
if let hir:: ExprKind :: Lit ( lit) = init. kind {
82
131
match lit. node {
83
- ast:: LitKind :: Bool ( false ) => check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Or , "any" , true ) ,
84
- ast:: LitKind :: Bool ( true ) => check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: And , "all" , true ) ,
85
- ast:: LitKind :: Int ( 0 , _) => check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Add , "sum" , false ) ,
132
+ ast:: LitKind :: Bool ( false ) => {
133
+ check_fold_with_op (
134
+ cx,
135
+ expr,
136
+ acc,
137
+ fold_span,
138
+ hir:: BinOpKind :: Or ,
139
+ Replacement {
140
+ has_args : true ,
141
+ has_generic_return : false ,
142
+ method_name : "any" ,
143
+ } ,
144
+ ) ;
145
+ } ,
146
+ ast:: LitKind :: Bool ( true ) => {
147
+ check_fold_with_op (
148
+ cx,
149
+ expr,
150
+ acc,
151
+ fold_span,
152
+ hir:: BinOpKind :: And ,
153
+ Replacement {
154
+ has_args : true ,
155
+ has_generic_return : false ,
156
+ method_name : "all" ,
157
+ } ,
158
+ ) ;
159
+ } ,
160
+ ast:: LitKind :: Int ( 0 , _) => check_fold_with_op (
161
+ cx,
162
+ expr,
163
+ acc,
164
+ fold_span,
165
+ hir:: BinOpKind :: Add ,
166
+ Replacement {
167
+ has_args : false ,
168
+ has_generic_return : needs_turbofish ( cx, expr) ,
169
+ method_name : "sum" ,
170
+ } ,
171
+ ) ,
86
172
ast:: LitKind :: Int ( 1 , _) => {
87
- check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Mul , "product" , false ) ;
173
+ check_fold_with_op (
174
+ cx,
175
+ expr,
176
+ acc,
177
+ fold_span,
178
+ hir:: BinOpKind :: Mul ,
179
+ Replacement {
180
+ has_args : false ,
181
+ has_generic_return : needs_turbofish ( cx, expr) ,
182
+ method_name : "product" ,
183
+ } ,
184
+ ) ;
88
185
} ,
89
186
_ => ( ) ,
90
187
}
0 commit comments