11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use clippy_utils:: res:: { MaybeDef , MaybeResPath , MaybeTypeckRes } ;
2+ use clippy_utils:: res:: { MaybeDef , MaybeQPath , MaybeResPath , MaybeTypeckRes } ;
33use clippy_utils:: source:: snippet_with_applicability;
4- use clippy_utils:: { peel_blocks, strip_pat_refs} ;
4+ use clippy_utils:: { DefinedTy , ExprUseNode , expr_use_ctxt , peel_blocks, strip_pat_refs} ;
55use rustc_ast:: ast;
66use rustc_data_structures:: packed:: Pu128 ;
77use rustc_errors:: Applicability ;
88use rustc_hir as hir;
99use rustc_hir:: PatKind ;
10+ use rustc_hir:: def:: { DefKind , Res } ;
1011use rustc_lint:: LateContext ;
1112use rustc_middle:: ty;
12- use rustc_span:: { Span , sym} ;
13+ use rustc_span:: { Span , Symbol , sym} ;
1314
1415use super :: UNNECESSARY_FOLD ;
1516
1617/// Do we need to suggest turbofish when suggesting a replacement method?
1718/// Changing `fold` to `sum` needs it sometimes when the return type can't be
1819/// inferred. This checks for some common cases where it can be safely omitted
19- fn needs_turbofish ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
20- let parent = cx. tcx . parent_hir_node ( expr. hir_id ) ;
21-
22- // some common cases where turbofish isn't needed:
23- // - assigned to a local variable with a type annotation
24- if let hir:: Node :: LetStmt ( local) = parent
25- && local. ty . is_some ( )
20+ fn needs_turbofish < ' tcx > ( cx : & LateContext < ' tcx > , expr : & hir:: Expr < ' tcx > ) -> bool {
21+ let use_cx = expr_use_ctxt ( cx, expr) ;
22+ if use_cx. same_ctxt
23+ && let use_node = use_cx. use_node ( cx)
24+ && let Some ( ty) = use_node. defined_ty ( cx)
2625 {
27- return false ;
28- }
26+ // some common cases where turbofish isn't needed:
27+ match ( use_node, ty) {
28+ // - assigned to a local variable with a type annotation
29+ ( ExprUseNode :: LetStmt ( _) , _) => return false ,
2930
30- // - part of a function call argument, can be inferred from the function signature (provided that
31- // the parameter is not a generic type parameter)
32- if let hir:: Node :: Expr ( parent_expr) = parent
33- && let hir:: ExprKind :: Call ( recv, args) = parent_expr. kind
34- && let hir:: ExprKind :: Path ( ref qpath) = recv. kind
35- && let Some ( fn_def_id) = cx. qpath_res ( qpath, recv. hir_id ) . opt_def_id ( )
36- && let fn_sig = cx. tcx . fn_sig ( fn_def_id) . skip_binder ( ) . skip_binder ( )
37- && let Some ( arg_pos) = args. iter ( ) . position ( |arg| arg. hir_id == expr. hir_id )
38- && let Some ( ty) = fn_sig. inputs ( ) . get ( arg_pos)
39- && !matches ! ( ty. kind( ) , ty:: Param ( _) )
40- {
41- return false ;
31+ // - part of a function call argument, can be inferred from the function signature (provided that the
32+ // parameter is not a generic type parameter)
33+ ( ExprUseNode :: FnArg ( ..) , DefinedTy :: Mir { ty : arg_ty, .. } )
34+ if !matches ! ( arg_ty. skip_binder( ) . kind( ) , ty:: Param ( _) ) =>
35+ {
36+ return false ;
37+ } ,
38+
39+ // - the final expression in the body of a function with a simple return type
40+ ( ExprUseNode :: Return ( _) , DefinedTy :: Mir { ty : fn_return_ty, .. } )
41+ if fn_return_ty. skip_binder ( ) . is_simple_text ( ) =>
42+ {
43+ return false ;
44+ } ,
45+ _ => { } ,
46+ }
4247 }
4348
4449 // if it's neither of those, stay on the safe side and suggest turbofish,
@@ -60,7 +65,7 @@ fn check_fold_with_op(
6065 fold_span : Span ,
6166 op : hir:: BinOpKind ,
6267 replacement : Replacement ,
63- ) {
68+ ) -> bool {
6469 if let hir:: ExprKind :: Closure ( & hir:: Closure { body, .. } ) = acc. kind
6570 // Extract the body of the closure passed to fold
6671 && let closure_body = cx. tcx . hir_body ( body)
@@ -93,7 +98,7 @@ fn check_fold_with_op(
9398 r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
9499 )
95100 } else {
96- format ! ( "{method}{turbofish}()" , method = replacement. method_name, )
101+ format ! ( "{method}{turbofish}()" , method = replacement. method_name)
97102 } ;
98103
99104 span_lint_and_sugg (
@@ -105,12 +110,47 @@ fn check_fold_with_op(
105110 sugg,
106111 applicability,
107112 ) ;
113+ return true ;
108114 }
115+ false
109116}
110117
111- pub ( super ) fn check (
118+ fn check_fold_with_method (
112119 cx : & LateContext < ' _ > ,
113120 expr : & hir:: Expr < ' _ > ,
121+ acc : & hir:: Expr < ' _ > ,
122+ fold_span : Span ,
123+ method : Symbol ,
124+ replacement : Replacement ,
125+ ) {
126+ // Extract the name of the function passed to `fold`
127+ if let Res :: Def ( DefKind :: AssocFn , fn_did) = acc. res_if_named ( cx, method)
128+ // Check if the function belongs to the operator
129+ && cx. tcx . is_diagnostic_item ( method, fn_did)
130+ {
131+ let applicability = Applicability :: MachineApplicable ;
132+
133+ let turbofish = if replacement. has_generic_return {
134+ format ! ( "::<{}>" , cx. typeck_results( ) . expr_ty( expr) )
135+ } else {
136+ String :: new ( )
137+ } ;
138+
139+ span_lint_and_sugg (
140+ cx,
141+ UNNECESSARY_FOLD ,
142+ fold_span. with_hi ( expr. span . hi ( ) ) ,
143+ "this `.fold` can be written more succinctly using another method" ,
144+ "try" ,
145+ format ! ( "{method}{turbofish}()" , method = replacement. method_name) ,
146+ applicability,
147+ ) ;
148+ }
149+ }
150+
151+ pub ( super ) fn check < ' tcx > (
152+ cx : & LateContext < ' tcx > ,
153+ expr : & hir:: Expr < ' tcx > ,
114154 init : & hir:: Expr < ' _ > ,
115155 acc : & hir:: Expr < ' _ > ,
116156 fold_span : Span ,
@@ -124,60 +164,40 @@ pub(super) fn check(
124164 if let hir:: ExprKind :: Lit ( lit) = init. kind {
125165 match lit. node {
126166 ast:: LitKind :: Bool ( false ) => {
127- check_fold_with_op (
128- cx,
129- expr,
130- acc,
131- fold_span,
132- hir:: BinOpKind :: Or ,
133- Replacement {
134- method_name : "any" ,
135- has_args : true ,
136- has_generic_return : false ,
137- } ,
138- ) ;
167+ let replacement = Replacement {
168+ method_name : "any" ,
169+ has_args : true ,
170+ has_generic_return : false ,
171+ } ;
172+ check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Or , replacement) ;
139173 } ,
140174 ast:: LitKind :: Bool ( true ) => {
141- check_fold_with_op (
142- cx,
143- expr,
144- acc,
145- fold_span,
146- hir:: BinOpKind :: And ,
147- Replacement {
148- method_name : "all" ,
149- has_args : true ,
150- has_generic_return : false ,
151- } ,
152- ) ;
175+ let replacement = Replacement {
176+ method_name : "all" ,
177+ has_args : true ,
178+ has_generic_return : false ,
179+ } ;
180+ check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: And , replacement) ;
153181 } ,
154182 ast:: LitKind :: Int ( Pu128 ( 0 ) , _) => {
155- check_fold_with_op (
156- cx,
157- expr,
158- acc,
159- fold_span,
160- hir:: BinOpKind :: Add ,
161- Replacement {
162- method_name : "sum" ,
163- has_args : false ,
164- has_generic_return : needs_turbofish ( cx, expr) ,
165- } ,
166- ) ;
183+ let replacement = Replacement {
184+ method_name : "sum" ,
185+ has_args : false ,
186+ has_generic_return : needs_turbofish ( cx, expr) ,
187+ } ;
188+ if !check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Add , replacement) {
189+ check_fold_with_method ( cx, expr, acc, fold_span, sym:: add, replacement) ;
190+ }
167191 } ,
168192 ast:: LitKind :: Int ( Pu128 ( 1 ) , _) => {
169- check_fold_with_op (
170- cx,
171- expr,
172- acc,
173- fold_span,
174- hir:: BinOpKind :: Mul ,
175- Replacement {
176- method_name : "product" ,
177- has_args : false ,
178- has_generic_return : needs_turbofish ( cx, expr) ,
179- } ,
180- ) ;
193+ let replacement = Replacement {
194+ method_name : "product" ,
195+ has_args : false ,
196+ has_generic_return : needs_turbofish ( cx, expr) ,
197+ } ;
198+ if !check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Mul , replacement) {
199+ check_fold_with_method ( cx, expr, acc, fold_span, sym:: mul, replacement) ;
200+ }
181201 } ,
182202 _ => ( ) ,
183203 }
0 commit comments