11use  clippy_utils:: diagnostics:: { span_lint_and_sugg,  span_lint_and_then} ; 
22use  clippy_utils:: source:: { snippet_with_applicability,  snippet_with_context} ; 
3+ use  clippy_utils:: sugg:: has_enclosing_paren; 
34use  clippy_utils:: ty:: peel_mid_ty_refs; 
45use  clippy_utils:: { get_parent_expr,  get_parent_node,  is_lint_allowed,  path_to_local} ; 
56use  rustc_ast:: util:: parser:: { PREC_POSTFIX ,  PREC_PREFIX } ; 
@@ -10,11 +11,10 @@ use rustc_hir::{
1011 Pat ,  PatKind ,  UnOp , 
1112} ; 
1213use  rustc_lint:: { LateContext ,  LateLintPass } ; 
13- use  rustc_middle:: ty:: adjustment:: { Adjust ,  Adjustment ,  AutoBorrow } ; 
14+ use  rustc_middle:: ty:: adjustment:: { Adjust ,  Adjustment ,  AutoBorrow ,   AutoBorrowMutability } ; 
1415use  rustc_middle:: ty:: { self ,  Ty ,  TyCtxt ,  TyS ,  TypeckResults } ; 
1516use  rustc_session:: { declare_tool_lint,  impl_lint_pass} ; 
1617use  rustc_span:: { symbol:: sym,  Span } ; 
17- use  std:: iter; 
1818
1919declare_clippy_lint !  { 
2020 /// ### What it does 
@@ -131,8 +131,6 @@ pub struct Dereferencing {
131131struct  StateData  { 
132132 /// Span of the top level expression 
133133  span :  Span , 
134-  /// The required mutability 
135-   target_mut :  Mutability , 
136134} 
137135
138136enum  State  { 
@@ -141,9 +139,13 @@ enum State {
141139 // The number of calls in a sequence which changed the referenced type 
142140 ty_changed_count :  usize , 
143141 is_final_ufcs :  bool , 
142+  /// The required mutability 
143+   target_mut :  Mutability , 
144144 } , 
145145 DerefedBorrow  { 
146-  count :  u32 , 
146+  count :  usize , 
147+  required_precedence :  i8 , 
148+  msg :  & ' static  str , 
147149 } , 
148150} 
149151
@@ -214,59 +216,98 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
214216 1 
215217 } , 
216218 is_final_ufcs :  matches ! ( expr. kind,  ExprKind :: Call ( ..) ) , 
217-  } , 
218-  StateData  { 
219-  span :  expr. span , 
220219 target_mut, 
221220 } , 
221+  StateData  {  span :  expr. span  } , 
222222 ) ) ; 
223223 } , 
224224 RefOp :: AddrOf  => { 
225225 // Find the number of times the borrow is auto-derefed. 
226226 let  mut  iter = find_adjustments ( cx. tcx ,  typeck,  expr) . iter ( ) ; 
227-  if  let  Some ( ( i,  adjust) )  = iter. by_ref ( ) . enumerate ( ) . find_map ( |( i,  adjust) | { 
228-  if  !matches ! ( adjust. kind,  Adjust :: Deref ( _) )  { 
229-  Some ( ( i,  adjust) ) 
230-  }  else  if  !adjust. target . is_ref ( )  { 
231-  // Add one to the number of references found. 
232-  Some ( ( i + 1 ,  adjust) ) 
227+  let  mut  deref_count = 0usize ; 
228+  let  next_adjust = loop  { 
229+  match  iter. next ( )  { 
230+  Some ( adjust)  => { 
231+  if  !matches ! ( adjust. kind,  Adjust :: Deref ( _) )  { 
232+  break  Some ( adjust) ; 
233+  }  else  if  !adjust. target . is_ref ( )  { 
234+  deref_count += 1 ; 
235+  break  iter. next ( ) ; 
236+  } 
237+  deref_count += 1 ; 
238+  } , 
239+  None  => break  None , 
240+  } ; 
241+  } ; 
242+ 
243+  // Determine the required number of references before any can be removed. In all cases the 
244+  // reference made by the current expression will be removed. After that there are four cases to 
245+  // handle. 
246+  // 
247+  // 1. Auto-borrow will trigger in the current position, so no further references are required. 
248+  // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to 
249+  // handle the automatically inserted re-borrow. 
250+  // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to 
251+  // start auto-deref. 
252+  // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow 
253+  // adjustments will not be inserted automatically, then leave one further reference to avoid 
254+  // moving a mutable borrow. 
255+  // e.g. 
256+  // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) { 
257+  // let x = match x { 
258+  // // Removing the borrow will cause `x` to be moved 
259+  // Some(x) => &mut *x, 
260+  // None => y 
261+  // }; 
262+  // } 
263+  let  deref_msg =
264+  "this expression creates a reference which is immediately dereferenced by the compiler" ; 
265+  let  borrow_msg = "this expression borrows a value the compiler would automatically borrow" ; 
266+ 
267+  let  ( required_refs,  required_precedence,  msg)  = if  is_auto_borrow_position ( parent,  expr. hir_id ) 
268+  { 
269+  ( 1 ,  PREC_POSTFIX ,  if  deref_count == 1  {  borrow_msg }  else  {  deref_msg } ) 
270+  }  else  if  let  Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _,  mutability) ) )  =
271+  next_adjust. map ( |a| & a. kind ) 
272+  { 
273+  if  matches ! ( mutability,  AutoBorrowMutability :: Mut  {  .. } ) 
274+  && !is_auto_reborrow_position ( parent) 
275+  { 
276+  ( 3 ,  0 ,  deref_msg) 
233277 }  else  { 
234-  None 
235-  } 
236-  } )  { 
237-  // Found two consecutive derefs. At least one can be removed. 
238-  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  { 
252-  count : 
253-  // Truncation here would require more than a `u32::MAX` level reference. The compiler 
254-  // does not support this. 
255-  #[ allow ( clippy:: cast_possible_truncation) ] 
256-  {  i as  u32  - 2  } 
257-  } , 
258-  StateData  { 
259-  span :  expr. span , 
260-  target_mut, 
261-  } , 
262-  ) ) ; 
278+  ( 2 ,  0 ,  deref_msg) 
263279 } 
280+  }  else  { 
281+  ( 2 ,  0 ,  deref_msg) 
282+  } ; 
283+ 
284+  if  deref_count >= required_refs { 
285+  self . state  = Some ( ( 
286+  State :: DerefedBorrow  { 
287+  // One of the required refs is for the current borrow expression, the remaining ones 
288+  // can't be removed without breaking the code. See earlier comment. 
289+  count :  deref_count - required_refs, 
290+  required_precedence, 
291+  msg, 
292+  } , 
293+  StateData  {  span :  expr. span  } , 
294+  ) ) ; 
264295 } 
265296 } , 
266297 _ => ( ) , 
267298 } 
268299 } , 
269-  ( Some ( ( State :: DerefMethod  {  ty_changed_count,  .. } ,  data) ) ,  RefOp :: Method ( _) )  => { 
300+  ( 
301+  Some ( ( 
302+  State :: DerefMethod  { 
303+  target_mut, 
304+  ty_changed_count, 
305+  ..
306+  } , 
307+  data, 
308+  ) ) , 
309+  RefOp :: Method ( _) , 
310+  )  => { 
270311 self . state  = Some ( ( 
271312 State :: DerefMethod  { 
272313 ty_changed_count :  if  deref_method_same_type ( typeck. expr_ty ( expr) ,  typeck. expr_ty ( sub_expr) )  { 
@@ -275,12 +316,30 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
275316 ty_changed_count + 1 
276317 } , 
277318 is_final_ufcs :  matches ! ( expr. kind,  ExprKind :: Call ( ..) ) , 
319+  target_mut, 
278320 } , 
279321 data, 
280322 ) ) ; 
281323 } , 
282-  ( Some ( ( State :: DerefedBorrow  {  count } ,  data) ) ,  RefOp :: AddrOf )  if  count != 0  => { 
283-  self . state  = Some ( ( State :: DerefedBorrow  {  count :  count - 1  } ,  data) ) ; 
324+  ( 
325+  Some ( ( 
326+  State :: DerefedBorrow  { 
327+  count, 
328+  required_precedence, 
329+  msg, 
330+  } , 
331+  data, 
332+  ) ) , 
333+  RefOp :: AddrOf , 
334+  )  if  count != 0  => { 
335+  self . state  = Some ( ( 
336+  State :: DerefedBorrow  { 
337+  count :  count - 1 , 
338+  required_precedence, 
339+  msg, 
340+  } , 
341+  data, 
342+  ) ) ; 
284343 } , 
285344
286345 ( Some ( ( state,  data) ) ,  _)  => report ( cx,  expr,  state,  data) , 
@@ -455,6 +514,30 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
455514 } 
456515} 
457516
517+ /// Checks if the given expression is in a position which can be auto-reborrowed. 
518+ /// Note: This is only correct assuming auto-deref is already occurring. 
519+ fn  is_auto_reborrow_position ( parent :  Option < Node < ' _ > > )  -> bool  { 
520+  match  parent { 
521+  Some ( Node :: Expr ( parent) )  => matches ! ( parent. kind,  ExprKind :: MethodCall ( ..)  | ExprKind :: Call ( ..) ) , 
522+  Some ( Node :: Local ( _) )  => true , 
523+  _ => false , 
524+  } 
525+ } 
526+ 
527+ /// Checks if the given expression is a position which can auto-borrow. 
528+ fn  is_auto_borrow_position ( parent :  Option < Node < ' _ > > ,  child_id :  HirId )  -> bool  { 
529+  if  let  Some ( Node :: Expr ( parent) )  = parent { 
530+  match  parent. kind  { 
531+  ExprKind :: MethodCall ( _,  [ self_arg,  ..] ,  _)  => self_arg. hir_id  == child_id, 
532+  ExprKind :: Field ( ..)  => true , 
533+  ExprKind :: Call ( f,  _)  => f. hir_id  == child_id, 
534+  _ => false , 
535+  } 
536+  }  else  { 
537+  false 
538+  } 
539+ } 
540+ 
458541/// Adjustments are sometimes made in the parent block rather than the expression itself. 
459542fn  find_adjustments < ' tcx > ( 
460543 tcx :  TyCtxt < ' tcx > , 
@@ -503,6 +586,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
503586 State :: DerefMethod  { 
504587 ty_changed_count, 
505588 is_final_ufcs, 
589+  target_mut, 
506590 }  => { 
507591 let  mut  app = Applicability :: MachineApplicable ; 
508592 let  ( expr_str,  expr_is_macro_call)  = snippet_with_context ( cx,  expr. span ,  data. span . ctxt ( ) ,  ".." ,  & mut  app) ; 
@@ -517,12 +601,12 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
517601 } ; 
518602 let  addr_of_str = if  ty_changed_count < ref_count { 
519603 // Check if a reborrow from &mut T -> &T is required. 
520-  if  data . target_mut  == Mutability :: Not  && matches ! ( ty. kind( ) ,  ty:: Ref ( _,  _,  Mutability :: Mut ) )  { 
604+  if  target_mut == Mutability :: Not  && matches ! ( ty. kind( ) ,  ty:: Ref ( _,  _,  Mutability :: Mut ) )  { 
521605 "&*" 
522606 }  else  { 
523607 "" 
524608 } 
525-  }  else  if  data . target_mut  == Mutability :: Mut  { 
609+  }  else  if  target_mut == Mutability :: Mut  { 
526610 "&mut " 
527611 }  else  { 
528612 "&" 
@@ -538,7 +622,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
538622 cx, 
539623 EXPLICIT_DEREF_METHODS , 
540624 data. span , 
541-  match  data . target_mut  { 
625+  match  target_mut { 
542626 Mutability :: Not  => "explicit `deref` method call" , 
543627 Mutability :: Mut  => "explicit `deref_mut` method call" , 
544628 } , 
@@ -547,19 +631,24 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
547631 app, 
548632 ) ; 
549633 } , 
550-  State :: DerefedBorrow  {  .. }  => { 
634+  State :: DerefedBorrow  { 
635+  required_precedence, 
636+  msg, 
637+  ..
638+  }  => { 
551639 let  mut  app = Applicability :: MachineApplicable ; 
552640 let  snip = snippet_with_context ( cx,  expr. span ,  data. span . ctxt ( ) ,  ".." ,  & mut  app) . 0 ; 
553641 span_lint_and_sugg ( 
554642 cx, 
555643 NEEDLESS_BORROW , 
556644 data. span , 
557-  & format ! ( 
558-  "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler" , 
559-  cx. typeck_results( ) . expr_ty( expr) , 
560-  ) , 
645+  msg, 
561646 "change this to" , 
562-  snip. into ( ) , 
647+  if  required_precedence > expr. precedence ( ) . order ( )  && !has_enclosing_paren ( & snip)  { 
648+  format ! ( "({})" ,  snip) 
649+  }  else  { 
650+  snip. into ( ) 
651+  } , 
563652 app, 
564653 ) ; 
565654 } , 
0 commit comments