@@ -11,11 +11,13 @@ use rustc_hir::{
1111 ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath , TraitItem ,
1212 TraitItemKind , TyKind , UnOp ,
1313} ;
14+ use rustc_infer:: infer:: TyCtxtInferExt ;
1415use rustc_lint:: { LateContext , LateLintPass } ;
1516use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
1617use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeFoldable , TypeckResults } ;
1718use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1819use rustc_span:: { symbol:: sym, Span , Symbol } ;
20+ use rustc_trait_selection:: infer:: InferCtxtExt ;
1921
2022declare_clippy_lint ! {
2123 /// ### What it does
@@ -165,7 +167,6 @@ struct StateData {
165167
166168struct DerefedBorrow {
167169 count : usize ,
168- required_precedence : i8 ,
169170 msg : & ' static str ,
170171 position : Position ,
171172}
@@ -329,19 +330,19 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
329330 "this expression creates a reference which is immediately dereferenced by the compiler" ;
330331 let borrow_msg = "this expression borrows a value the compiler would automatically borrow" ;
331332
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 } )
334335 } else if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
335336 next_adjust. map ( |a| & a. kind )
336337 {
337338 if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } ) && !position. is_reborrow_stable ( )
338339 {
339- ( 3 , 0 , deref_msg)
340+ ( 3 , deref_msg)
340341 } else {
341- ( 2 , 0 , deref_msg)
342+ ( 2 , deref_msg)
342343 }
343344 } else {
344- ( 2 , 0 , deref_msg)
345+ ( 2 , deref_msg)
345346 } ;
346347
347348 if deref_count >= required_refs {
@@ -350,7 +351,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
350351 // One of the required refs is for the current borrow expression, the remaining ones
351352 // can't be removed without breaking the code. See earlier comment.
352353 count : deref_count - required_refs,
353- required_precedence,
354354 msg,
355355 position,
356356 } ) ,
@@ -601,6 +601,8 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
601601#[ derive( Clone , Copy ) ]
602602enum Position {
603603 MethodReceiver ,
604+ /// The method is defined on a reference type. e.g. `impl Foo for &T`
605+ MethodReceiverRefImpl ,
604606 Callee ,
605607 FieldAccess ( Symbol ) ,
606608 Postfix ,
@@ -627,6 +629,13 @@ impl Position {
627629 fn lint_explicit_deref ( self ) -> bool {
628630 matches ! ( self , Self :: Other | Self :: DerefStable | Self :: ReborrowStable )
629631 }
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+ }
630639}
631640
632641/// 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, &
730739 let id = cx. typeck_results ( ) . type_dependent_def_id ( parent. hir_id ) . unwrap ( ) ;
731740 args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
732741 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 {
736748 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
737770 }
738771 } else {
739772 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
964997 let mut app = Applicability :: MachineApplicable ;
965998 let snip = snippet_with_context ( cx, expr. span , data. span . ctxt ( ) , ".." , & mut app) . 0 ;
966999 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) {
9681001 format ! ( "({})" , snip)
9691002 } else {
9701003 snip. into ( )
0 commit comments