Skip to content

Commit 43ea74d

Browse files
committed
Address second round of review comments
1 parent 986e420 commit 43ea74d

File tree

3 files changed

+102
-75
lines changed

3 files changed

+102
-75
lines changed

clippy_lints/src/dereference.rs

Lines changed: 87 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
22
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
33
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};
116
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
127
use rustc_data_structures::fx::FxIndexMap;
138
use rustc_errors::Applicability;
@@ -20,10 +15,13 @@ use rustc_hir::{
2015
use rustc_infer::infer::TyCtxtInferExt;
2116
use rustc_lint::{LateContext, LateLintPass};
2217
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+
};
2421
use rustc_session::{declare_tool_lint, impl_lint_pass};
2522
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};
2725

2826
declare_clippy_lint! {
2927
/// ### What it does
@@ -658,7 +656,7 @@ impl Position {
658656
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
659657
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
660658
/// locations as those follow different rules.
661-
#[allow(clippy::too_many_lines)]
659+
#[expect(clippy::too_many_lines)]
662660
fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
663661
let mut adjustments = [].as_slice();
664662
let mut precedence = 0i8;
@@ -943,19 +941,13 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
943941
v.0
944942
}
945943

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-
953944
// Checks whether:
954945
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
955946
// * `e`'s type implements `Trait` and is copyable
956947
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
957948
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
958949
// be moved, but it cannot be.
950+
#[allow(clippy::too_many_lines)]
959951
fn needless_borrow_impl_arg_position<'tcx>(
960952
cx: &LateContext<'tcx>,
961953
parent: &Expr<'tcx>,
@@ -967,14 +959,45 @@ fn needless_borrow_impl_arg_position<'tcx>(
967959
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
968960

969961
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+
});
970970

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<_>>();
973996

974997
// If no traits were found, or only the `Sized` or `Any` traits were found, return.
975998
if trait_predicates.iter().all(|trait_predicate| {
976999
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)
9781001
}) {
9791002
return Position::Other(precedence);
9801003
}
@@ -996,11 +1019,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
9961019
let check_referent = |referent| {
9971020
let referent_ty = cx.typeck_results().expr_ty(referent);
9981021

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) {
10041023
return false;
10051024
}
10061025

@@ -1013,41 +1032,49 @@ fn needless_borrow_impl_arg_position<'tcx>(
10131032
return false;
10141033
}
10151034

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))
10511078
}) {
10521079
return false;
10531080
}

clippy_utils/src/paths.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
1515
];
1616
#[cfg(feature = "internal")]
1717
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
18-
pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
1918
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
2019
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
2120
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];

clippy_utils/src/ty.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,14 @@ pub fn get_input_traits_and_projections<'tcx>(
9595
let mut trait_predicates = Vec::new();
9696
let mut projection_predicates = Vec::new();
9797
for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
98-
// `substs` should have 1 + n elements. The first is the type on the left hand side of an
99-
// `as`. The remaining n are trait parameters.
100-
let is_input_substs = |substs: SubstsRef<'tcx>| {
101-
if let Some(arg) = substs.iter().next()
102-
&& let GenericArgKind::Type(arg_ty) = arg.unpack()
103-
&& arg_ty == input
104-
{
105-
true
106-
} else {
107-
false
108-
}
109-
};
11098
match predicate.kind().skip_binder() {
11199
PredicateKind::Trait(trait_predicate) => {
112-
if is_input_substs(trait_predicate.trait_ref.substs) {
100+
if is_substs_for_type(trait_predicate.trait_ref.substs, input) {
113101
trait_predicates.push(trait_predicate);
114102
}
115103
},
116104
PredicateKind::Projection(projection_predicate) => {
117-
if is_input_substs(projection_predicate.projection_ty.substs) {
105+
if is_substs_for_type(projection_predicate.projection_ty.substs, input) {
118106
projection_predicates.push(projection_predicate);
119107
}
120108
},
@@ -326,6 +314,19 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
326314
}
327315
}
328316

317+
// Returns `true` if `substs` has 1 + n elements and the first is `ty`. Think of the first as the
318+
// type on the left hand side of an `as`, and the rest as trait parameters.
319+
pub fn is_substs_for_type<'tcx>(substs: SubstsRef<'tcx>, ty: Ty<'tcx>) -> bool {
320+
if let Some(arg) = substs.iter().next()
321+
&& let GenericArgKind::Type(arg_ty) = arg.unpack()
322+
&& arg_ty == ty
323+
{
324+
true
325+
} else {
326+
false
327+
}
328+
}
329+
329330
/// Checks if the type is a reference equals to a diagnostic item
330331
pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
331332
match ty.kind() {

0 commit comments

Comments
 (0)