Skip to content

Commit d75101d

Browse files
committed
Rework redundant_closure
* Better track when a early-bound region appears when a late-bound region is required * Don't lint when the closure gives explicit types.
1 parent 3d193fa commit d75101d

File tree

10 files changed

+468
-215
lines changed

10 files changed

+468
-215
lines changed

clippy_lints/src/derive.rs

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

clippy_lints/src/eta_reduction.rs

+207-122
Large diffs are not rendered by default.

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;
@@ -190,7 +190,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
190190
if !ty.is_mutable_ptr();
191191
if !is_copy(cx, ty);
192192
if ty.is_sized(cx.tcx, cx.param_env);
193-
if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None]));
193+
if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter(
194+
cx.tcx,
195+
cx.param_env,
196+
ty,
197+
t,
198+
[Option::<ty::GenericArg<'tcx>>::None],
199+
));
194200
if !implements_borrow_trait;
195201
if !all_borrowable_trait;
196202

clippy_utils/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(array_chunks)]
22
#![feature(box_patterns)]
3+
#![feature(is_some_and)]
34
#![feature(let_chains)]
45
#![feature(lint_reasons)]
56
#![feature(never_type)]

clippy_utils/src/ty.rs

+100-66
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;
@@ -15,17 +16,18 @@ use rustc_infer::infer::{
1516
};
1617
use rustc_lint::LateContext;
1718
use rustc_middle::mir::interpret::{ConstValue, Scalar};
19+
use rustc_middle::traits::EvaluationResult;
1820
use rustc_middle::ty::{
19-
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate,
20-
PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
21-
TypeVisitor, UintTy, VariantDef, VariantDiscr,
21+
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericArg, GenericArgKind,
22+
GenericParamDefKind, IntTy, List, ParamEnv, Predicate, PredicateKind, Region, RegionKind, SubstsRef, ToPredicate,
23+
Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
2224
};
23-
use rustc_middle::ty::{GenericArg, GenericArgKind};
2425
use rustc_span::symbol::Ident;
2526
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
2627
use rustc_target::abi::{Size, VariantIdx};
27-
use rustc_trait_selection::infer::InferCtxtExt;
28+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2829
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
30+
use rustc_trait_selection::traits::{Obligation, ObligationCause};
2931
use std::iter;
3032

3133
use crate::{match_def_path, path_res, paths};
@@ -206,15 +208,9 @@ pub fn implements_trait<'tcx>(
206208
cx: &LateContext<'tcx>,
207209
ty: Ty<'tcx>,
208210
trait_id: DefId,
209-
ty_params: &[GenericArg<'tcx>],
211+
substs: &[GenericArg<'tcx>],
210212
) -> bool {
211-
implements_trait_with_env(
212-
cx.tcx,
213-
cx.param_env,
214-
ty,
215-
trait_id,
216-
ty_params.iter().map(|&arg| Some(arg)),
217-
)
213+
implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, substs.iter().map(|&x| Some(x)))
218214
}
219215

220216
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -223,7 +219,18 @@ pub fn implements_trait_with_env<'tcx>(
223219
param_env: ParamEnv<'tcx>,
224220
ty: Ty<'tcx>,
225221
trait_id: DefId,
226-
ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
222+
substs: &[GenericArg<'tcx>],
223+
) -> bool {
224+
implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, substs.iter().map(|&x| Some(x)))
225+
}
226+
227+
/// Same as `implements_trait_from_env` but takes the substitutions as an iterator.
228+
pub fn implements_trait_with_env_from_iter<'tcx>(
229+
tcx: TyCtxt<'tcx>,
230+
param_env: ParamEnv<'tcx>,
231+
ty: Ty<'tcx>,
232+
trait_id: DefId,
233+
substs: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>,
227234
) -> bool {
228235
// Clippy shouldn't have infer types
229236
assert!(!ty.needs_infer());
@@ -232,19 +239,36 @@ pub fn implements_trait_with_env<'tcx>(
232239
if ty.has_escaping_bound_vars() {
233240
return false;
234241
}
242+
235243
let infcx = tcx.infer_ctxt().build();
236-
let orig = TypeVariableOrigin {
237-
kind: TypeVariableOriginKind::MiscVariable,
238-
span: DUMMY_SP,
239-
};
240-
let ty_params = tcx.mk_substs(
241-
ty_params
244+
let trait_ref = tcx.mk_trait_ref(
245+
trait_id,
246+
Some(GenericArg::from(ty))
242247
.into_iter()
243-
.map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
248+
.chain(substs.into_iter().map(|subst| {
249+
subst.into().unwrap_or_else(|| {
250+
let orig = TypeVariableOrigin {
251+
kind: TypeVariableOriginKind::MiscVariable,
252+
span: DUMMY_SP,
253+
};
254+
infcx.next_ty_var(orig).into()
255+
})
256+
})),
244257
);
258+
259+
debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
260+
#[cfg(debug_assertions)]
261+
assert_substs_match(tcx, trait_id, trait_ref.substs);
262+
263+
let obligation = Obligation {
264+
cause: ObligationCause::dummy(),
265+
param_env,
266+
recursion_depth: 0,
267+
predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
268+
};
245269
infcx
246-
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
247-
.must_apply_modulo_regions()
270+
.evaluate_obligation(&obligation)
271+
.is_ok_and(EvaluationResult::must_apply_modulo_regions)
248272
}
249273

250274
/// Checks whether this type implements `Drop`.
@@ -392,6 +416,11 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangI
392416
}
393417
}
394418

419+
/// Gets the diagnostic name of the type, if it has one
420+
pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
421+
ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did()))
422+
}
423+
395424
/// Return `true` if the passed `typ` is `isize` or `usize`.
396425
pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
397426
matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
@@ -1001,6 +1030,53 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
10011030
}
10021031
}
10031032

1033+
/// Asserts that the given substitutions matches the generic parameters of the given item.
1034+
fn assert_substs_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, substs: &[GenericArg<'tcx>]) {
1035+
let g = tcx.generics_of(did);
1036+
let parent = g.parent.map(|did| tcx.generics_of(did));
1037+
let count = g.parent_count + g.params.len();
1038+
let params = parent
1039+
.map_or([].as_slice(), |p| p.params.as_slice())
1040+
.iter()
1041+
.chain(&g.params)
1042+
.map(|x| &x.kind);
1043+
1044+
assert!(
1045+
count == substs.len(),
1046+
"wrong number substs for `{did:?}`: expected `{count}`, found {}\n\
1047+
note: the expected parameters are: `[{}]`\n\
1048+
the given substs are: `{substs:#?}`",
1049+
substs.len(),
1050+
params.clone().map(GenericParamDefKind::descr).format(", "),
1051+
);
1052+
1053+
if let Some((idx, (param, arg))) =
1054+
params
1055+
.clone()
1056+
.zip(substs.iter().map(|&x| x.unpack()))
1057+
.enumerate()
1058+
.find(|(_, (param, arg))| match (param, arg) {
1059+
(GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1060+
| (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1061+
| (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false,
1062+
(
1063+
GenericParamDefKind::Lifetime
1064+
| GenericParamDefKind::Type { .. }
1065+
| GenericParamDefKind::Const { .. },
1066+
_,
1067+
) => true,
1068+
})
1069+
{
1070+
panic!(
1071+
"incorrect subst for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\
1072+
note: the expected parameters are `[{}]`\n\
1073+
the given arguments are `{substs:#?}`",
1074+
param.descr(),
1075+
params.clone().map(GenericParamDefKind::descr).format(", "),
1076+
);
1077+
}
1078+
}
1079+
10041080
/// Makes the projection type for the named associated type in the given impl or trait impl.
10051081
///
10061082
/// This function is for associated types which are "known" to exist, and as such, will only return
@@ -1028,49 +1104,7 @@ pub fn make_projection<'tcx>(
10281104
return None;
10291105
};
10301106
#[cfg(debug_assertions)]
1031-
{
1032-
let generics = tcx.generics_of(assoc_item.def_id);
1033-
let generic_count = generics.parent_count + generics.params.len();
1034-
let params = generics
1035-
.parent
1036-
.map_or([].as_slice(), |id| &*tcx.generics_of(id).params)
1037-
.iter()
1038-
.chain(&generics.params)
1039-
.map(|x| &x.kind);
1040-
1041-
debug_assert!(
1042-
generic_count == substs.len(),
1043-
"wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
1044-
note: the expected parameters are: {:#?}\n\
1045-
the given arguments are: `{substs:#?}`",
1046-
assoc_item.def_id,
1047-
substs.len(),
1048-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
1049-
);
1050-
1051-
if let Some((idx, (param, arg))) = params
1052-
.clone()
1053-
.zip(substs.iter().map(GenericArg::unpack))
1054-
.enumerate()
1055-
.find(|(_, (param, arg))| {
1056-
!matches!(
1057-
(param, arg),
1058-
(ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1059-
| (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1060-
| (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
1061-
)
1062-
})
1063-
{
1064-
debug_assert!(
1065-
false,
1066-
"mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
1067-
note: the expected parameters are {:#?}\n\
1068-
the given arguments are {substs:#?}",
1069-
param.descr(),
1070-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
1071-
);
1072-
}
1073-
}
1107+
assert_substs_match(tcx, assoc_item.def_id, substs);
10741108

10751109
Some(tcx.mk_alias_ty(assoc_item.def_id, substs))
10761110
}

clippy_utils/src/usage.rs

+23-22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate as utils;
2-
use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend};
2+
use crate::get_enclosing_loop_or_multi_call_closure;
3+
use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend, Visitable};
34
use core::ops::ControlFlow;
45
use rustc_hir as hir;
56
use rustc_hir::intravisit::{self, Visitor};
67
use rustc_hir::HirIdSet;
7-
use rustc_hir::{Expr, ExprKind, HirId, Node};
8+
use rustc_hir::{Expr, ExprKind, HirId};
89
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
910
use rustc_infer::infer::TyCtxtInferExt;
1011
use rustc_lint::LateContext;
@@ -155,6 +156,17 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
155156
.is_some()
156157
}
157158

159+
pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool {
160+
for_each_expr_with_closures(cx, v, |e| {
161+
if utils::path_to_local_id(e, local_id) {
162+
ControlFlow::Break(())
163+
} else {
164+
ControlFlow::Continue(())
165+
}
166+
})
167+
.is_some()
168+
}
169+
158170
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
159171
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
160172

@@ -165,32 +177,21 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
165177
// let closure = || local;
166178
// closure();
167179
// closure();
168-
let in_loop_or_closure = cx
169-
.tcx
170-
.hir()
171-
.parent_iter(after.hir_id)
172-
.take_while(|&(id, _)| id != block.hir_id)
173-
.any(|(_, node)| {
174-
matches!(
175-
node,
176-
Node::Expr(Expr {
177-
kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
178-
..
179-
})
180-
)
181-
});
182-
if in_loop_or_closure {
183-
return true;
184-
}
180+
let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id);
185181

186182
let mut past_expr = false;
187183
for_each_expr_with_closures(cx, block, |e| {
188-
if e.hir_id == after.hir_id {
184+
if past_expr {
185+
if utils::path_to_local_id(e, local_id) {
186+
ControlFlow::Break(())
187+
} else {
188+
ControlFlow::Continue(Descend::Yes)
189+
}
190+
} else if e.hir_id == after.hir_id {
189191
past_expr = true;
190192
ControlFlow::Continue(Descend::No)
191-
} else if past_expr && utils::path_to_local_id(e, local_id) {
192-
ControlFlow::Break(())
193193
} else {
194+
past_expr = Some(e.hir_id) == loop_start;
194195
ControlFlow::Continue(Descend::Yes)
195196
}
196197
})

clippy_utils/src/visitors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ pub trait Visitable<'tcx> {
5252
/// Calls the corresponding `visit_*` function on the visitor.
5353
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
5454
}
55+
impl<'tcx, T> Visitable<'tcx> for &'tcx [T]
56+
where
57+
&'tcx T: Visitable<'tcx>,
58+
{
59+
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
60+
for x in self {
61+
x.visit(visitor);
62+
}
63+
}
64+
}
5565
macro_rules! visitable_ref {
5666
($t:ident, $f:ident) => {
5767
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {

0 commit comments

Comments
 (0)