@@ -11,11 +11,13 @@ use rustc_hir::{
11
11
ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath , TraitItem ,
12
12
TraitItemKind , TyKind , UnOp ,
13
13
} ;
14
+ use rustc_infer:: infer:: TyCtxtInferExt ;
14
15
use rustc_lint:: { LateContext , LateLintPass } ;
15
16
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
16
17
use rustc_middle:: ty:: { self , Ty , TyCtxt , TyS , TypeFoldable , TypeckResults } ;
17
18
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
18
19
use rustc_span:: { symbol:: sym, Span , Symbol } ;
20
+ use rustc_trait_selection:: infer:: InferCtxtExt ;
19
21
20
22
declare_clippy_lint ! {
21
23
/// ### What it does
@@ -155,7 +157,6 @@ pub struct Dereferencing {
155
157
156
158
struct DerefedBorrow {
157
159
count : usize ,
158
- required_precedence : i8 ,
159
160
msg : & ' static str ,
160
161
position : Position ,
161
162
}
@@ -312,19 +313,19 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
312
313
"this expression creates a reference which is immediately dereferenced by the compiler" ;
313
314
let borrow_msg = "this expression borrows a value the compiler would automatically borrow" ;
314
315
315
- let ( required_refs, required_precedence , msg) = if position. can_auto_borrow ( ) {
316
- ( 1 , PREC_POSTFIX , if deref_count == 1 { borrow_msg } else { deref_msg } )
316
+ let ( required_refs, msg) = if position. can_auto_borrow ( ) {
317
+ ( 1 , if deref_count == 1 { borrow_msg } else { deref_msg } )
317
318
} else if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
318
319
next_adjust. map ( |a| & a. kind )
319
320
{
320
321
if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } ) && !position. is_reborrow_stable ( )
321
322
{
322
- ( 3 , 0 , deref_msg)
323
+ ( 3 , deref_msg)
323
324
} else {
324
- ( 2 , 0 , deref_msg)
325
+ ( 2 , deref_msg)
325
326
}
326
327
} else {
327
- ( 2 , 0 , deref_msg)
328
+ ( 2 , deref_msg)
328
329
} ;
329
330
330
331
if deref_count >= required_refs {
@@ -333,7 +334,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
333
334
// One of the required refs is for the current borrow expression, the remaining ones
334
335
// can't be removed without breaking the code. See earlier comment.
335
336
count : deref_count - required_refs,
336
- required_precedence,
337
337
msg,
338
338
position,
339
339
} ) ,
@@ -542,6 +542,8 @@ fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool {
542
542
#[ derive( Clone , Copy ) ]
543
543
enum Position {
544
544
MethodReceiver ,
545
+ /// The method is defined on a reference type. e.g. `impl Foo for &T`
546
+ MethodReceiverRefImpl ,
545
547
Callee ,
546
548
FieldAccess ( Symbol ) ,
547
549
Postfix ,
@@ -568,6 +570,13 @@ impl Position {
568
570
fn lint_explicit_deref ( self ) -> bool {
569
571
matches ! ( self , Self :: Other | Self :: DerefStable | Self :: ReborrowStable )
570
572
}
573
+
574
+ fn needs_parens ( self , precedence : i8 ) -> bool {
575
+ matches ! (
576
+ self ,
577
+ Self :: MethodReceiver | Self :: MethodReceiverRefImpl | Self :: Callee | Self :: FieldAccess ( _) | Self :: Postfix
578
+ ) && precedence < PREC_POSTFIX
579
+ }
571
580
}
572
581
573
582
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
@@ -673,10 +682,34 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
673
682
let id = cx. typeck_results ( ) . type_dependent_def_id ( parent. hir_id ) . unwrap ( ) ;
674
683
args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
675
684
if i == 0 {
676
- if e. hir_id == child_id {
677
- Position :: MethodReceiver
678
- } else {
685
+ // Check for calls to trait methods where the trait is implemented on a reference.
686
+ // Two cases need to be handled:
687
+ // * `self` methods on `&T` will never have auto-borrow
688
+ // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
689
+ // priority.
690
+ if e. hir_id != child_id {
679
691
Position :: ReborrowStable
692
+ } else if let Some ( trait_id) = cx. tcx . trait_of_item ( id)
693
+ && let arg_ty = cx. tcx . erase_regions ( cx. typeck_results ( ) . expr_ty_adjusted ( e) )
694
+ && let ty:: Ref ( _, sub_ty, _) = * arg_ty. kind ( )
695
+ && let subs = cx. typeck_results ( ) . node_substs_opt ( child_id) . unwrap_or_else (
696
+ || cx. tcx . mk_substs ( [ ] . iter ( ) )
697
+ ) && let impl_ty = if cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) {
698
+ // Trait methods taking `&self`
699
+ sub_ty
700
+ } else {
701
+ // Trait methods taking `self`
702
+ arg_ty
703
+ } && impl_ty. is_ref ( )
704
+ && cx. tcx . infer_ctxt ( ) . enter ( |infcx|
705
+ infcx
706
+ . type_implements_trait ( trait_id, impl_ty, subs, cx. param_env )
707
+ . must_apply_modulo_regions ( )
708
+ )
709
+ {
710
+ Position :: MethodReceiverRefImpl
711
+ } else {
712
+ Position :: MethodReceiver
680
713
}
681
714
} else {
682
715
param_auto_deref_stability ( cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i] )
@@ -912,7 +945,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, span: Span) {
912
945
span,
913
946
state. msg ,
914
947
"change this to" ,
915
- if state. required_precedence > expr. precedence ( ) . order ( ) && !has_enclosing_paren ( & snip) {
948
+ if state. position . needs_parens ( expr. precedence ( ) . order ( ) ) && !has_enclosing_paren ( & snip) {
916
949
format ! ( "({})" , snip)
917
950
} else {
918
951
snip. into ( )
0 commit comments