@@ -7,10 +7,74 @@ use rustc_errors::Applicability;
77use rustc_hir as hir;
88use rustc_hir:: PatKind ;
99use rustc_lint:: LateContext ;
10+ use rustc_middle:: ty;
1011use rustc_span:: { source_map:: Span , sym} ;
1112
1213use super :: UNNECESSARY_FOLD ;
1314
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+
1478pub ( super ) fn check (
1579 cx : & LateContext < ' _ > ,
1680 expr : & hir:: Expr < ' _ > ,
@@ -24,8 +88,7 @@ pub(super) fn check(
2488 acc : & hir:: Expr < ' _ > ,
2589 fold_span : Span ,
2690 op : hir:: BinOpKind ,
27- replacement_method_name : & str ,
28- replacement_has_args : bool ,
91+ replacement : Replacement ,
2992 ) {
3093 if_chain ! {
3194 // Extract the body of the closure passed to fold
@@ -43,18 +106,27 @@ pub(super) fn check(
43106 if let PatKind :: Binding ( _, second_arg_id, second_arg_ident, _) = strip_pat_refs( param_b. pat) . kind;
44107
45108 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) ;
47110
48111 then {
49112 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 {
51121 format!(
52- "{replacement_method_name}(|{second_arg_ident}| {r})" ,
122+ "{method}{turbofish}(|{second_arg_ident}| {r})" ,
123+ method = replacement. method_name,
53124 r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
54125 )
55126 } else {
56127 format!(
57- "{replacement_method_name}()" ,
128+ "{method}{turbofish}()" ,
129+ method = replacement. method_name,
58130 )
59131 } ;
60132
@@ -80,11 +152,43 @@ pub(super) fn check(
80152 // Check if the first argument to .fold is a suitable literal
81153 if let hir:: ExprKind :: Lit ( lit) = init. kind {
82154 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+ ) ,
86183 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+ ) ;
88192 } ,
89193 _ => ( ) ,
90194 }
0 commit comments