@@ -130,8 +130,6 @@ pub struct Dereferencing {
130
130
struct StateData {
131
131
/// Span of the top level expression
132
132
span : Span ,
133
- /// The required mutability
134
- target_mut : Mutability ,
135
133
}
136
134
137
135
enum State {
@@ -140,9 +138,11 @@ enum State {
140
138
// The number of calls in a sequence which changed the referenced type
141
139
ty_changed_count : usize ,
142
140
is_final_ufcs : bool ,
141
+ /// The required mutability
142
+ target_mut : Mutability ,
143
143
} ,
144
144
DerefedBorrow {
145
- count : u32 ,
145
+ count : usize ,
146
146
} ,
147
147
}
148
148
@@ -213,77 +213,76 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
213
213
1
214
214
} ,
215
215
is_final_ufcs : matches ! ( expr. kind, ExprKind :: Call ( ..) ) ,
216
- } ,
217
- StateData {
218
- span : expr. span ,
219
216
target_mut,
220
217
} ,
218
+ StateData { span : expr. span } ,
221
219
) ) ;
222
220
} ,
223
221
RefOp :: AddrOf => {
224
222
// Find the number of times the borrow is auto-derefed.
225
223
let mut iter = find_adjustments ( cx. tcx , typeck, expr) . iter ( ) ;
226
- if let Some ( ( i, adjust) ) = iter. by_ref ( ) . enumerate ( ) . find_map ( |( i, adjust) | {
227
- if !matches ! ( adjust. kind, Adjust :: Deref ( _) ) {
228
- Some ( ( i, Some ( adjust) ) )
229
- } else if !adjust. target . is_ref ( ) {
230
- // Include the current deref.
231
- Some ( ( i + 1 , None ) )
232
- } else {
233
- None
234
- }
235
- } ) {
236
- if i > 1 {
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 )
224
+ let mut deref_count = 0usize ;
225
+ let next_adjust = loop {
226
+ match iter. next ( ) {
227
+ Some ( adjust) => {
228
+ if !matches ! ( adjust. kind, Adjust :: Deref ( _) ) {
229
+ break Some ( adjust) ;
230
+ } else if !adjust. target . is_ref ( ) {
231
+ deref_count += 1 ;
232
+ break iter. next ( ) ;
247
233
} else {
248
- ( i , mutability . into ( ) )
234
+ deref_count += 1 ;
249
235
}
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 {
268
- count :
269
- // Truncation here would require more than a `u32::MAX` level reference. The compiler
270
- // does not support this.
271
- #[ allow ( clippy:: cast_possible_truncation) ]
272
- { i as u32 - 2 }
273
- } ,
274
- StateData {
275
- span : expr. span ,
276
- target_mut,
277
- } ,
278
- ) ) ;
279
- }
236
+ } ,
237
+ None => break None ,
238
+ } ;
239
+ } ;
240
+
241
+ let required_derefs: usize = if is_member_access ( parent, expr. hir_id ) {
242
+ // Neither field accesses nor method calls require a borrow to trigger auto-deref.
243
+ 1
244
+ } else if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
245
+ next_adjust. map ( |a| & a. kind )
246
+ {
247
+ // If the next adjustment is a mutable borrow, then check to see if the compiler will
248
+ // insert a re-borrow here. If not, leave an extra borrow here to avoid attempting to
249
+ // move the mutable reference.
250
+ if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } )
251
+ && !is_auto_reborrow_position ( parent, expr. hir_id )
252
+ {
253
+ deref_count = deref_count. saturating_sub ( 1 ) ;
280
254
}
255
+ 2
256
+ } else {
257
+ 2
258
+ } ;
259
+
260
+ if deref_count >= required_derefs {
261
+ self . state = Some ( (
262
+ // Subtract one for the current borrow expression, and, optionally, one to cover the
263
+ // last reference which can't be removed (it's either reborrowed, or needed for
264
+ // auto-deref to happen).
265
+ State :: DerefedBorrow {
266
+ count : deref_count - required_derefs,
267
+ } ,
268
+ StateData { span : expr. span } ,
269
+ ) ) ;
281
270
}
282
271
} ,
283
272
_ => ( ) ,
284
273
}
285
274
} ,
286
- ( Some ( ( State :: DerefMethod { ty_changed_count, .. } , data) ) , RefOp :: Method ( _) ) => {
275
+ (
276
+ Some ( (
277
+ State :: DerefMethod {
278
+ target_mut,
279
+ ty_changed_count,
280
+ ..
281
+ } ,
282
+ data,
283
+ ) ) ,
284
+ RefOp :: Method ( _) ,
285
+ ) => {
287
286
self . state = Some ( (
288
287
State :: DerefMethod {
289
288
ty_changed_count : if deref_method_same_type ( typeck. expr_ty ( expr) , typeck. expr_ty ( sub_expr) ) {
@@ -292,6 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
292
291
ty_changed_count + 1
293
292
} ,
294
293
is_final_ufcs : matches ! ( expr. kind, ExprKind :: Call ( ..) ) ,
294
+ target_mut,
295
295
} ,
296
296
data,
297
297
) ) ;
@@ -487,6 +487,19 @@ fn is_auto_reborrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool
487
487
}
488
488
}
489
489
490
+ /// Checks if the given expression is a method of field access.
491
+ fn is_member_access ( parent : Option < Node < ' _ > > , child_id : HirId ) -> bool {
492
+ if let Some ( Node :: Expr ( parent) ) = parent {
493
+ match parent. kind {
494
+ ExprKind :: MethodCall ( _, _, [ self_arg, ..] , _) => self_arg. hir_id == child_id,
495
+ ExprKind :: Field ( ..) => true ,
496
+ _ => false ,
497
+ }
498
+ } else {
499
+ false
500
+ }
501
+ }
502
+
490
503
/// Adjustments are sometimes made in the parent block rather than the expression itself.
491
504
fn find_adjustments (
492
505
tcx : TyCtxt < ' tcx > ,
@@ -535,6 +548,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
535
548
State :: DerefMethod {
536
549
ty_changed_count,
537
550
is_final_ufcs,
551
+ target_mut,
538
552
} => {
539
553
let mut app = Applicability :: MachineApplicable ;
540
554
let ( expr_str, expr_is_macro_call) = snippet_with_context ( cx, expr. span , data. span . ctxt ( ) , ".." , & mut app) ;
@@ -549,12 +563,12 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
549
563
} ;
550
564
let addr_of_str = if ty_changed_count < ref_count {
551
565
// Check if a reborrow from &mut T -> &T is required.
552
- if data . target_mut == Mutability :: Not && matches ! ( ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) ) {
566
+ if target_mut == Mutability :: Not && matches ! ( ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) ) {
553
567
"&*"
554
568
} else {
555
569
""
556
570
}
557
- } else if data . target_mut == Mutability :: Mut {
571
+ } else if target_mut == Mutability :: Mut {
558
572
"&mut "
559
573
} else {
560
574
"&"
@@ -570,7 +584,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
570
584
cx,
571
585
EXPLICIT_DEREF_METHODS ,
572
586
data. span ,
573
- match data . target_mut {
587
+ match target_mut {
574
588
Mutability :: Not => "explicit `deref` method call" ,
575
589
Mutability :: Mut => "explicit `deref_mut` method call" ,
576
590
} ,
0 commit comments