@@ -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 , 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
@@ -165,7 +167,6 @@ struct StateData {
165
167
166
168
struct DerefedBorrow {
167
169
count : usize ,
168
- required_precedence : i8 ,
169
170
msg : & ' static str ,
170
171
position : Position ,
171
172
}
@@ -329,19 +330,19 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
329
330
"this expression creates a reference which is immediately dereferenced by the compiler" ;
330
331
let borrow_msg = "this expression borrows a value the compiler would automatically borrow" ;
331
332
332
- let ( required_refs, required_precedence , msg) = if position. can_auto_borrow ( ) {
333
- ( 1 , PREC_POSTFIX , if deref_count == 1 { borrow_msg } else { deref_msg } )
333
+ let ( required_refs, msg) = if position. can_auto_borrow ( ) {
334
+ ( 1 , if deref_count == 1 { borrow_msg } else { deref_msg } )
334
335
} else if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
335
336
next_adjust. map ( |a| & a. kind )
336
337
{
337
338
if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } ) && !position. is_reborrow_stable ( )
338
339
{
339
- ( 3 , 0 , deref_msg)
340
+ ( 3 , deref_msg)
340
341
} else {
341
- ( 2 , 0 , deref_msg)
342
+ ( 2 , deref_msg)
342
343
}
343
344
} else {
344
- ( 2 , 0 , deref_msg)
345
+ ( 2 , deref_msg)
345
346
} ;
346
347
347
348
if deref_count >= required_refs {
@@ -350,7 +351,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
350
351
// One of the required refs is for the current borrow expression, the remaining ones
351
352
// can't be removed without breaking the code. See earlier comment.
352
353
count : deref_count - required_refs,
353
- required_precedence,
354
354
msg,
355
355
position,
356
356
} ) ,
@@ -601,6 +601,8 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
601
601
#[ derive( Clone , Copy ) ]
602
602
enum Position {
603
603
MethodReceiver ,
604
+ /// The method is defined on a reference type. e.g. `impl Foo for &T`
605
+ MethodReceiverRefImpl ,
604
606
Callee ,
605
607
FieldAccess ( Symbol ) ,
606
608
Postfix ,
@@ -627,6 +629,13 @@ impl Position {
627
629
fn lint_explicit_deref ( self ) -> bool {
628
630
matches ! ( self , Self :: Other | Self :: DerefStable | Self :: ReborrowStable )
629
631
}
632
+
633
+ fn needs_parens ( self , precedence : i8 ) -> bool {
634
+ matches ! (
635
+ self ,
636
+ Self :: MethodReceiver | Self :: MethodReceiverRefImpl | Self :: Callee | Self :: FieldAccess ( _) | Self :: Postfix
637
+ ) && precedence < PREC_POSTFIX
638
+ }
630
639
}
631
640
632
641
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
@@ -730,10 +739,34 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
730
739
let id = cx. typeck_results ( ) . type_dependent_def_id ( parent. hir_id ) . unwrap ( ) ;
731
740
args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
732
741
if i == 0 {
733
- if e. hir_id == child_id {
734
- Position :: MethodReceiver
735
- } else {
742
+ // Check for calls to trait methods where the trait is implemented on a reference.
743
+ // Two cases need to be handled:
744
+ // * `self` methods on `&T` will never have auto-borrow
745
+ // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
746
+ // priority.
747
+ if e. hir_id != child_id {
736
748
Position :: ReborrowStable
749
+ } else if let Some ( trait_id) = cx. tcx . trait_of_item ( id)
750
+ && let arg_ty = cx. tcx . erase_regions ( cx. typeck_results ( ) . expr_ty_adjusted ( e) )
751
+ && let ty:: Ref ( _, sub_ty, _) = * arg_ty. kind ( )
752
+ && let subs = cx. typeck_results ( ) . node_substs_opt ( child_id) . unwrap_or_else (
753
+ || cx. tcx . mk_substs ( [ ] . iter ( ) )
754
+ ) && let impl_ty = if cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) {
755
+ // Trait methods taking `&self`
756
+ sub_ty
757
+ } else {
758
+ // Trait methods taking `self`
759
+ arg_ty
760
+ } && impl_ty. is_ref ( )
761
+ && cx. tcx . infer_ctxt ( ) . enter ( |infcx|
762
+ infcx
763
+ . type_implements_trait ( trait_id, impl_ty, subs, cx. param_env )
764
+ . must_apply_modulo_regions ( )
765
+ )
766
+ {
767
+ Position :: MethodReceiverRefImpl
768
+ } else {
769
+ Position :: MethodReceiver
737
770
}
738
771
} else {
739
772
param_auto_deref_stability ( cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i] )
@@ -964,7 +997,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
964
997
let mut app = Applicability :: MachineApplicable ;
965
998
let snip = snippet_with_context ( cx, expr. span , data. span . ctxt ( ) , ".." , & mut app) . 0 ;
966
999
span_lint_hir_and_then ( cx, NEEDLESS_BORROW , data. hir_id , data. span , state. msg , |diag| {
967
- let sugg = if state. required_precedence > expr. precedence ( ) . order ( ) && !has_enclosing_paren ( & snip) {
1000
+ let sugg = if state. position . needs_parens ( expr. precedence ( ) . order ( ) ) && !has_enclosing_paren ( & snip) {
968
1001
format ! ( "({})" , snip)
969
1002
} else {
970
1003
snip. into ( )
0 commit comments