@@ -7,7 +7,7 @@ use clippy_utils::{get_parent_expr_for_hir, is_trait_method, strip_pat_refs};
7
7
use if_chain:: if_chain;
8
8
use rustc_errors:: Applicability ;
9
9
use rustc_hir as hir;
10
- use rustc_hir:: { self , ExprKind , HirId , PatKind } ;
10
+ use rustc_hir:: { self , ExprKind , HirId , MutTy , PatKind , TyKind } ;
11
11
use rustc_infer:: infer:: TyCtxtInferExt ;
12
12
use rustc_lint:: LateContext ;
13
13
use rustc_middle:: hir:: place:: ProjectionKind ;
@@ -52,26 +52,12 @@ pub(super) fn check<'tcx>(
52
52
then {
53
53
if let hir:: PatKind :: Ref ( ..) = closure_arg. pat. kind {
54
54
Some ( search_snippet. replacen( '&' , "" , 1 ) )
55
- } else if let PatKind :: Binding ( _, binding_id, _, _) = strip_pat_refs( closure_arg. pat) . kind {
56
- // this binding is composed of at least two levels of references, so we need to remove one
57
- let binding_type = cx. typeck_results( ) . node_type( binding_id) ;
58
- let innermost_is_ref = if let ty:: Ref ( _, inner, _) = binding_type. kind( ) {
59
- matches!( inner. kind( ) , ty:: Ref ( _, innermost, _) if innermost. is_ref( ) )
60
- } else {
61
- false
62
- } ;
63
-
55
+ } else if let PatKind :: Binding ( ..) = strip_pat_refs( closure_arg. pat) . kind {
64
56
// `find()` provides a reference to the item, but `any` does not,
65
57
// so we should fix item usages for suggestion
66
- if let Some ( closure_sugg) = get_closure_suggestion( cx, search_arg, closure_body ) {
58
+ if let Some ( closure_sugg) = get_closure_suggestion( cx, search_arg) {
67
59
applicability = closure_sugg. applicability;
68
- if innermost_is_ref {
69
- Some ( closure_sugg. suggestion. replacen( '&' , "" , 1 ) )
70
- } else {
71
- Some ( closure_sugg. suggestion)
72
- }
73
- } else if innermost_is_ref {
74
- Some ( search_snippet. replacen( '&' , "" , 1 ) )
60
+ Some ( closure_sugg. suggestion)
75
61
} else {
76
62
Some ( search_snippet. to_string( ) )
77
63
}
@@ -174,6 +160,7 @@ pub(super) fn check<'tcx>(
174
160
}
175
161
}
176
162
163
+ #[ derive( Debug ) ]
177
164
struct ClosureSugg {
178
165
applicability : Applicability ,
179
166
suggestion : String ,
@@ -182,38 +169,45 @@ struct ClosureSugg {
182
169
// Build suggestion gradually by handling closure arg specific usages,
183
170
// such as explicit deref and borrowing cases.
184
171
// Returns `None` if no such use cases have been triggered in closure body
185
- fn get_closure_suggestion < ' tcx > (
186
- cx : & LateContext < ' _ > ,
187
- search_arg : & ' tcx hir:: Expr < ' _ > ,
188
- closure_body : & hir:: Body < ' _ > ,
189
- ) -> Option < ClosureSugg > {
190
- let mut visitor = DerefDelegate {
191
- cx,
192
- closure_span : search_arg. span ,
193
- next_pos : search_arg. span . lo ( ) ,
194
- suggestion_start : String :: new ( ) ,
195
- applicability : Applicability :: MachineApplicable ,
196
- } ;
172
+ fn get_closure_suggestion < ' tcx > ( cx : & LateContext < ' _ > , search_expr : & ' tcx hir:: Expr < ' _ > ) -> Option < ClosureSugg > {
173
+ if let hir:: ExprKind :: Closure ( _, fn_decl, body_id, ..) = search_expr. kind {
174
+ let closure_body = cx. tcx . hir ( ) . body ( body_id) ;
175
+ // is closure arg a double reference (i.e.: `|x: &&i32| ...`)
176
+ let closure_arg_is_double_ref = if let TyKind :: Rptr ( _, MutTy { ty, .. } ) = fn_decl. inputs [ 0 ] . kind {
177
+ matches ! ( ty. kind, TyKind :: Rptr ( _, MutTy { .. } ) )
178
+ } else {
179
+ false
180
+ } ;
181
+
182
+ let mut visitor = DerefDelegate {
183
+ cx,
184
+ closure_span : search_expr. span ,
185
+ closure_arg_is_double_ref,
186
+ next_pos : search_expr. span . lo ( ) ,
187
+ suggestion_start : String :: new ( ) ,
188
+ applicability : Applicability :: MachineApplicable ,
189
+ } ;
197
190
198
- let fn_def_id = cx. tcx . hir ( ) . local_def_id ( search_arg . hir_id ) ;
199
- cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
200
- ExprUseVisitor :: new ( & mut visitor, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) )
201
- . consume_body ( closure_body) ;
202
- } ) ;
191
+ let fn_def_id = cx. tcx . hir ( ) . local_def_id ( search_expr . hir_id ) ;
192
+ cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
193
+ ExprUseVisitor :: new ( & mut visitor, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) )
194
+ . consume_body ( closure_body) ;
195
+ } ) ;
203
196
204
- if visitor. suggestion_start . is_empty ( ) {
205
- None
206
- } else {
207
- Some ( ClosureSugg {
208
- applicability : visitor. applicability ,
209
- suggestion : visitor. finish ( ) ,
210
- } )
197
+ if !visitor. suggestion_start . is_empty ( ) {
198
+ return Some ( ClosureSugg {
199
+ applicability : visitor. applicability ,
200
+ suggestion : visitor. finish ( ) ,
201
+ } ) ;
202
+ }
211
203
}
204
+ None
212
205
}
213
206
214
207
struct DerefDelegate < ' a , ' tcx > {
215
208
cx : & ' a LateContext < ' tcx > ,
216
209
closure_span : Span ,
210
+ closure_arg_is_double_ref : bool ,
217
211
next_pos : BytePos ,
218
212
suggestion_start : String ,
219
213
applicability : Applicability ,
@@ -223,7 +217,12 @@ impl DerefDelegate<'_, 'tcx> {
223
217
pub fn finish ( & mut self ) -> String {
224
218
let end_span = Span :: new ( self . next_pos , self . closure_span . hi ( ) , self . closure_span . ctxt ( ) , None ) ;
225
219
let end_snip = snippet_with_applicability ( self . cx , end_span, ".." , & mut self . applicability ) ;
226
- format ! ( "{}{}" , self . suggestion_start, end_snip)
220
+ let sugg = format ! ( "{}{}" , self . suggestion_start, end_snip) ;
221
+ if self . closure_arg_is_double_ref {
222
+ sugg. replacen ( '&' , "" , 1 )
223
+ } else {
224
+ sugg
225
+ }
227
226
}
228
227
229
228
fn func_takes_arg_by_double_ref ( & self , parent_expr : & ' tcx hir:: Expr < ' _ > , cmt_hir_id : HirId ) -> bool {
@@ -261,6 +260,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
261
260
if cmt. place . projections . is_empty ( ) {
262
261
// handle item without any projection, that needs an explicit borrowing
263
262
// i.e.: suggest `&x` instead of `x`
263
+ self . closure_arg_is_double_ref = false ;
264
264
self . suggestion_start . push_str ( & format ! ( "{}&{}" , start_snip, ident_str) ) ;
265
265
} else {
266
266
// cases where a parent `Call` or `MethodCall` is using the item
@@ -272,29 +272,43 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
272
272
// - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
273
273
// no projection should be suggested
274
274
if let Some ( parent_expr) = get_parent_expr_for_hir ( self . cx , cmt. hir_id ) {
275
- if let ExprKind :: Call ( _, call_args) | ExprKind :: MethodCall ( _, _, call_args, _) = parent_expr. kind {
276
- let expr = self . cx . tcx . hir ( ) . expect_expr ( cmt. hir_id ) ;
277
- let arg_ty_kind = self . cx . typeck_results ( ) . expr_ty ( expr) . kind ( ) ;
278
-
279
- if matches ! ( arg_ty_kind, ty:: Ref ( _, _, Mutability :: Not ) ) {
280
- // suggest ampersand if call function is taking args by double reference
281
- let takes_arg_by_double_ref = self . func_takes_arg_by_double_ref ( parent_expr, cmt. hir_id ) ;
282
-
283
- // do not suggest ampersand if the ident is the method caller
284
- let ident_sugg = if !call_args. is_empty ( )
285
- && call_args[ 0 ] . hir_id == cmt. hir_id
286
- && !takes_arg_by_double_ref
287
- {
288
- format ! ( "{}{}" , start_snip, ident_str)
289
- } else {
290
- format ! ( "{}&{}" , start_snip, ident_str)
291
- } ;
292
- self . suggestion_start . push_str ( & ident_sugg) ;
275
+ match & parent_expr. kind {
276
+ // given expression is the self argument and will be handled completely by the compiler
277
+ // i.e.: `|x| x.is_something()`
278
+ ExprKind :: MethodCall ( _, _, [ self_expr, ..] , _) if self_expr. hir_id == cmt. hir_id => {
279
+ self . suggestion_start . push_str ( & format ! ( "{}{}" , start_snip, ident_str) ) ;
293
280
self . next_pos = span. hi ( ) ;
294
281
return ;
295
- }
282
+ } ,
283
+ // item is used in a call
284
+ // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
285
+ ExprKind :: Call ( _, [ call_args @ ..] ) | ExprKind :: MethodCall ( _, _, [ _, call_args @ ..] , _) => {
286
+ let expr = self . cx . tcx . hir ( ) . expect_expr ( cmt. hir_id ) ;
287
+ let arg_ty_kind = self . cx . typeck_results ( ) . expr_ty ( expr) . kind ( ) ;
296
288
297
- self . applicability = Applicability :: Unspecified ;
289
+ if matches ! ( arg_ty_kind, ty:: Ref ( _, _, Mutability :: Not ) ) {
290
+ // suggest ampersand if call function is taking args by double reference
291
+ let takes_arg_by_double_ref =
292
+ self . func_takes_arg_by_double_ref ( parent_expr, cmt. hir_id ) ;
293
+
294
+ // no need to bind again if the function doesn't take arg by double ref
295
+ // and if the item is already a double ref
296
+ let ident_sugg = if !call_args. is_empty ( )
297
+ && !takes_arg_by_double_ref
298
+ && self . closure_arg_is_double_ref
299
+ {
300
+ format ! ( "{}{}" , start_snip, ident_str)
301
+ } else {
302
+ format ! ( "{}&{}" , start_snip, ident_str)
303
+ } ;
304
+ self . suggestion_start . push_str ( & ident_sugg) ;
305
+ self . next_pos = span. hi ( ) ;
306
+ return ;
307
+ }
308
+
309
+ self . applicability = Applicability :: Unspecified ;
310
+ } ,
311
+ _ => ( ) ,
298
312
}
299
313
}
300
314
@@ -346,7 +360,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
346
360
347
361
// handle `ProjectionKind::Deref` by removing one explicit deref
348
362
// if no special case was detected (i.e.: suggest `*x` instead of `**x`)
349
- if !projections_handled {
363
+ if projections_handled {
364
+ self . closure_arg_is_double_ref = false ;
365
+ } else {
350
366
let last_deref = cmt
351
367
. place
352
368
. projections
0 commit comments