1
+ use std:: iter;
2
+
1
3
use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
2
4
use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
3
5
use clippy_utils:: ty:: is_type_diagnostic_item;
@@ -223,6 +225,26 @@ impl DerefDelegate<'_, 'tcx> {
223
225
let end_snip = snippet_with_applicability ( self . cx , end_span, ".." , & mut self . applicability ) ;
224
226
format ! ( "{}{}" , self . suggestion_start, end_snip)
225
227
}
228
+
229
+ fn func_takes_arg_by_ref ( & self , parent_expr : & ' tcx hir:: Expr < ' _ > , cmt_hir_id : HirId ) -> bool {
230
+ if_chain ! {
231
+ if let ExprKind :: Call ( func, call_args) = parent_expr. kind;
232
+ let typ = self . cx. typeck_results( ) . expr_ty( func) ;
233
+ if let ty:: FnDef ( ..) = typ. kind( ) ;
234
+
235
+ then {
236
+ let mut takes_by_ref = false ;
237
+ for ( arg, ty) in iter:: zip( call_args, typ. fn_sig( self . cx. tcx) . skip_binder( ) . inputs( ) ) {
238
+ if arg. hir_id == cmt_hir_id {
239
+ takes_by_ref = matches!( ty. kind( ) , ty:: Ref ( _, inner, _) if inner. is_ref( ) ) ;
240
+ }
241
+ }
242
+ takes_by_ref
243
+ } else {
244
+ false
245
+ }
246
+ }
247
+ }
226
248
}
227
249
228
250
impl < ' tcx > Delegate < ' tcx > for DerefDelegate < ' _ , ' tcx > {
@@ -252,42 +274,32 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
252
274
let start_span = Span :: new ( self . next_pos , span. lo ( ) , span. ctxt ( ) ) ;
253
275
let start_snip =
254
276
snippet_with_applicability ( self . cx , start_span, ".." , & mut self . applicability ) ;
277
+
278
+ // suggest ampersand if call function is taking args by ref
279
+ let takes_arg_by_ref = self . func_takes_arg_by_ref ( parent_expr, cmt. hir_id ) ;
280
+
255
281
// do not suggest ampersand if the ident is the method caller
256
- let ident_sugg = if !call_args. is_empty ( ) && call_args[ 0 ] . hir_id == cmt. hir_id {
257
- format ! ( "{}{}" , start_snip, ident_str)
258
- } else {
259
- format ! ( "{}&{}" , start_snip, ident_str)
260
- } ;
282
+ let ident_sugg =
283
+ if !call_args. is_empty ( ) && call_args[ 0 ] . hir_id == cmt. hir_id && !takes_arg_by_ref {
284
+ format ! ( "{}{}" , start_snip, ident_str)
285
+ } else {
286
+ format ! ( "{}&{}" , start_snip, ident_str)
287
+ } ;
261
288
self . suggestion_start . push_str ( & ident_sugg) ;
262
289
self . next_pos = span. hi ( ) ;
263
290
return ;
264
- } else {
265
- self . applicability = Applicability :: Unspecified ;
266
291
}
292
+
293
+ self . applicability = Applicability :: Unspecified ;
267
294
}
268
295
}
269
296
270
297
// handle item projections by removing one explicit deref
271
298
// i.e.: suggest `*x` instead of `**x`
272
299
let mut replacement_str = ident_str;
273
300
274
- // handle index projection first
275
- let index_handled = cmt. place . projections . iter ( ) . any ( |proj| match proj. kind {
276
- // Index projection like `|x| foo[x]`
277
- // the index is dropped so we can't get it to build the suggestion,
278
- // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
279
- // instead of `span.lo()` (i.e.: `foo`)
280
- ProjectionKind :: Index => {
281
- let start_span = Span :: new ( self . next_pos , span. hi ( ) , span. ctxt ( ) ) ;
282
- start_snip = snippet_with_applicability ( self . cx , start_span, ".." , & mut self . applicability ) ;
283
- replacement_str. clear ( ) ;
284
- true
285
- } ,
286
- _ => false ,
287
- } ) ;
288
-
289
- // looking for projections other that need to be handled differently
290
- let other_projections_handled = cmt. place . projections . iter ( ) . enumerate ( ) . any ( |( i, proj) | {
301
+ let mut projections_handled = false ;
302
+ cmt. place . projections . iter ( ) . enumerate ( ) . for_each ( |( i, proj) | {
291
303
match proj. kind {
292
304
// Field projection like `|v| v.foo`
293
305
ProjectionKind :: Field ( idx, variant) => match cmt. place . ty_before_projection ( i) . kind ( ) {
@@ -297,34 +309,41 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
297
309
replacement_str,
298
310
def. variants[ variant] . fields[ idx as usize ] . ident. name. as_str( )
299
311
) ;
300
- true
312
+ projections_handled = true ;
301
313
} ,
302
314
ty:: Tuple ( _) => {
303
315
replacement_str = format ! ( "{}.{}" , replacement_str, idx) ;
304
- true
316
+ projections_handled = true ;
305
317
} ,
306
- _ => false ,
318
+ _ => ( ) ,
319
+ } ,
320
+ // Index projection like `|x| foo[x]`
321
+ // the index is dropped so we can't get it to build the suggestion,
322
+ // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
323
+ // instead of `span.lo()` (i.e.: `foo`)
324
+ ProjectionKind :: Index => {
325
+ let start_span = Span :: new ( self . next_pos , span. hi ( ) , span. ctxt ( ) ) ;
326
+ start_snip = snippet_with_applicability ( self . cx , start_span, ".." , & mut self . applicability ) ;
327
+ replacement_str. clear ( ) ;
328
+ projections_handled = true ;
307
329
} ,
308
- // handled previously
309
- ProjectionKind :: Index |
310
- // note: unable to trigger `Subslice` kind in tests
311
- ProjectionKind :: Subslice => false ,
330
+ // note: unable to trigger `Subslice` kind in tests
331
+ ProjectionKind :: Subslice => ( ) ,
312
332
ProjectionKind :: Deref => {
313
333
// explicit deref for arrays should be avoided in the suggestion
314
334
// i.e.: `|sub| *sub[1..4].len() == 3` is not expected
315
- match cmt. place . ty_before_projection ( i) . kind ( ) {
335
+ if let ty :: Ref ( _ , inner , _ ) = cmt. place . ty_before_projection ( i) . kind ( ) {
316
336
// dereferencing an array (i.e.: `|sub| sub[1..4].len() == 3`)
317
- ty:: Ref ( _, inner, _) => {
318
- matches ! ( inner. kind( ) , ty:: Ref ( _, innermost, _) if innermost. is_array( ) )
319
- } ,
320
- _ => false ,
337
+ if matches ! ( inner. kind( ) , ty:: Ref ( _, innermost, _) if innermost. is_array( ) ) {
338
+ projections_handled = true ;
339
+ }
321
340
}
322
341
} ,
323
342
}
324
343
} ) ;
325
344
326
345
// handle `ProjectionKind::Deref` if no special case detected
327
- if !index_handled && !other_projections_handled {
346
+ if !projections_handled {
328
347
let last_deref = cmt
329
348
. place
330
349
. projections
0 commit comments