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, is_copy, is_substs_for_type , peel_mid_ty_refs, variant_of_res} ;
4
+ use clippy_utils:: ty:: { contains_ty, expr_sig, is_copy, peel_mid_ty_refs, variant_of_res} ;
5
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 ;
@@ -12,16 +12,19 @@ use rustc_hir::{
12
12
GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind ,
13
13
Path , QPath , TraitItem , TraitItemKind , TyKind , UnOp ,
14
14
} ;
15
+ use rustc_index:: bit_set:: BitSet ;
15
16
use rustc_infer:: infer:: TyCtxtInferExt ;
16
17
use rustc_lint:: { LateContext , LateLintPass } ;
17
18
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
18
19
use rustc_middle:: ty:: {
19
- self , subst:: Subst , EarlyBinder , ParamTy , PredicateKind , TraitPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults ,
20
+ self , subst:: Subst , EarlyBinder , FnSig , ParamTy , PredicateKind , ProjectionPredicate , TraitPredicate , Ty , TyCtxt ,
21
+ TypeVisitable , TypeckResults ,
20
22
} ;
21
23
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
22
24
use rustc_span:: { symbol:: sym, Span , Symbol } ;
23
25
use rustc_trait_selection:: infer:: InferCtxtExt as _;
24
26
use rustc_trait_selection:: traits:: { query:: evaluate_obligation:: InferCtxtExt as _, Obligation , ObligationCause } ;
27
+ use std:: collections:: VecDeque ;
25
28
26
29
declare_clippy_lint ! {
27
30
/// ### What it does
@@ -958,6 +961,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
958
961
let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
959
962
960
963
let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return Position :: Other ( precedence) } ;
964
+ let fn_sig = cx. tcx . fn_sig ( callee_def_id) . skip_binder ( ) ;
961
965
let substs_with_expr_ty = cx
962
966
. typeck_results ( )
963
967
. node_substs ( if let ExprKind :: Call ( callee, _) = parent. kind {
@@ -967,11 +971,11 @@ fn needless_borrow_impl_arg_position<'tcx>(
967
971
} ) ;
968
972
969
973
let predicates = cx. tcx . param_env ( callee_def_id) . caller_bounds ( ) ;
970
- let trait_predicates = predicates
974
+ let trait_predicates_for_param_ty = predicates
971
975
. iter ( )
972
976
. filter_map ( |predicate| {
973
977
if let PredicateKind :: Trait ( trait_predicate) = predicate. kind ( ) . skip_binder ( )
974
- && is_substs_for_type ( trait_predicate. trait_ref . substs , param_ty. to_ty ( cx. tcx ) )
978
+ && trait_predicate. trait_ref . self_ty ( ) == param_ty. to_ty ( cx. tcx )
975
979
{
976
980
Some ( trait_predicate)
977
981
} else {
@@ -982,9 +986,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
982
986
let projection_predicates = predicates
983
987
. iter ( )
984
988
. filter_map ( |predicate| {
985
- if let PredicateKind :: Projection ( projection_predicate) = predicate. kind ( ) . skip_binder ( )
986
- && is_substs_for_type ( projection_predicate. projection_ty . substs , param_ty. to_ty ( cx. tcx ) )
987
- {
989
+ if let PredicateKind :: Projection ( projection_predicate) = predicate. kind ( ) . skip_binder ( ) {
988
990
Some ( projection_predicate)
989
991
} else {
990
992
None
@@ -993,57 +995,43 @@ fn needless_borrow_impl_arg_position<'tcx>(
993
995
. collect :: < Vec < _ > > ( ) ;
994
996
995
997
// If no traits were found, or only the `Sized` or `Any` traits were found, return.
996
- if trait_predicates . iter ( ) . all ( |trait_predicate| {
998
+ if trait_predicates_for_param_ty . iter ( ) . all ( |trait_predicate| {
997
999
let trait_def_id = trait_predicate. def_id ( ) ;
998
1000
Some ( trait_def_id) == sized_trait_def_id || cx. tcx . is_diagnostic_item ( sym:: Any , trait_def_id)
999
1001
} ) {
1000
1002
return Position :: Other ( precedence) ;
1001
1003
}
1002
1004
1003
- // If `param_ty` appears in more than one place, then substituting it with `T` instead of `&T` could
1004
- // cause a type error.
1005
- if cx
1006
- . tcx
1007
- . fn_sig ( callee_def_id)
1008
- . skip_binder ( )
1009
- . inputs_and_output
1005
+ let has_ref_mut_self_method = trait_predicates_for_param_ty
1010
1006
. iter ( )
1011
- . enumerate ( )
1012
- . any ( |( i, ty) | i != arg_index && contains_ty ( ty, param_ty. to_ty ( cx. tcx ) ) )
1013
- {
1014
- return Position :: Other ( precedence) ;
1015
- }
1007
+ . any ( |TraitPredicate { trait_ref, .. } | has_ref_mut_self_method ( cx, trait_ref. def_id ) ) ;
1016
1008
1017
- let check_referent = |referent| {
1009
+ // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
1010
+ // elements are modified each time `check_referent` is called.
1011
+ let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
1012
+
1013
+ let mut check_referent = |referent| {
1018
1014
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
1019
1015
1020
1016
if !is_copy ( cx, referent_ty) {
1021
1017
return false ;
1022
1018
}
1023
1019
1024
1020
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
1025
- if !matches ! ( referent_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) )
1026
- && trait_predicates
1027
- . iter ( )
1028
- . any ( |TraitPredicate { trait_ref, .. } | has_ref_mut_self_method ( cx, trait_ref. def_id ) )
1029
- {
1021
+ if has_ref_mut_self_method && !matches ! ( referent_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) ) {
1030
1022
return false ;
1031
1023
}
1032
1024
1033
- let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
1034
- substs_with_referent_ty[ param_ty. index as usize ] = ty:: GenericArg :: from ( referent_ty) ;
1035
- for projection_predicate in & projection_predicates {
1036
- if let ty:: Term :: Ty ( term_ty) = projection_predicate. term
1037
- && let ty:: Param ( term_param_ty) = term_ty. kind ( )
1038
- {
1039
- let item_def_id = projection_predicate. projection_ty . item_def_id ;
1040
- let assoc_item = cx. tcx . associated_item ( item_def_id) ;
1041
- let projection = cx
1042
- . tcx
1043
- . mk_projection ( assoc_item. def_id , cx. tcx . mk_substs_trait ( referent_ty, & [ ] ) ) ;
1044
- let projected_ty = cx. tcx . normalize_erasing_regions ( cx. param_env , projection) ;
1045
- substs_with_referent_ty[ term_param_ty. index as usize ] = ty:: GenericArg :: from ( projected_ty) ;
1046
- }
1025
+ if !replace_types (
1026
+ cx,
1027
+ param_ty,
1028
+ referent_ty,
1029
+ fn_sig,
1030
+ arg_index,
1031
+ & projection_predicates,
1032
+ & mut substs_with_referent_ty,
1033
+ ) {
1034
+ return false ;
1047
1035
}
1048
1036
1049
1037
predicates. iter ( ) . all ( |predicate| {
@@ -1085,6 +1073,62 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
1085
1073
} )
1086
1074
}
1087
1075
1076
+ // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
1077
+ // projected type that is a type parameter. Returns `false` if replacing the types would have an
1078
+ // effect on the function signature beyond substituting `new_ty` for `param_ty`.
1079
+ // See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
1080
+ fn replace_types < ' tcx > (
1081
+ cx : & LateContext < ' tcx > ,
1082
+ param_ty : ParamTy ,
1083
+ new_ty : Ty < ' tcx > ,
1084
+ fn_sig : FnSig < ' tcx > ,
1085
+ arg_index : usize ,
1086
+ projection_predicates : & [ ProjectionPredicate < ' tcx > ] ,
1087
+ substs : & mut [ ty:: GenericArg < ' tcx > ] ,
1088
+ ) -> bool {
1089
+ let mut replaced = BitSet :: new_empty ( substs. len ( ) ) ;
1090
+
1091
+ let mut deque = VecDeque :: with_capacity ( substs. len ( ) ) ;
1092
+ deque. push_back ( ( param_ty, new_ty) ) ;
1093
+
1094
+ while let Some ( ( param_ty, new_ty) ) = deque. pop_front ( ) {
1095
+ let new_generic_arg = ty:: GenericArg :: from ( new_ty) ;
1096
+
1097
+ // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
1098
+ if !( substs[ param_ty. index as usize ] == new_generic_arg
1099
+ || fn_sig
1100
+ . inputs_and_output
1101
+ . iter ( )
1102
+ . enumerate ( )
1103
+ . all ( |( i, ty) | ( replaced. is_empty ( ) && i == arg_index) || !contains_ty ( ty, param_ty. to_ty ( cx. tcx ) ) ) )
1104
+ {
1105
+ return false ;
1106
+ }
1107
+
1108
+ // The next line should not panic, but panicking seems better than going into an infinite loop.
1109
+ assert ! ( replaced. insert( param_ty. index) ) ;
1110
+
1111
+ substs[ param_ty. index as usize ] = new_generic_arg;
1112
+
1113
+ for projection_predicate in projection_predicates {
1114
+ if projection_predicate. projection_ty . self_ty ( ) == param_ty. to_ty ( cx. tcx )
1115
+ && let ty:: Term :: Ty ( term_ty) = projection_predicate. term
1116
+ && let ty:: Param ( term_param_ty) = term_ty. kind ( )
1117
+ {
1118
+ let item_def_id = projection_predicate. projection_ty . item_def_id ;
1119
+ let assoc_item = cx. tcx . associated_item ( item_def_id) ;
1120
+ let projection = cx. tcx
1121
+ . mk_projection ( assoc_item. def_id , cx. tcx . mk_substs_trait ( new_ty, & [ ] ) ) ;
1122
+ let projected_ty = cx. tcx . normalize_erasing_regions ( cx. param_env , projection) ;
1123
+
1124
+ deque. push_back ( ( * term_param_ty, projected_ty) ) ;
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ true
1130
+ }
1131
+
1088
1132
// Checks whether a type is stable when switching to auto dereferencing,
1089
1133
fn param_auto_deref_stability ( ty : Ty < ' _ > , precedence : i8 ) -> Position {
1090
1134
let ty:: Ref ( _, mut ty, _) = * ty. kind ( ) else {
0 commit comments