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:: {
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
- } ;
4
+ use clippy_utils:: ty:: { contains_ty, expr_sig, is_copy, is_substs_for_type, 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} ;
11
6
use rustc_ast:: util:: parser:: { PREC_POSTFIX , PREC_PREFIX } ;
12
7
use rustc_data_structures:: fx:: FxIndexMap ;
13
8
use rustc_errors:: Applicability ;
@@ -20,10 +15,13 @@ use rustc_hir::{
20
15
use rustc_infer:: infer:: TyCtxtInferExt ;
21
16
use rustc_lint:: { LateContext , LateLintPass } ;
22
17
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
23
- use rustc_middle:: ty:: { self , TraitPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults } ;
18
+ use rustc_middle:: ty:: {
19
+ self , subst:: Subst , EarlyBinder , ParamTy , PredicateKind , TraitPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults ,
20
+ } ;
24
21
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
25
22
use rustc_span:: { symbol:: sym, Span , Symbol } ;
26
- use rustc_trait_selection:: infer:: InferCtxtExt ;
23
+ use rustc_trait_selection:: infer:: InferCtxtExt as _;
24
+ use rustc_trait_selection:: traits:: { query:: evaluate_obligation:: InferCtxtExt as _, Obligation , ObligationCause } ;
27
25
28
26
declare_clippy_lint ! {
29
27
/// ### What it does
@@ -658,7 +656,7 @@ impl Position {
658
656
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
659
657
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
660
658
/// locations as those follow different rules.
661
- #[ allow ( clippy:: too_many_lines) ]
659
+ #[ expect ( clippy:: too_many_lines) ]
662
660
fn walk_parents < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
663
661
let mut adjustments = [ ] . as_slice ( ) ;
664
662
let mut precedence = 0i8 ;
@@ -943,19 +941,13 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
943
941
v. 0
944
942
}
945
943
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
-
953
944
// Checks whether:
954
945
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
955
946
// * `e`'s type implements `Trait` and is copyable
956
947
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
957
948
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
958
949
// be moved, but it cannot be.
950
+ #[ allow( clippy:: too_many_lines) ]
959
951
fn needless_borrow_impl_arg_position < ' tcx > (
960
952
cx : & LateContext < ' tcx > ,
961
953
parent : & Expr < ' tcx > ,
@@ -967,14 +959,45 @@ fn needless_borrow_impl_arg_position<'tcx>(
967
959
let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
968
960
969
961
let Some ( callee_def_id) = fn_def_id ( cx, parent) else { return Position :: Other ( precedence) } ;
962
+ let callee_generics = cx. tcx . generics_of ( callee_def_id) ;
963
+ let substs_with_expr_ty = cx
964
+ . typeck_results ( )
965
+ . node_substs ( if let ExprKind :: Call ( callee, _) = parent. kind {
966
+ callee. hir_id
967
+ } else {
968
+ parent. hir_id
969
+ } ) ;
970
970
971
- let ( trait_predicates, projection_predicates) =
972
- get_input_traits_and_projections ( cx, callee_def_id, param_ty. to_ty ( cx. tcx ) ) ;
971
+ let predicates = cx. tcx . param_env ( callee_def_id) . caller_bounds ( ) ;
972
+ let trait_predicates = predicates
973
+ . iter ( )
974
+ . filter_map ( |predicate| {
975
+ if let PredicateKind :: Trait ( trait_predicate) = predicate. kind ( ) . skip_binder ( )
976
+ && is_substs_for_type ( trait_predicate. trait_ref . substs , param_ty. to_ty ( cx. tcx ) )
977
+ {
978
+ Some ( trait_predicate)
979
+ } else {
980
+ None
981
+ }
982
+ } )
983
+ . collect :: < Vec < _ > > ( ) ;
984
+ let projection_predicates = predicates
985
+ . iter ( )
986
+ . filter_map ( |predicate| {
987
+ if let PredicateKind :: Projection ( projection_predicate) = predicate. kind ( ) . skip_binder ( )
988
+ && is_substs_for_type ( projection_predicate. projection_ty . substs , param_ty. to_ty ( cx. tcx ) )
989
+ {
990
+ Some ( projection_predicate)
991
+ } else {
992
+ None
993
+ }
994
+ } )
995
+ . collect :: < Vec < _ > > ( ) ;
973
996
974
997
// If no traits were found, or only the `Sized` or `Any` traits were found, return.
975
998
if trait_predicates. iter ( ) . all ( |trait_predicate| {
976
999
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 )
1000
+ Some ( trait_def_id) == sized_trait_def_id || cx . tcx . is_diagnostic_item ( sym :: Any , trait_def_id)
978
1001
} ) {
979
1002
return Position :: Other ( precedence) ;
980
1003
}
@@ -996,11 +1019,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
996
1019
let check_referent = |referent| {
997
1020
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
998
1021
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
- {
1022
+ if !is_copy ( cx, referent_ty) {
1004
1023
return false ;
1005
1024
}
1006
1025
@@ -1013,41 +1032,49 @@ fn needless_borrow_impl_arg_position<'tcx>(
1013
1032
return false ;
1014
1033
}
1015
1034
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
- } )
1035
+ let substs_with_expr_ty = substs_with_expr_ty. split_at ( callee_generics. parent_count ) ;
1036
+ let substs_with_referent_ty = substs_with_expr_ty
1037
+ . 0
1038
+ . iter ( )
1039
+ . copied ( )
1040
+ . chain (
1041
+ callee_generics
1042
+ . params
1043
+ . iter ( )
1044
+ . zip ( substs_with_expr_ty. 1 . iter ( ) )
1045
+ . map ( |( param, arg) | {
1046
+ if param. index == param_ty. index {
1047
+ ty:: GenericArg :: from ( referent_ty)
1048
+ } else {
1049
+ projection_predicates
1050
+ . iter ( )
1051
+ . find_map ( |projection_predicate| {
1052
+ if let ty:: Term :: Ty ( term_ty) = projection_predicate. term
1053
+ && term_ty. is_param ( param. index )
1054
+ {
1055
+ let item_def_id = projection_predicate. projection_ty . item_def_id ;
1056
+ let assoc_item = cx. tcx . associated_item ( item_def_id) ;
1057
+ let projection = cx
1058
+ . tcx
1059
+ . mk_projection ( assoc_item. def_id , cx. tcx . mk_substs_trait ( referent_ty, & [ ] ) ) ;
1060
+ let projected_ty = cx. tcx . normalize_erasing_regions ( cx. param_env , projection) ;
1061
+ Some ( ty:: GenericArg :: from ( projected_ty) )
1062
+ } else {
1063
+ None
1064
+ }
1065
+ } )
1066
+ . unwrap_or ( * arg)
1067
+ }
1068
+ } ) ,
1069
+ )
1070
+ . collect :: < Vec < _ > > ( ) ;
1071
+
1072
+ let predicates = EarlyBinder ( predicates) . subst ( cx. tcx , & substs_with_referent_ty) ;
1073
+ if !predicates. iter ( ) . all ( |predicate| {
1074
+ let obligation = Obligation :: new ( ObligationCause :: dummy ( ) , cx. param_env , predicate) ;
1075
+ cx. tcx
1076
+ . infer_ctxt ( )
1077
+ . enter ( |infcx| infcx. predicate_must_hold_modulo_regions ( & obligation) )
1051
1078
} ) {
1052
1079
return false ;
1053
1080
}
0 commit comments