1
1
use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
2
2
use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
3
3
use clippy_utils:: sugg:: has_enclosing_paren;
4
- use clippy_utils:: ty:: { expr_sig, peel_mid_ty_refs, variant_of_res} ;
5
- use clippy_utils:: { get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage} ;
4
+ use clippy_utils:: ty:: { contains_ty , expr_sig, implements_trait , is_copy , peel_mid_ty_refs, variant_of_res} ;
5
+ use clippy_utils:: { fn_def_id , get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage} ;
6
6
use rustc_ast:: util:: parser:: { PREC_POSTFIX , PREC_PREFIX } ;
7
7
use rustc_data_structures:: fx:: FxIndexMap ;
8
8
use rustc_errors:: Applicability ;
@@ -15,7 +15,10 @@ use rustc_hir::{
15
15
use rustc_infer:: infer:: TyCtxtInferExt ;
16
16
use rustc_lint:: { LateContext , LateLintPass } ;
17
17
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
18
- use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitable , TypeckResults } ;
18
+ use rustc_middle:: ty:: {
19
+ self , subst:: GenericArgKind , PredicateKind , TraitPredicate , TraitRef , Ty , TyCtxt , TypeFoldable , TypeVisitable ,
20
+ TypeckResults , TypeckResults ,
21
+ } ;
19
22
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
20
23
use rustc_span:: { symbol:: sym, Span , Symbol } ;
21
24
use rustc_trait_selection:: infer:: InferCtxtExt ;
@@ -170,6 +173,7 @@ struct StateData {
170
173
struct DerefedBorrow {
171
174
count : usize ,
172
175
msg : & ' static str ,
176
+ snip_expr : Option < HirId > ,
173
177
}
174
178
175
179
enum State {
@@ -331,20 +335,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
331
335
let deref_msg =
332
336
"this expression creates a reference which is immediately dereferenced by the compiler" ;
333
337
let borrow_msg = "this expression borrows a value the compiler would automatically borrow" ;
338
+ let impl_msg = "the borrowed expression implements the required traits" ;
334
339
335
- let ( required_refs, msg) = if position. can_auto_borrow ( ) {
336
- ( 1 , if deref_count == 1 { borrow_msg } else { deref_msg } )
340
+ let ( required_refs, msg, snip_expr) = if position. can_auto_borrow ( ) {
341
+ ( 1 , if deref_count == 1 { borrow_msg } else { deref_msg } , None )
342
+ } else if let Position :: ImplArg ( hir_id) = position {
343
+ ( 0 , impl_msg, Some ( hir_id) )
337
344
} else if let Some ( & Adjust :: Borrow ( AutoBorrow :: Ref ( _, mutability) ) ) =
338
345
next_adjust. map ( |a| & a. kind )
339
346
{
340
347
if matches ! ( mutability, AutoBorrowMutability :: Mut { .. } ) && !position. is_reborrow_stable ( )
341
348
{
342
- ( 3 , deref_msg)
349
+ ( 3 , deref_msg, None )
343
350
} else {
344
- ( 2 , deref_msg)
351
+ ( 2 , deref_msg, None )
345
352
}
346
353
} else {
347
- ( 2 , deref_msg)
354
+ ( 2 , deref_msg, None )
348
355
} ;
349
356
350
357
if deref_count >= required_refs {
@@ -354,6 +361,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
354
361
// can't be removed without breaking the code. See earlier comment.
355
362
count : deref_count - required_refs,
356
363
msg,
364
+ snip_expr,
357
365
} ) ,
358
366
StateData { span : expr. span , hir_id : expr. hir_id , position } ,
359
367
) ) ;
@@ -521,7 +529,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
521
529
spans: vec![ pat. span] ,
522
530
app,
523
531
replacements: vec![ ( pat. span, snip. into( ) ) ] ,
524
- hir_id: pat. hir_id
532
+ hir_id: pat. hir_id,
525
533
} ) ,
526
534
) ;
527
535
}
@@ -605,6 +613,7 @@ enum Position {
605
613
/// The method is defined on a reference type. e.g. `impl Foo for &T`
606
614
MethodReceiverRefImpl ,
607
615
Callee ,
616
+ ImplArg ( HirId ) ,
608
617
FieldAccess ( Symbol ) ,
609
618
Postfix ,
610
619
Deref ,
@@ -638,7 +647,7 @@ impl Position {
638
647
| Self :: Callee
639
648
| Self :: FieldAccess ( _)
640
649
| Self :: Postfix => PREC_POSTFIX ,
641
- Self :: Deref => PREC_PREFIX ,
650
+ Self :: ImplArg ( _ ) | Self :: Deref => PREC_PREFIX ,
642
651
Self :: DerefStable ( p) | Self :: ReborrowStable ( p) | Self :: Other ( p) => p,
643
652
}
644
653
}
@@ -751,12 +760,14 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
751
760
. iter ( )
752
761
. position ( |arg| arg. hir_id == child_id)
753
762
. zip ( expr_sig ( cx, func) )
754
- . and_then ( |( i, sig) | sig. input_with_hir ( i) )
755
- . map ( |( hir_ty, ty) | match hir_ty {
756
- // Type inference for closures can depend on how they're called. Only go by the explicit
757
- // types here.
758
- Some ( ty) => binding_ty_auto_deref_stability ( ty, precedence) ,
759
- None => param_auto_deref_stability ( ty. skip_binder ( ) , precedence) ,
763
+ . and_then ( |( i, sig) | {
764
+ sig. input_with_hir ( i) . map ( |( hir_ty, ty) | match hir_ty {
765
+ // Type inference for closures can depend on how they're called. Only go by the explicit
766
+ // types here.
767
+ Some ( ty) => binding_ty_auto_deref_stability ( ty, precedence) ,
768
+ None => needless_borrow_impl_arg_position ( cx, parent, i, ty. skip_binder ( ) , child_id)
769
+ . unwrap_or_else ( || param_auto_deref_stability ( ty. skip_binder ( ) , precedence) ) ,
770
+ } )
760
771
} ) ,
761
772
ExprKind :: MethodCall ( _, args, _) => {
762
773
let id = cx. typeck_results ( ) . type_dependent_def_id ( parent. hir_id ) . unwrap ( ) ;
@@ -797,7 +808,9 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
797
808
Position :: MethodReceiver
798
809
}
799
810
} else {
800
- param_auto_deref_stability ( cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i] , precedence)
811
+ let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i] ;
812
+ needless_borrow_impl_arg_position ( cx, parent, i, ty, child_id)
813
+ . unwrap_or_else ( || param_auto_deref_stability ( ty, precedence) )
801
814
}
802
815
} )
803
816
} ,
@@ -920,6 +933,111 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
920
933
v. 0
921
934
}
922
935
936
+ // Checks whether:
937
+ // * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
938
+ // * `e`'s type implements `Trait` and is copyable
939
+ // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
940
+ // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
941
+ // be moved, but it cannot be.
942
+ fn needless_borrow_impl_arg_position < ' tcx > (
943
+ cx : & LateContext < ' tcx > ,
944
+ parent : & Expr < ' tcx > ,
945
+ child_arg_index : usize ,
946
+ child_arg_ty : Ty < ' tcx > ,
947
+ child_id : HirId ,
948
+ ) -> Option < Position > {
949
+ let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
950
+
951
+ let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return None } ;
952
+
953
+ let traits = cx
954
+ . tcx
955
+ . predicates_of ( callee_def_id)
956
+ . predicates
957
+ . iter ( )
958
+ . filter_map ( |( predicate, _) | {
959
+ if_chain ! {
960
+ if let PredicateKind :: Trait ( TraitPredicate {
961
+ trait_ref:
962
+ TraitRef {
963
+ def_id: trait_def_id,
964
+ substs,
965
+ ..
966
+ } ,
967
+ ..
968
+ } ) = predicate. kind( ) . skip_binder( ) ;
969
+ if let Some ( arg) = substs. iter( ) . next( ) ;
970
+ if let GenericArgKind :: Type ( arg_ty) = arg. unpack( ) ;
971
+ if arg_ty == child_arg_ty;
972
+ then { Some ( ( trait_def_id, & substs[ 1 ..] ) ) } else { None }
973
+ }
974
+ } )
975
+ . collect :: < Vec < _ > > ( ) ;
976
+
977
+ // If only `Sized` was found, return.
978
+ if traits
979
+ . iter ( )
980
+ . all ( |& ( trait_def_id, _) | Some ( trait_def_id) == sized_trait_def_id)
981
+ {
982
+ return None ;
983
+ }
984
+
985
+ // If `child_arg_ty` is a type parameter that appears in more than one place, then substituting
986
+ // it with `T` instead of `&T` could cause a type error.
987
+ if cx
988
+ . tcx
989
+ . fn_sig ( callee_def_id)
990
+ . skip_binder ( )
991
+ . inputs_and_output
992
+ . iter ( )
993
+ . enumerate ( )
994
+ . any ( |( i, ty) | i != child_arg_index && contains_ty ( ty, child_arg_ty) )
995
+ {
996
+ return None ;
997
+ }
998
+
999
+ let check_referent = |referent| {
1000
+ let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
1001
+
1002
+ if !is_copy ( cx, referent_ty) {
1003
+ return false ;
1004
+ }
1005
+
1006
+ // * If an applicable trait is found and `referent_ty` implements it, `needless_borrow` is set to
1007
+ // `true`.
1008
+ // * If an applicable trait is found that `referent_ty` does not implement, `false` is returned
1009
+ // immediately.
1010
+ // * If no applicable traits are found, `needless_borrow` remains `false`.
1011
+ let mut needless_borrow = false ;
1012
+ for & ( trait_def_id, substs) in & traits {
1013
+ if implements_trait ( cx, referent_ty, trait_def_id, substs) {
1014
+ // Ignore `Sized` since it is required by default.
1015
+ needless_borrow = Some ( trait_def_id) != sized_trait_def_id;
1016
+ } else {
1017
+ return false ;
1018
+ }
1019
+ }
1020
+ needless_borrow
1021
+ } ;
1022
+
1023
+ let Node :: Expr ( mut descendent) = cx. tcx . hir ( ) . get ( child_id) else { return None } ;
1024
+
1025
+ let mut needless_borrow = false ;
1026
+ while let ExprKind :: AddrOf ( _, _, referent) = descendent. kind {
1027
+ if !check_referent ( referent) {
1028
+ break ;
1029
+ }
1030
+ descendent = referent;
1031
+ needless_borrow = true ;
1032
+ }
1033
+
1034
+ if needless_borrow {
1035
+ Some ( Position :: ImplArg ( descendent. hir_id ) )
1036
+ } else {
1037
+ None
1038
+ }
1039
+ }
1040
+
923
1041
// Checks whether a type is stable when switching to auto dereferencing,
924
1042
fn param_auto_deref_stability ( ty : Ty < ' _ > , precedence : i8 ) -> Position {
925
1043
let ty:: Ref ( _, mut ty, _) = * ty. kind ( ) else {
@@ -1026,7 +1144,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
1026
1144
} ,
1027
1145
State :: DerefedBorrow ( state) => {
1028
1146
let mut app = Applicability :: MachineApplicable ;
1029
- let ( snip, snip_is_macro) = snippet_with_context ( cx, expr. span , data. span . ctxt ( ) , ".." , & mut app) ;
1147
+ let snip_expr = state. snip_expr . map_or ( expr, |hir_id| cx. tcx . hir ( ) . expect_expr ( hir_id) ) ;
1148
+ let ( snip, snip_is_macro) = snippet_with_context ( cx, snip_expr. span , data. span . ctxt ( ) , ".." , & mut app) ;
1030
1149
span_lint_hir_and_then ( cx, NEEDLESS_BORROW , data. hir_id , data. span , state. msg , |diag| {
1031
1150
let sugg = if !snip_is_macro
1032
1151
&& expr. precedence ( ) . order ( ) < data. position . precedence ( )
0 commit comments