1+ use std:: iter;
2+
13use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
24use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
35use clippy_utils:: ty:: is_type_diagnostic_item;
@@ -223,6 +225,26 @@ impl DerefDelegate<'_, 'tcx> {
223225 let end_snip = snippet_with_applicability ( self . cx , end_span, ".." , & mut self . applicability ) ;
224226 format ! ( "{}{}" , self . suggestion_start, end_snip)
225227 }
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+ }
226248}
227249
228250impl < ' tcx > Delegate < ' tcx > for DerefDelegate < ' _ , ' tcx > {
@@ -252,42 +274,32 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
252274 let start_span = Span :: new ( self . next_pos , span. lo ( ) , span. ctxt ( ) ) ;
253275 let start_snip =
254276 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+
255281 // 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+ } ;
261288 self . suggestion_start . push_str ( & ident_sugg) ;
262289 self . next_pos = span. hi ( ) ;
263290 return ;
264- } else {
265- self . applicability = Applicability :: Unspecified ;
266291 }
292+
293+ self . applicability = Applicability :: Unspecified ;
267294 }
268295 }
269296
270297 // handle item projections by removing one explicit deref
271298 // i.e.: suggest `*x` instead of `**x`
272299 let mut replacement_str = ident_str;
273300
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) | {
291303 match proj. kind {
292304 // Field projection like `|v| v.foo`
293305 ProjectionKind :: Field ( idx, variant) => match cmt. place . ty_before_projection ( i) . kind ( ) {
@@ -297,34 +309,41 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
297309 replacement_str,
298310 def. variants[ variant] . fields[ idx as usize ] . ident. name. as_str( )
299311 ) ;
300- true
312+ projections_handled = true ;
301313 } ,
302314 ty:: Tuple ( _) => {
303315 replacement_str = format ! ( "{}.{}" , replacement_str, idx) ;
304- true
316+ projections_handled = true ;
305317 } ,
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 ;
307329 } ,
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 => ( ) ,
312332 ProjectionKind :: Deref => {
313333 // explicit deref for arrays should be avoided in the suggestion
314334 // 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 ( ) {
316336 // 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+ }
321340 }
322341 } ,
323342 }
324343 } ) ;
325344
326345 // handle `ProjectionKind::Deref` if no special case detected
327- if !index_handled && !other_projections_handled {
346+ if !projections_handled {
328347 let last_deref = cmt
329348 . place
330349 . projections
0 commit comments