@@ -7,10 +7,74 @@ 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
15
+ /// No turbofish needed in any case.
16
+ fn no_turbofish ( _: & LateContext < ' _ > , _: & hir:: Expr < ' _ > ) -> bool {
17
+ false
18
+ }
19
+
20
+ /// Turbofish (`::<T>`) may be needed, but can be omitted if we are certain
21
+ /// that the type can be inferred from usage.
22
+ fn turbofish_if_not_inferred ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
23
+ let parent = cx. tcx . hir ( ) . get_parent ( expr. hir_id ) ;
24
+
25
+ // some common cases where turbofish isn't needed:
26
+ // - assigned to a local variable with a type annotation
27
+ if let hir:: Node :: Local ( local) = parent
28
+ && local. ty . is_some ( )
29
+ {
30
+ return false ;
31
+ }
32
+
33
+ // - part of a function call argument, can be inferred from the function signature (provided that
34
+ // the parameter is not a generic type parameter)
35
+ if let hir:: Node :: Expr ( parent_expr) = parent
36
+ && let hir:: ExprKind :: Call ( recv, args) = parent_expr. kind
37
+ && let hir:: ExprKind :: Path ( ref qpath) = recv. kind
38
+ && let Some ( fn_def_id) = cx. qpath_res ( qpath, recv. hir_id ) . opt_def_id ( )
39
+ && let fn_sig = cx. tcx . fn_sig ( fn_def_id) . skip_binder ( ) . skip_binder ( )
40
+ && let Some ( arg_pos) = args. iter ( ) . position ( |arg| arg. hir_id == expr. hir_id )
41
+ && let Some ( ty) = fn_sig. inputs ( ) . get ( arg_pos)
42
+ && !matches ! ( ty. kind( ) , ty:: Param ( _) )
43
+ {
44
+ return false ;
45
+ }
46
+
47
+ // if it's neither of those, stay on the safe side and suggest turbofish,
48
+ // even if it could work!
49
+ true
50
+ }
51
+
52
+ #[ derive( Copy , Clone ) ]
53
+ struct Replacement {
54
+ method_name : & ' static str ,
55
+ has_args : bool ,
56
+ requires_turbofish : fn ( & LateContext < ' _ > , & hir:: Expr < ' _ > ) -> bool ,
57
+ }
58
+ impl Replacement {
59
+ /// `any(f)`, `all(f)`
60
+ pub fn non_generic ( method_name : & ' static str ) -> Self {
61
+ Self {
62
+ method_name,
63
+ has_args : true ,
64
+ requires_turbofish : no_turbofish,
65
+ }
66
+ }
67
+
68
+ /// `sum::<T>()`, `product::<T>()`
69
+ pub fn generic ( method_name : & ' static str ) -> Self {
70
+ Self {
71
+ method_name,
72
+ has_args : false ,
73
+ requires_turbofish : turbofish_if_not_inferred,
74
+ }
75
+ }
76
+ }
77
+
14
78
pub ( super ) fn check (
15
79
cx : & LateContext < ' _ > ,
16
80
expr : & hir:: Expr < ' _ > ,
@@ -24,8 +88,7 @@ pub(super) fn check(
24
88
acc : & hir:: Expr < ' _ > ,
25
89
fold_span : Span ,
26
90
op : hir:: BinOpKind ,
27
- replacement_method_name : & str ,
28
- replacement_has_args : bool ,
91
+ replacement : Replacement ,
29
92
) {
30
93
if_chain ! {
31
94
// Extract the body of the closure passed to fold
@@ -43,18 +106,27 @@ pub(super) fn check(
43
106
if let PatKind :: Binding ( _, second_arg_id, second_arg_ident, _) = strip_pat_refs( param_b. pat) . kind;
44
107
45
108
if path_to_local_id( left_expr, first_arg_id) ;
46
- if replacement_has_args || path_to_local_id( right_expr, second_arg_id) ;
109
+ if replacement . has_args || path_to_local_id( right_expr, second_arg_id) ;
47
110
48
111
then {
49
112
let mut applicability = Applicability :: MachineApplicable ;
50
- let sugg = if replacement_has_args {
113
+
114
+ let turbofish = if ( replacement. requires_turbofish) ( cx, expr) {
115
+ format!( "::<{}>" , cx. typeck_results( ) . expr_ty_adjusted( right_expr) . peel_refs( ) )
116
+ } else {
117
+ String :: new( )
118
+ } ;
119
+
120
+ let sugg = if replacement. has_args {
51
121
format!(
52
- "{replacement_method_name}(|{second_arg_ident}| {r})" ,
122
+ "{method}{turbofish}(|{second_arg_ident}| {r})" ,
123
+ method = replacement. method_name,
53
124
r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
54
125
)
55
126
} else {
56
127
format!(
57
- "{replacement_method_name}()" ,
128
+ "{method}{turbofish}()" ,
129
+ method = replacement. method_name,
58
130
)
59
131
} ;
60
132
@@ -80,11 +152,43 @@ pub(super) fn check(
80
152
// Check if the first argument to .fold is a suitable literal
81
153
if let hir:: ExprKind :: Lit ( lit) = init. kind {
82
154
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 ) ,
155
+ ast:: LitKind :: Bool ( false ) => {
156
+ check_fold_with_op (
157
+ cx,
158
+ expr,
159
+ acc,
160
+ fold_span,
161
+ hir:: BinOpKind :: Or ,
162
+ Replacement :: non_generic ( "any" ) ,
163
+ ) ;
164
+ } ,
165
+ ast:: LitKind :: Bool ( true ) => {
166
+ check_fold_with_op (
167
+ cx,
168
+ expr,
169
+ acc,
170
+ fold_span,
171
+ hir:: BinOpKind :: And ,
172
+ Replacement :: non_generic ( "all" ) ,
173
+ ) ;
174
+ } ,
175
+ ast:: LitKind :: Int ( 0 , _) => check_fold_with_op (
176
+ cx,
177
+ expr,
178
+ acc,
179
+ fold_span,
180
+ hir:: BinOpKind :: Add ,
181
+ Replacement :: generic ( "sum" ) ,
182
+ ) ,
86
183
ast:: LitKind :: Int ( 1 , _) => {
87
- check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Mul , "product" , false ) ;
184
+ check_fold_with_op (
185
+ cx,
186
+ expr,
187
+ acc,
188
+ fold_span,
189
+ hir:: BinOpKind :: Mul ,
190
+ Replacement :: generic ( "product" ) ,
191
+ ) ;
88
192
} ,
89
193
_ => ( ) ,
90
194
}
0 commit comments