Skip to content

Commit 4c2f460

Browse files
committed
Auto merge of rust-lang#8685 - Jarcho:redundant_closure_fixes, r=llogiq
`redundant_closure` fixes fixes rust-lang#8548 A good chunk of the code is fixing false negatives. The old code banned any non late-bound regions from appearing in the callee's signature. The new version checks when the late-bound region is actually required. changelog: Better track when a early-bound region appears when a late-bound region is required in `redundant_closure`. changelog: Don't lint `redundant_closure` when the closure gives explicit types.
2 parents 2973096 + 7423c27 commit 4c2f460

File tree

12 files changed

+476
-246
lines changed

12 files changed

+476
-246
lines changed

clippy_lints/src/derive.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -471,12 +471,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
471471
if let Some(def_id) = trait_ref.trait_def_id();
472472
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
473473
let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
474-
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []);
474+
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
475475
// If all of our fields implement `Eq`, we can implement `Eq` too
476476
if adt
477477
.all_fields()
478478
.map(|f| f.ty(cx.tcx, args))
479-
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []));
479+
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
480480
then {
481481
span_lint_and_sugg(
482482
cx,

clippy_lints/src/eta_reduction.rs

+205-129
Large diffs are not rendered by default.

clippy_lints/src/incorrect_impls.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,7 @@ impl LateLintPass<'_> for IncorrectImpls {
189189
.diagnostic_items(trait_impl.def_id.krate)
190190
.name_to_id
191191
.get(&sym::Ord)
192-
&& implements_trait(
193-
cx,
194-
hir_ty_to_ty(cx.tcx, imp.self_ty),
195-
*ord_def_id,
196-
trait_impl.args,
197-
)
192+
&& implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[])
198193
{
199194
// If the `cmp` call likely needs to be fully qualified in the suggestion
200195
// (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't

clippy_lints/src/loops/explicit_iter_loop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ fn is_ref_iterable<'tcx>(
109109
&& let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
110110
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
111111
&& let param_env = cx.tcx.param_env(fn_id)
112-
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, [])
112+
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[])
113113
&& let Some(into_iter_ty) =
114114
make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
115115
&& let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)

clippy_lints/src/methods/or_fun_call.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub(super) fn check<'tcx>(
5757
cx.tcx
5858
.get_diagnostic_item(sym::Default)
5959
.map_or(false, |default_trait_id| {
60-
implements_trait(cx, output_ty, default_trait_id, args)
60+
implements_trait(cx, output_ty, default_trait_id, &[])
6161
})
6262
} else {
6363
false

clippy_lints/src/needless_pass_by_value.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
22
use clippy_utils::ptr::get_spans;
33
use clippy_utils::source::{snippet, snippet_opt};
44
use clippy_utils::ty::{
5-
implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
5+
implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
66
};
77
use clippy_utils::{get_trait_def_id, is_self, paths};
88
use if_chain::if_chain;
@@ -182,7 +182,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
182182
if !ty.is_mutable_ptr();
183183
if !is_copy(cx, ty);
184184
if ty.is_sized(cx.tcx, cx.param_env);
185-
if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None]));
185+
if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter(
186+
cx.tcx,
187+
cx.param_env,
188+
ty,
189+
t,
190+
[Option::<ty::GenericArg<'tcx>>::None],
191+
));
186192
if !implements_borrow_trait;
187193
if !all_borrowable_trait;
188194

clippy_utils/src/ty.rs

+108-81
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(clippy::module_name_repetitions)]
44

55
use core::ops::ControlFlow;
6+
use itertools::Itertools;
67
use rustc_ast::ast::Mutability;
78
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
89
use rustc_hir as hir;
@@ -13,17 +14,19 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
1314
use rustc_infer::infer::TyCtxtInferExt;
1415
use rustc_lint::LateContext;
1516
use rustc_middle::mir::interpret::{ConstValue, Scalar};
17+
use rustc_middle::traits::EvaluationResult;
1618
use rustc_middle::ty::layout::ValidityRequirement;
1719
use rustc_middle::ty::{
18-
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy,
19-
List, ParamEnv, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
20-
UintTy, VariantDef, VariantDiscr,
20+
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
21+
GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
22+
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
2123
};
2224
use rustc_span::symbol::Ident;
2325
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
2426
use rustc_target::abi::{Size, VariantIdx};
25-
use rustc_trait_selection::infer::InferCtxtExt;
27+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2628
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
29+
use rustc_trait_selection::traits::{Obligation, ObligationCause};
2730
use std::iter;
2831

2932
use crate::{match_def_path, path_res, paths};
@@ -207,15 +210,9 @@ pub fn implements_trait<'tcx>(
207210
cx: &LateContext<'tcx>,
208211
ty: Ty<'tcx>,
209212
trait_id: DefId,
210-
ty_params: &[GenericArg<'tcx>],
213+
args: &[GenericArg<'tcx>],
211214
) -> bool {
212-
implements_trait_with_env(
213-
cx.tcx,
214-
cx.param_env,
215-
ty,
216-
trait_id,
217-
ty_params.iter().map(|&arg| Some(arg)),
218-
)
215+
implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, args.iter().map(|&x| Some(x)))
219216
}
220217

221218
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -224,7 +221,18 @@ pub fn implements_trait_with_env<'tcx>(
224221
param_env: ParamEnv<'tcx>,
225222
ty: Ty<'tcx>,
226223
trait_id: DefId,
227-
ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
224+
args: &[GenericArg<'tcx>],
225+
) -> bool {
226+
implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, args.iter().map(|&x| Some(x)))
227+
}
228+
229+
/// Same as `implements_trait_from_env` but takes the arguments as an iterator.
230+
pub fn implements_trait_with_env_from_iter<'tcx>(
231+
tcx: TyCtxt<'tcx>,
232+
param_env: ParamEnv<'tcx>,
233+
ty: Ty<'tcx>,
234+
trait_id: DefId,
235+
args: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>,
228236
) -> bool {
229237
// Clippy shouldn't have infer types
230238
assert!(!ty.has_infer());
@@ -233,19 +241,37 @@ pub fn implements_trait_with_env<'tcx>(
233241
if ty.has_escaping_bound_vars() {
234242
return false;
235243
}
244+
236245
let infcx = tcx.infer_ctxt().build();
237-
let orig = TypeVariableOrigin {
238-
kind: TypeVariableOriginKind::MiscVariable,
239-
span: DUMMY_SP,
240-
};
241-
let ty_params = tcx.mk_args_from_iter(
242-
ty_params
246+
let trait_ref = TraitRef::new(
247+
tcx,
248+
trait_id,
249+
Some(GenericArg::from(ty))
243250
.into_iter()
244-
.map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
251+
.chain(args.into_iter().map(|arg| {
252+
arg.into().unwrap_or_else(|| {
253+
let orig = TypeVariableOrigin {
254+
kind: TypeVariableOriginKind::MiscVariable,
255+
span: DUMMY_SP,
256+
};
257+
infcx.next_ty_var(orig).into()
258+
})
259+
})),
245260
);
261+
262+
debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
263+
#[cfg(debug_assertions)]
264+
assert_generic_args_match(tcx, trait_id, trait_ref.args);
265+
266+
let obligation = Obligation {
267+
cause: ObligationCause::dummy(),
268+
param_env,
269+
recursion_depth: 0,
270+
predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
271+
};
246272
infcx
247-
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
248-
.must_apply_modulo_regions()
273+
.evaluate_obligation(&obligation)
274+
.is_ok_and(EvaluationResult::must_apply_modulo_regions)
249275
}
250276

251277
/// Checks whether this type implements `Drop`.
@@ -393,6 +419,11 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangI
393419
}
394420
}
395421

422+
/// Gets the diagnostic name of the type, if it has one
423+
pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
424+
ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did()))
425+
}
426+
396427
/// Return `true` if the passed `typ` is `isize` or `usize`.
397428
pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
398429
matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
@@ -1014,12 +1045,60 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
10141045
}
10151046
}
10161047

1048+
/// Asserts that the given arguments match the generic parameters of the given item.
1049+
#[allow(dead_code)]
1050+
fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) {
1051+
let g = tcx.generics_of(did);
1052+
let parent = g.parent.map(|did| tcx.generics_of(did));
1053+
let count = g.parent_count + g.params.len();
1054+
let params = parent
1055+
.map_or([].as_slice(), |p| p.params.as_slice())
1056+
.iter()
1057+
.chain(&g.params)
1058+
.map(|x| &x.kind);
1059+
1060+
assert!(
1061+
count == args.len(),
1062+
"wrong number of arguments for `{did:?}`: expected `{count}`, found {}\n\
1063+
note: the expected arguments are: `[{}]`\n\
1064+
the given arguments are: `{args:#?}`",
1065+
args.len(),
1066+
params.clone().map(GenericParamDefKind::descr).format(", "),
1067+
);
1068+
1069+
if let Some((idx, (param, arg))) =
1070+
params
1071+
.clone()
1072+
.zip(args.iter().map(|&x| x.unpack()))
1073+
.enumerate()
1074+
.find(|(_, (param, arg))| match (param, arg) {
1075+
(GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1076+
| (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1077+
| (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false,
1078+
(
1079+
GenericParamDefKind::Lifetime
1080+
| GenericParamDefKind::Type { .. }
1081+
| GenericParamDefKind::Const { .. },
1082+
_,
1083+
) => true,
1084+
})
1085+
{
1086+
panic!(
1087+
"incorrect argument for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\
1088+
note: the expected arguments are `[{}]`\n\
1089+
the given arguments are `{args:#?}`",
1090+
param.descr(),
1091+
params.clone().map(GenericParamDefKind::descr).format(", "),
1092+
);
1093+
}
1094+
}
1095+
10171096
/// Makes the projection type for the named associated type in the given impl or trait impl.
10181097
///
10191098
/// This function is for associated types which are "known" to exist, and as such, will only return
10201099
/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
10211100
/// enabled this will check that the named associated type exists, the correct number of
1022-
/// substitutions are given, and that the correct kinds of substitutions are given (lifetime,
1101+
/// arguments are given, and that the correct kinds of arguments are given (lifetime,
10231102
/// constant or type). This will not check if type normalization would succeed.
10241103
pub fn make_projection<'tcx>(
10251104
tcx: TyCtxt<'tcx>,
@@ -1043,49 +1122,7 @@ pub fn make_projection<'tcx>(
10431122
return None;
10441123
};
10451124
#[cfg(debug_assertions)]
1046-
{
1047-
let generics = tcx.generics_of(assoc_item.def_id);
1048-
let generic_count = generics.parent_count + generics.params.len();
1049-
let params = generics
1050-
.parent
1051-
.map_or([].as_slice(), |id| &*tcx.generics_of(id).params)
1052-
.iter()
1053-
.chain(&generics.params)
1054-
.map(|x| &x.kind);
1055-
1056-
debug_assert!(
1057-
generic_count == args.len(),
1058-
"wrong number of args for `{:?}`: found `{}` expected `{generic_count}`.\n\
1059-
note: the expected parameters are: {:#?}\n\
1060-
the given arguments are: `{args:#?}`",
1061-
assoc_item.def_id,
1062-
args.len(),
1063-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
1064-
);
1065-
1066-
if let Some((idx, (param, arg))) = params
1067-
.clone()
1068-
.zip(args.iter().map(GenericArg::unpack))
1069-
.enumerate()
1070-
.find(|(_, (param, arg))| {
1071-
!matches!(
1072-
(param, arg),
1073-
(ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1074-
| (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1075-
| (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
1076-
)
1077-
})
1078-
{
1079-
debug_assert!(
1080-
false,
1081-
"mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
1082-
note: the expected parameters are {:#?}\n\
1083-
the given arguments are {args:#?}",
1084-
param.descr(),
1085-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
1086-
);
1087-
}
1088-
}
1125+
assert_generic_args_match(tcx, assoc_item.def_id, args);
10891126

10901127
Some(tcx.mk_alias_ty(assoc_item.def_id, args))
10911128
}
@@ -1100,7 +1137,7 @@ pub fn make_projection<'tcx>(
11001137
/// Normalizes the named associated type in the given impl or trait impl.
11011138
///
11021139
/// This function is for associated types which are "known" to be valid with the given
1103-
/// substitutions, and as such, will only return `None` when debug assertions are disabled in order
1140+
/// arguments, and as such, will only return `None` when debug assertions are disabled in order
11041141
/// to prevent ICE's. With debug assertions enabled this will check that type normalization
11051142
/// succeeds as well as everything checked by `make_projection`.
11061143
pub fn make_normalized_projection<'tcx>(
@@ -1112,17 +1149,12 @@ pub fn make_normalized_projection<'tcx>(
11121149
) -> Option<Ty<'tcx>> {
11131150
fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
11141151
#[cfg(debug_assertions)]
1115-
if let Some((i, subst)) = ty
1116-
.args
1117-
.iter()
1118-
.enumerate()
1119-
.find(|(_, subst)| subst.has_late_bound_regions())
1120-
{
1152+
if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
11211153
debug_assert!(
11221154
false,
11231155
"args contain late-bound region at index `{i}` which can't be normalized.\n\
11241156
use `TyCtxt::erase_late_bound_regions`\n\
1125-
note: subst is `{subst:#?}`",
1157+
note: arg is `{arg:#?}`",
11261158
);
11271159
return None;
11281160
}
@@ -1190,17 +1222,12 @@ pub fn make_normalized_projection_with_regions<'tcx>(
11901222
) -> Option<Ty<'tcx>> {
11911223
fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
11921224
#[cfg(debug_assertions)]
1193-
if let Some((i, subst)) = ty
1194-
.args
1195-
.iter()
1196-
.enumerate()
1197-
.find(|(_, subst)| subst.has_late_bound_regions())
1198-
{
1225+
if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
11991226
debug_assert!(
12001227
false,
12011228
"args contain late-bound region at index `{i}` which can't be normalized.\n\
12021229
use `TyCtxt::erase_late_bound_regions`\n\
1203-
note: subst is `{subst:#?}`",
1230+
note: arg is `{arg:#?}`",
12041231
);
12051232
return None;
12061233
}

0 commit comments

Comments
 (0)