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 , Expr , ExprKind , FnRetTy , GenericArg , HirId , ImplItem ,
12
- 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 , Expr , ExprKind , FnRetTy , GenericArg ,
17
+ HirId , ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath ,
18
+ 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:: subst:: GenericArgKind ;
19
- use rustc_middle:: ty:: { self , PredicateKind , TraitPredicate , TraitRef , Ty , TyCtxt , TypeFoldable , TypeckResults } ;
23
+ use rustc_middle:: ty:: { self , layout:: LayoutOf , ParamTy , TraitPredicate , Ty , TyCtxt , TypeFoldable , TypeckResults } ;
20
24
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
21
25
use rustc_span:: { symbol:: sym, Span , Symbol } ;
22
26
use rustc_trait_selection:: infer:: InferCtxtExt ;
@@ -763,8 +767,13 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
763
767
// Type inference for closures can depend on how they're called. Only go by the explicit
764
768
// types here.
765
769
Some ( ty) => binding_ty_auto_deref_stability ( ty, precedence) ,
766
- None => needless_borrow_impl_arg_position ( cx, parent, i, ty. skip_binder ( ) , child_id)
767
- . 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
+ } ,
768
777
} )
769
778
} ) ,
770
779
ExprKind :: MethodCall ( _, args, _) => {
@@ -807,8 +816,11 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
807
816
}
808
817
} else {
809
818
let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i] ;
810
- needless_borrow_impl_arg_position ( cx, parent, i, ty, child_id)
811
- . 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
+ }
812
824
}
813
825
} )
814
826
} ,
@@ -931,6 +943,13 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
931
943
v. 0
932
944
}
933
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
+
934
953
// Checks whether:
935
954
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
936
955
// * `e`'s type implements `Trait` and is copyable
@@ -940,106 +959,132 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
940
959
fn needless_borrow_impl_arg_position < ' tcx > (
941
960
cx : & LateContext < ' tcx > ,
942
961
parent : & Expr < ' tcx > ,
943
- child_arg_index : usize ,
944
- child_arg_ty : Ty < ' tcx > ,
945
- child_id : HirId ,
946
- ) -> Option < Position > {
962
+ arg_index : usize ,
963
+ param_ty : ParamTy ,
964
+ mut expr : & Expr < ' tcx > ,
965
+ precedence : i8 ,
966
+ ) -> Position {
947
967
let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
948
968
949
- let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return None } ;
950
-
951
- let traits = cx
952
- . tcx
953
- . predicates_of ( callee_def_id)
954
- . predicates
955
- . iter ( )
956
- . filter_map ( |( predicate, _) | {
957
- if_chain ! {
958
- if let PredicateKind :: Trait ( TraitPredicate {
959
- trait_ref:
960
- TraitRef {
961
- def_id: trait_def_id,
962
- substs,
963
- ..
964
- } ,
965
- ..
966
- } ) = predicate. kind( ) . skip_binder( ) ;
967
- if let Some ( arg) = substs. iter( ) . next( ) ;
968
- if let GenericArgKind :: Type ( arg_ty) = arg. unpack( ) ;
969
- if arg_ty == child_arg_ty;
970
- then { Some ( ( trait_def_id, & substs[ 1 ..] ) ) } else { None }
971
- }
972
- } )
973
- . collect :: < Vec < _ > > ( ) ;
969
+ let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return Position :: Other ( precedence) } ;
974
970
975
- // If only `Sized` was found, return.
976
- if traits
977
- . iter ( )
978
- . all ( |& ( trait_def_id, _) | Some ( trait_def_id) == sized_trait_def_id)
979
- {
980
- return None ;
981
- }
971
+ let ( trait_predicates, projection_predicates) =
972
+ get_input_traits_and_projections ( cx, callee_def_id, param_ty. to_ty ( cx. tcx ) ) ;
982
973
983
- if !matches ! ( child_arg_ty. kind( ) , ty:: Param ( _) ) {
984
- 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) ;
985
980
}
986
981
987
- // If `child_arg_ty` is a type parameter that appears in more than one place, then substituting
988
- // 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.
989
984
if cx
990
985
. tcx
991
986
. fn_sig ( callee_def_id)
992
987
. skip_binder ( )
993
988
. inputs_and_output
994
989
. iter ( )
995
990
. enumerate ( )
996
- . 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 ) ) )
997
992
{
998
- return None ;
993
+ return Position :: Other ( precedence ) ;
999
994
}
1000
995
1001
996
let check_referent = |referent| {
1002
997
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
1003
998
1004
- 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
+ {
1005
1004
return false ;
1006
1005
}
1007
1006
1008
- // * If an applicable trait is found and `referent_ty` implements it, `needless_borrow` is set to
1009
- // `true`.
1010
- // * If an applicable trait is found that `referent_ty` does not implement, `false` is returned
1011
- // immediately.
1012
- // * If no applicable traits are found, `needless_borrow` remains `false`.
1013
- let mut needless_borrow = false ;
1014
- for & ( trait_def_id, substs) in & traits {
1015
- if implements_trait ( cx, referent_ty, trait_def_id, substs) {
1016
- // Ignore `Sized` since it is required by default.
1017
- needless_borrow = Some ( trait_def_id) != sized_trait_def_id;
1018
- } else {
1019
- return false ;
1020
- }
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 ;
1021
1053
}
1022
- needless_borrow
1023
- } ;
1024
1054
1025
- let Node :: Expr ( mut descendent) = cx. tcx . hir ( ) . get ( child_id) else { return None } ;
1055
+ true
1056
+ } ;
1026
1057
1027
1058
let mut needless_borrow = false ;
1028
- while let ExprKind :: AddrOf ( _, _, referent) = descendent . kind {
1059
+ while let ExprKind :: AddrOf ( _, _, referent) = expr . kind {
1029
1060
if !check_referent ( referent) {
1030
1061
break ;
1031
1062
}
1032
- descendent = referent;
1063
+ expr = referent;
1033
1064
needless_borrow = true ;
1034
1065
}
1035
1066
1036
1067
if needless_borrow {
1037
- Some ( Position :: ImplArg ( descendent . hir_id ) )
1068
+ Position :: ImplArg ( expr . hir_id )
1038
1069
} else {
1039
- None
1070
+ Position :: Other ( precedence )
1040
1071
}
1041
1072
}
1042
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
+
1043
1088
// Checks whether a type is stable when switching to auto dereferencing,
1044
1089
fn param_auto_deref_stability ( ty : Ty < ' _ > , precedence : i8 ) -> Position {
1045
1090
let ty:: Ref ( _, mut ty, _) = * ty. kind ( ) else {
0 commit comments