@@ -10,11 +10,10 @@ use rustc_hir::{
1010 Pat , PatKind , UnOp ,
1111} ;
1212use rustc_lint:: { LateContext , LateLintPass } ;
13- use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
13+ use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
1414use rustc_middle:: ty:: { self , Ty , TyCtxt , TyS , TypeckResults } ;
1515use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1616use rustc_span:: { symbol:: sym, Span } ;
17- use std:: iter;
1817
1918declare_clippy_lint ! {
2019 /// ### What it does
@@ -226,40 +225,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
226225 let mut iter = find_adjustments ( cx. tcx , typeck, expr) . iter ( ) ;
227226 if let Some ( ( i, adjust) ) = iter. by_ref ( ) . enumerate ( ) . find_map ( |( i, adjust) | {
228227 if !matches ! ( adjust. kind, Adjust :: Deref ( _) ) {
229- Some ( ( i, adjust) )
228+ Some ( ( i, Some ( adjust) ) )
230229 } 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 ) )
233232 } else {
234233 None
235234 }
236235 } ) {
237- // Found two consecutive derefs. At least one can be removed.
238236 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 {
252268 count :
253269 // Truncation here would require more than a `u32::MAX` level reference. The compiler
254270 // does not support this.
255271 #[ allow ( clippy:: cast_possible_truncation) ]
256272 { i as u32 - 2 }
257273 } ,
258- StateData {
259- span : expr. span ,
260- target_mut,
261- } ,
262- ) ) ;
274+ StateData {
275+ span : expr. span ,
276+ target_mut,
277+ } ,
278+ ) ) ;
279+ }
263280 }
264281 }
265282 } ,
@@ -456,6 +473,20 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
456473 }
457474}
458475
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+
459490/// Adjustments are sometimes made in the parent block rather than the expression itself.
460491fn find_adjustments < ' tcx > (
461492 tcx : TyCtxt < ' tcx > ,
0 commit comments