Skip to content

Commit bee42e8

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 476efe9 commit bee42e8

File tree

9 files changed

+477
-222
lines changed

9 files changed

+477
-222
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

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

clippy_utils/src/ty.rs

+103-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,19 @@ 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, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv,
20-
Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
21-
TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
21+
self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg,
22+
GenericArgKind, GenericParamDefKind, IntTy, List, ParamEnv, Predicate, PredicateKind, Region, RegionKind,
23+
SubstsRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
24+
UintTy, VariantDef, VariantDiscr,
2225
};
23-
use rustc_middle::ty::{GenericArg, GenericArgKind};
2426
use rustc_span::symbol::Ident;
2527
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
2628
use rustc_target::abi::{Size, VariantIdx};
27-
use rustc_trait_selection::infer::InferCtxtExt;
29+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2830
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
31+
use rustc_trait_selection::traits::{Obligation, ObligationCause};
2932
use std::iter;
3033

3134
use crate::{match_def_path, path_res, paths};
@@ -206,15 +209,9 @@ pub fn implements_trait<'tcx>(
206209
cx: &LateContext<'tcx>,
207210
ty: Ty<'tcx>,
208211
trait_id: DefId,
209-
ty_params: &[GenericArg<'tcx>],
212+
substs: &[GenericArg<'tcx>],
210213
) -> 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-
)
214+
implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, substs.iter().map(|&x| Some(x)))
218215
}
219216

220217
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -223,7 +220,18 @@ pub fn implements_trait_with_env<'tcx>(
223220
param_env: ParamEnv<'tcx>,
224221
ty: Ty<'tcx>,
225222
trait_id: DefId,
226-
ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
223+
substs: &[GenericArg<'tcx>],
224+
) -> bool {
225+
implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, substs.iter().map(|&x| Some(x)))
226+
}
227+
228+
/// Same as `implements_trait_from_env` but takes the substitutions as an iterator.
229+
pub fn implements_trait_with_env_from_iter<'tcx>(
230+
tcx: TyCtxt<'tcx>,
231+
param_env: ParamEnv<'tcx>,
232+
ty: Ty<'tcx>,
233+
trait_id: DefId,
234+
substs: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>,
227235
) -> bool {
228236
// Clippy shouldn't have infer types
229237
assert!(!ty.has_infer());
@@ -232,19 +240,37 @@ pub fn implements_trait_with_env<'tcx>(
232240
if ty.has_escaping_bound_vars() {
233241
return false;
234242
}
243+
235244
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_from_iter(
241-
ty_params
245+
let trait_ref = TraitRef::new(
246+
tcx,
247+
trait_id,
248+
Some(GenericArg::from(ty))
242249
.into_iter()
243-
.map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
250+
.chain(substs.into_iter().map(|subst| {
251+
subst.into().unwrap_or_else(|| {
252+
let orig = TypeVariableOrigin {
253+
kind: TypeVariableOriginKind::MiscVariable,
254+
span: DUMMY_SP,
255+
};
256+
infcx.next_ty_var(orig).into()
257+
})
258+
})),
244259
);
260+
261+
debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
262+
#[cfg(debug_assertions)]
263+
assert_substs_match(tcx, trait_id, trait_ref.substs);
264+
265+
let obligation = Obligation {
266+
cause: ObligationCause::dummy(),
267+
param_env,
268+
recursion_depth: 0,
269+
predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
270+
};
245271
infcx
246-
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
247-
.must_apply_modulo_regions()
272+
.evaluate_obligation(&obligation)
273+
.is_ok_and(EvaluationResult::must_apply_modulo_regions)
248274
}
249275

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

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

1050+
/// Asserts that the given substitutions matches the generic parameters of the given item.
1051+
#[allow(dead_code)]
1052+
fn assert_substs_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, substs: &[GenericArg<'tcx>]) {
1053+
let g = tcx.generics_of(did);
1054+
let parent = g.parent.map(|did| tcx.generics_of(did));
1055+
let count = g.parent_count + g.params.len();
1056+
let params = parent
1057+
.map_or([].as_slice(), |p| p.params.as_slice())
1058+
.iter()
1059+
.chain(&g.params)
1060+
.map(|x| &x.kind);
1061+
1062+
assert!(
1063+
count == substs.len(),
1064+
"wrong number substs for `{did:?}`: expected `{count}`, found {}\n\
1065+
note: the expected parameters are: `[{}]`\n\
1066+
the given substs are: `{substs:#?}`",
1067+
substs.len(),
1068+
params.clone().map(GenericParamDefKind::descr).format(", "),
1069+
);
1070+
1071+
if let Some((idx, (param, arg))) =
1072+
params
1073+
.clone()
1074+
.zip(substs.iter().map(|&x| x.unpack()))
1075+
.enumerate()
1076+
.find(|(_, (param, arg))| match (param, arg) {
1077+
(GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1078+
| (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1079+
| (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false,
1080+
(
1081+
GenericParamDefKind::Lifetime
1082+
| GenericParamDefKind::Type { .. }
1083+
| GenericParamDefKind::Const { .. },
1084+
_,
1085+
) => true,
1086+
})
1087+
{
1088+
panic!(
1089+
"incorrect subst for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\
1090+
note: the expected parameters are `[{}]`\n\
1091+
the given arguments are `{substs:#?}`",
1092+
param.descr(),
1093+
params.clone().map(GenericParamDefKind::descr).format(", "),
1094+
);
1095+
}
1096+
}
1097+
10191098
/// Makes the projection type for the named associated type in the given impl or trait impl.
10201099
///
10211100
/// This function is for associated types which are "known" to exist, and as such, will only return
@@ -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 == substs.len(),
1058-
"wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
1059-
note: the expected parameters are: {:#?}\n\
1060-
the given arguments are: `{substs:#?}`",
1061-
assoc_item.def_id,
1062-
substs.len(),
1063-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
1064-
);
1065-
1066-
if let Some((idx, (param, arg))) = params
1067-
.clone()
1068-
.zip(substs.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 {substs:#?}",
1084-
param.descr(),
1085-
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
1086-
);
1087-
}
1088-
}
1125+
assert_substs_match(tcx, assoc_item.def_id, substs);
10891126

10901127
Some(tcx.mk_alias_ty(assoc_item.def_id, substs))
10911128
}

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)