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:: { 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} ;
4
+ use clippy_utils:: ty:: {
5
+ contains_ty, expr_sig, get_input_traits_and_projections, implements_trait, is_copy, peel_mid_ty_refs,
6
+ variant_of_res,
7
+ } ;
8
+ use clippy_utils:: {
9
+ fn_def_id, get_parent_expr, is_lint_allowed, match_def_path, path_to_local, paths, walk_to_expr_usage,
10
+ } ;
6
11
use rustc_ast:: util:: parser:: { PREC_POSTFIX , PREC_PREFIX } ;
7
12
use rustc_data_structures:: fx:: FxIndexMap ;
8
13
use rustc_errors:: Applicability ;
9
14
use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
10
15
use rustc_hir:: {
11
- self as hir, BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy , GenericArg , HirId ,
12
- ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath , TraitItem ,
13
- TraitItemKind , TyKind , UnOp ,
16
+ self as hir, def_id :: DefId , BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy ,
17
+ GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind ,
18
+ Path , QPath , TraitItem , TraitItemKind , TyKind , UnOp ,
14
19
} ;
15
20
use rustc_infer:: infer:: TyCtxtInferExt ;
16
21
use rustc_lint:: { LateContext , LateLintPass } ;
17
22
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
18
- use rustc_middle:: ty:: {
19
- self , subst:: GenericArgKind , PredicateKind , TraitPredicate , TraitRef , Ty , TyCtxt , TypeFoldable , TypeVisitable ,
20
- TypeckResults , TypeckResults ,
21
- } ;
23
+ use rustc_middle:: ty:: { self , layout:: LayoutOf , ParamTy , TraitPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults } ;
22
24
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
23
25
use rustc_span:: { symbol:: sym, Span , Symbol } ;
24
26
use rustc_trait_selection:: infer:: InferCtxtExt ;
@@ -765,8 +767,13 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
765
767
// Type inference for closures can depend on how they're called. Only go by the explicit
766
768
// types here.
767
769
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
+ None => {
771
+ if let ty:: Param ( param_ty) = ty. skip_binder ( ) . kind ( ) {
772
+ needless_borrow_impl_arg_position ( cx, parent, i, * param_ty, e, precedence)
773
+ } else {
774
+ param_auto_deref_stability ( ty. skip_binder ( ) , precedence)
775
+ }
776
+ } ,
770
777
} )
771
778
} ) ,
772
779
ExprKind :: MethodCall ( _, args, _) => {
@@ -809,8 +816,11 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
809
816
}
810
817
} else {
811
818
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) )
819
+ if let ty:: Param ( param_ty) = ty. kind ( ) {
820
+ needless_borrow_impl_arg_position ( cx, parent, i, * param_ty, e, precedence)
821
+ } else {
822
+ param_auto_deref_stability ( ty, precedence)
823
+ }
814
824
}
815
825
} )
816
826
} ,
@@ -933,6 +943,13 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
933
943
v. 0
934
944
}
935
945
946
+ // The maximum size (in bytes) to consider removing a reference from. Its purpose is to help limit
947
+ // the performance impact in cases where a value would have to be moved, but it cannot be (see "is
948
+ // copyable" comment just below). The choice of this parameter should not affect situations beyond
949
+ // that. At the time the parameter was chosen, the largest type flagged by this lint in Clippy
950
+ // itself was 160 bytes.
951
+ const NEEDLESS_BORROW_SIZE_LIMIT : u64 = 256 ;
952
+
936
953
// Checks whether:
937
954
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
938
955
// * `e`'s type implements `Trait` and is copyable
@@ -942,106 +959,132 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
942
959
fn needless_borrow_impl_arg_position < ' tcx > (
943
960
cx : & LateContext < ' tcx > ,
944
961
parent : & Expr < ' tcx > ,
945
- child_arg_index : usize ,
946
- child_arg_ty : Ty < ' tcx > ,
947
- child_id : HirId ,
948
- ) -> Option < Position > {
962
+ arg_index : usize ,
963
+ param_ty : ParamTy ,
964
+ mut expr : & Expr < ' tcx > ,
965
+ precedence : i8 ,
966
+ ) -> Position {
949
967
let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
950
968
951
- let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return None } ;
969
+ let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return Position :: Other ( precedence ) } ;
952
970
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
- }
971
+ let ( trait_predicates, projection_predicates) =
972
+ get_input_traits_and_projections ( cx, callee_def_id, param_ty. to_ty ( cx. tcx ) ) ;
984
973
985
- if !matches ! ( child_arg_ty. kind( ) , ty:: Param ( _) ) {
986
- return None ;
974
+ // If no traits were found, or only the `Sized` or `Any` traits were found, return.
975
+ if trait_predicates. iter ( ) . all ( |trait_predicate| {
976
+ let trait_def_id = trait_predicate. def_id ( ) ;
977
+ Some ( trait_def_id) == sized_trait_def_id || match_def_path ( cx, trait_def_id, & paths:: ANY_TRAIT )
978
+ } ) {
979
+ return Position :: Other ( precedence) ;
987
980
}
988
981
989
- // If `child_arg_ty` is a type parameter that appears in more than one place, then substituting
990
- // it with `T` instead of `&T` could cause a type error.
982
+ // If `param_ty` appears in more than one place, then substituting it with `T` instead of `&T` could
983
+ // cause a type error.
991
984
if cx
992
985
. tcx
993
986
. fn_sig ( callee_def_id)
994
987
. skip_binder ( )
995
988
. inputs_and_output
996
989
. iter ( )
997
990
. enumerate ( )
998
- . any ( |( i, ty) | i != child_arg_index && contains_ty ( ty, child_arg_ty ) )
991
+ . any ( |( i, ty) | i != arg_index && contains_ty ( ty, param_ty . to_ty ( cx . tcx ) ) )
999
992
{
1000
- return None ;
993
+ return Position :: Other ( precedence ) ;
1001
994
}
1002
995
1003
996
let check_referent = |referent| {
1004
997
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
1005
998
1006
- if !is_copy ( cx, referent_ty) {
999
+ if !is_copy ( cx, referent_ty)
1000
+ || cx
1001
+ . layout_of ( referent_ty)
1002
+ . map_or ( true , |layout| layout. size . bytes ( ) > NEEDLESS_BORROW_SIZE_LIMIT )
1003
+ {
1007
1004
return false ;
1008
1005
}
1009
1006
1010
- // * If an applicable trait is found and `referent_ty` implements it, `needless_borrow` is set to
1011
- // `true`.
1012
- // * If an applicable trait is found that `referent_ty` does not implement, `false` is returned
1013
- // immediately.
1014
- // * If no applicable traits are found, `needless_borrow` remains `false`.
1015
- let mut needless_borrow = false ;
1016
- for & ( trait_def_id, substs) in & traits {
1017
- if implements_trait ( cx, referent_ty, trait_def_id, substs) {
1018
- // Ignore `Sized` since it is required by default.
1019
- needless_borrow = Some ( trait_def_id) != sized_trait_def_id;
1020
- } else {
1021
- return false ;
1022
- }
1007
+ // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
1008
+ if !matches ! ( referent_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) )
1009
+ && trait_predicates
1010
+ . iter ( )
1011
+ . any ( |TraitPredicate { trait_ref, .. } | has_ref_mut_self_method ( cx, trait_ref. def_id ) )
1012
+ {
1013
+ return false ;
1014
+ }
1015
+
1016
+ if !trait_predicates. iter ( ) . all ( |TraitPredicate { trait_ref, .. } | {
1017
+ // The use of `has_escaping_bound_vars` addresses:
1018
+ // https://github.com/rust-lang/rust-clippy/pull/9136#issuecomment-1184024609
1019
+ let substs = & trait_ref. substs [ 1 ..] ;
1020
+ substs. iter ( ) . all ( |arg| !arg. has_escaping_bound_vars ( ) )
1021
+ && implements_trait ( cx, referent_ty, trait_ref. def_id , substs)
1022
+ } ) {
1023
+ return false ;
1024
+ }
1025
+
1026
+ if !projection_predicates. iter ( ) . all ( |projection_predicate| {
1027
+ let item_def_id = projection_predicate. projection_ty . item_def_id ;
1028
+ let assoc_item = cx. tcx . associated_item ( item_def_id) ;
1029
+ let ty:: Term :: Ty ( expected_ty) = projection_predicate. term else { return false } ;
1030
+ let projection = cx
1031
+ . tcx
1032
+ . mk_projection ( assoc_item. def_id , cx. tcx . mk_substs_trait ( referent_ty, & [ ] ) ) ;
1033
+ let projected_ty = cx. tcx . normalize_erasing_regions ( cx. param_env , projection) ;
1034
+ // This code is not general. It handles just two specific cases:
1035
+ // * the expected type matches the referent's projected type exactly
1036
+ // * the expected type is a parameter with trait bounds and the referent's projected type satisfies
1037
+ // those trait bounds
1038
+ expected_ty == projected_ty
1039
+ || ( matches ! ( expected_ty. kind( ) , ty:: Param ( _) ) && {
1040
+ let ( expected_ty_trait_predicates, expected_ty_projection_predicates) =
1041
+ get_input_traits_and_projections ( cx, callee_def_id, expected_ty) ;
1042
+ expected_ty_trait_predicates
1043
+ . iter ( )
1044
+ . all ( |TraitPredicate { trait_ref, .. } | {
1045
+ let substs = & trait_ref. substs [ 1 ..] ;
1046
+ substs. iter ( ) . all ( |arg| !arg. has_escaping_bound_vars ( ) )
1047
+ && implements_trait ( cx, projected_ty, trait_ref. def_id , substs)
1048
+ } )
1049
+ && expected_ty_projection_predicates. is_empty ( )
1050
+ } )
1051
+ } ) {
1052
+ return false ;
1023
1053
}
1024
- needless_borrow
1025
- } ;
1026
1054
1027
- let Node :: Expr ( mut descendent) = cx. tcx . hir ( ) . get ( child_id) else { return None } ;
1055
+ true
1056
+ } ;
1028
1057
1029
1058
let mut needless_borrow = false ;
1030
- while let ExprKind :: AddrOf ( _, _, referent) = descendent . kind {
1059
+ while let ExprKind :: AddrOf ( _, _, referent) = expr . kind {
1031
1060
if !check_referent ( referent) {
1032
1061
break ;
1033
1062
}
1034
- descendent = referent;
1063
+ expr = referent;
1035
1064
needless_borrow = true ;
1036
1065
}
1037
1066
1038
1067
if needless_borrow {
1039
- Some ( Position :: ImplArg ( descendent . hir_id ) )
1068
+ Position :: ImplArg ( expr . hir_id )
1040
1069
} else {
1041
- None
1070
+ Position :: Other ( precedence )
1042
1071
}
1043
1072
}
1044
1073
1074
+ fn has_ref_mut_self_method ( cx : & LateContext < ' _ > , trait_def_id : DefId ) -> bool {
1075
+ cx. tcx
1076
+ . associated_items ( trait_def_id)
1077
+ . in_definition_order ( )
1078
+ . any ( |assoc_item| {
1079
+ if assoc_item. fn_has_self_parameter {
1080
+ let self_ty = cx. tcx . fn_sig ( assoc_item. def_id ) . skip_binder ( ) . inputs ( ) [ 0 ] ;
1081
+ matches ! ( self_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) )
1082
+ } else {
1083
+ false
1084
+ }
1085
+ } )
1086
+ }
1087
+
1045
1088
// Checks whether a type is stable when switching to auto dereferencing,
1046
1089
fn param_auto_deref_stability ( ty : Ty < ' _ > , precedence : i8 ) -> Position {
1047
1090
let ty:: Ref ( _, mut ty, _) = * ty. kind ( ) else {
0 commit comments