@@ -10,11 +10,10 @@ use rustc_hir::{
10
10
Pat , PatKind , UnOp ,
11
11
} ;
12
12
use rustc_lint:: { LateContext , LateLintPass } ;
13
- use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
13
+ use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
14
14
use rustc_middle:: ty:: { self , Ty , TyCtxt , TyS , TypeckResults } ;
15
15
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
16
16
use rustc_span:: { symbol:: sym, Span } ;
17
- use std:: iter;
18
17
19
18
declare_clippy_lint ! {
20
19
/// ### What it does
@@ -226,40 +225,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
226
225
let mut iter = find_adjustments ( cx. tcx , typeck, expr) . iter ( ) ;
227
226
if let Some ( ( i, adjust) ) = iter. by_ref ( ) . enumerate ( ) . find_map ( |( i, adjust) | {
228
227
if !matches ! ( adjust. kind, Adjust :: Deref ( _) ) {
229
- Some ( ( i, adjust) )
228
+ Some ( ( i, Some ( adjust) ) )
230
229
} else if !adjust. target . is_ref ( ) {
231
- // Add one to the number of references found .
232
- Some ( ( i + 1 , adjust ) )
230
+ // Include the current deref .
231
+ Some ( ( i + 1 , None ) )
233
232
} else {
234
233
None
235
234
}
236
235
} ) {
237
- // Found two consecutive derefs. At least one can be removed.
238
236
if i > 1 {
239
- let target_mut = iter:: once ( adjust)
240
- . chain ( iter)
241
- . find_map ( |adjust| match adjust. kind {
242
- Adjust :: Borrow ( AutoBorrow :: Ref ( _, m) ) => Some ( m. into ( ) ) ,
243
- _ => None ,
244
- } )
245
- // This default should never happen. Auto-deref always reborrows.
246
- . unwrap_or ( Mutability :: Not ) ;
247
- self . state = Some ( (
248
- // Subtract one for the current borrow expression, and one to cover the last
249
- // reference which can't be removed (it's either reborrowed, or needed for
250
- // auto-deref to happen).
251
- State :: DerefedBorrow {
237
+ // If the next adjustment is a mutable borrow, then check to see if the compiler will
238
+ // insert a re-borrow here. If not, leave an extra borrow here to avoid attempting to
239
+ // move the a mutable reference.
240
+ let ( i, target_mut) = if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
241
+ adjust. or_else ( || iter. next ( ) ) . map ( |a| & a. kind )
242
+ {
243
+ if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } )
244
+ && !is_auto_reborrow_position ( parent, expr. hir_id )
245
+ {
246
+ ( i - 1 , Mutability :: Mut )
247
+ } else {
248
+ ( i, mutability. into ( ) )
249
+ }
250
+ } else {
251
+ (
252
+ i,
253
+ iter. find_map ( |adjust| match adjust. kind {
254
+ Adjust :: Borrow ( AutoBorrow :: Ref ( _, m) ) => Some ( m. into ( ) ) ,
255
+ _ => None ,
256
+ } )
257
+ // This default should never happen. Auto-deref always reborrows.
258
+ . unwrap_or ( Mutability :: Not ) ,
259
+ )
260
+ } ;
261
+
262
+ if i > 1 {
263
+ self . state = Some ( (
264
+ // Subtract one for the current borrow expression, and one to cover the last
265
+ // reference which can't be removed (it's either reborrowed, or needed for
266
+ // auto-deref to happen).
267
+ State :: DerefedBorrow {
252
268
count :
253
269
// Truncation here would require more than a `u32::MAX` level reference. The compiler
254
270
// does not support this.
255
271
#[ allow ( clippy:: cast_possible_truncation) ]
256
272
{ i as u32 - 2 }
257
273
} ,
258
- StateData {
259
- span : expr. span ,
260
- target_mut,
261
- } ,
262
- ) ) ;
274
+ StateData {
275
+ span : expr. span ,
276
+ target_mut,
277
+ } ,
278
+ ) ) ;
279
+ }
263
280
}
264
281
}
265
282
} ,
@@ -456,6 +473,20 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
456
473
}
457
474
}
458
475
476
+ /// Checks if the given expression is in a position which can be auto-reborrowed.
477
+ /// Note: This is only correct assuming auto-deref is already occurring.
478
+ fn is_auto_reborrow_position ( parent : Option < Node < ' _ > > , child_id : HirId ) -> bool {
479
+ match parent {
480
+ Some ( Node :: Expr ( parent) ) => match parent. kind {
481
+ ExprKind :: MethodCall ( ..) => true ,
482
+ ExprKind :: Call ( callee, _) => callee. hir_id != child_id,
483
+ _ => false ,
484
+ } ,
485
+ Some ( Node :: Local ( _) ) => true ,
486
+ _ => false ,
487
+ }
488
+ }
489
+
459
490
/// Adjustments are sometimes made in the parent block rather than the expression itself.
460
491
fn find_adjustments < ' tcx > (
461
492
tcx : TyCtxt < ' tcx > ,
0 commit comments