Skip to content

Commit 3b2d61d

Browse files
committed
Address review comments
1 parent 2507d2a commit 3b2d61d

File tree

7 files changed

+257
-120
lines changed

7 files changed

+257
-120
lines changed

clippy_lints/src/dereference.rs

Lines changed: 119 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
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::{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+
};
611
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
712
use rustc_data_structures::fx::FxIndexMap;
813
use rustc_errors::Applicability;
914
use rustc_hir::intravisit::{walk_ty, Visitor};
1015
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,
1419
};
1520
use rustc_infer::infer::TyCtxtInferExt;
1621
use rustc_lint::{LateContext, LateLintPass};
1722
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};
2024
use rustc_session::{declare_tool_lint, impl_lint_pass};
2125
use rustc_span::{symbol::sym, Span, Symbol};
2226
use rustc_trait_selection::infer::InferCtxtExt;
@@ -763,8 +767,13 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
763767
// Type inference for closures can depend on how they're called. Only go by the explicit
764768
// types here.
765769
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+
},
768777
})
769778
}),
770779
ExprKind::MethodCall(_, args, _) => {
@@ -807,8 +816,11 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
807816
}
808817
} else {
809818
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+
}
812824
}
813825
})
814826
},
@@ -931,6 +943,13 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
931943
v.0
932944
}
933945

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+
934953
// Checks whether:
935954
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
936955
// * `e`'s type implements `Trait` and is copyable
@@ -940,106 +959,132 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
940959
fn needless_borrow_impl_arg_position<'tcx>(
941960
cx: &LateContext<'tcx>,
942961
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 {
947967
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
948968

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) };
974970

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));
982973

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);
985980
}
986981

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.
989984
if cx
990985
.tcx
991986
.fn_sig(callee_def_id)
992987
.skip_binder()
993988
.inputs_and_output
994989
.iter()
995990
.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)))
997992
{
998-
return None;
993+
return Position::Other(precedence);
999994
}
1000995

1001996
let check_referent = |referent| {
1002997
let referent_ty = cx.typeck_results().expr_ty(referent);
1003998

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+
{
10051004
return false;
10061005
}
10071006

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;
10211053
}
1022-
needless_borrow
1023-
};
10241054

1025-
let Node::Expr(mut descendent) = cx.tcx.hir().get(child_id) else { return None };
1055+
true
1056+
};
10261057

10271058
let mut needless_borrow = false;
1028-
while let ExprKind::AddrOf(_, _, referent) = descendent.kind {
1059+
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
10291060
if !check_referent(referent) {
10301061
break;
10311062
}
1032-
descendent = referent;
1063+
expr = referent;
10331064
needless_borrow = true;
10341065
}
10351066

10361067
if needless_borrow {
1037-
Some(Position::ImplArg(descendent.hir_id))
1068+
Position::ImplArg(expr.hir_id)
10381069
} else {
1039-
None
1070+
Position::Other(precedence)
10401071
}
10411072
}
10421073

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+
10431088
// Checks whether a type is stable when switching to auto dereferencing,
10441089
fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
10451090
let ty::Ref(_, mut ty, _) = *ty.kind() else {

clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
33
use clippy_utils::diagnostics::span_lint_and_sugg;
44
use clippy_utils::source::snippet_opt;
55
use clippy_utils::ty::{
6-
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
6+
contains_ty, get_associated_type, get_input_traits_and_projections, get_iterator_item_ty, implements_trait,
7+
is_copy, peel_mid_ty_refs,
78
};
8-
use clippy_utils::{meets_msrv, msrvs};
9-
109
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
10+
use clippy_utils::{meets_msrv, msrvs};
1111
use rustc_errors::Applicability;
1212
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
1313
use rustc_lint::LateContext;
1414
use rustc_middle::mir::Mutability;
15+
use rustc_middle::ty;
1516
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
1617
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
17-
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
1818
use rustc_semver::RustcVersion;
1919
use rustc_span::{sym, Symbol};
2020
use std::cmp::max;
@@ -352,42 +352,6 @@ fn get_callee_substs_and_args<'tcx>(
352352
None
353353
}
354354

355-
/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
356-
fn get_input_traits_and_projections<'tcx>(
357-
cx: &LateContext<'tcx>,
358-
callee_def_id: DefId,
359-
input: Ty<'tcx>,
360-
) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
361-
let mut trait_predicates = Vec::new();
362-
let mut projection_predicates = Vec::new();
363-
for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
364-
// `substs` should have 1 + n elements. The first is the type on the left hand side of an
365-
// `as`. The remaining n are trait parameters.
366-
let is_input_substs = |substs: SubstsRef<'tcx>| {
367-
if_chain! {
368-
if let Some(arg) = substs.iter().next();
369-
if let GenericArgKind::Type(arg_ty) = arg.unpack();
370-
if arg_ty == input;
371-
then { true } else { false }
372-
}
373-
};
374-
match predicate.kind().skip_binder() {
375-
PredicateKind::Trait(trait_predicate) => {
376-
if is_input_substs(trait_predicate.trait_ref.substs) {
377-
trait_predicates.push(trait_predicate);
378-
}
379-
},
380-
PredicateKind::Projection(projection_predicate) => {
381-
if is_input_substs(projection_predicate.projection_ty.substs) {
382-
projection_predicates.push(projection_predicate);
383-
}
384-
},
385-
_ => {},
386-
}
387-
}
388-
(trait_predicates, projection_predicates)
389-
}
390-
391355
/// Composes two substitutions by applying the latter to the types of the former.
392356
fn compose_substs<'tcx>(
393357
cx: &LateContext<'tcx>,

clippy_utils/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ 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"];
1819
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
1920
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
2021
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];

0 commit comments

Comments
 (0)