Skip to content

Commit d1acbf5

Browse files
committed
extract supertrait collection to separate function
1 parent 28443e6 commit d1acbf5

File tree

1 file changed

+80
-76
lines changed

1 file changed

+80
-76
lines changed

clippy_lints/src/implied_bounds_in_impls.rs

Lines changed: 80 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
33
use rustc_errors::{Applicability, SuggestionStyle};
4-
use rustc_hir::def_id::{DefId, LocalDefId};
4+
use rustc_hir::def_id::DefId;
55
use rustc_hir::intravisit::FnKind;
66
use rustc_hir::{
7-
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
8-
TraitItemKind, TyKind,
7+
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
8+
TraitItem, TraitItemKind, TyKind, TypeBinding,
99
};
1010
use rustc_hir_analysis::hir_ty_to_ty;
1111
use rustc_lint::{LateContext, LateLintPass};
1212
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
1313
use rustc_session::declare_lint_pass;
14+
use rustc_span::def_id::LocalDefId;
1415
use rustc_span::Span;
1516

1617
declare_clippy_lint! {
@@ -50,20 +51,17 @@ declare_clippy_lint! {
5051
}
5152
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
5253

53-
#[allow(clippy::too_many_arguments)]
5454
fn emit_lint(
5555
cx: &LateContext<'_>,
5656
poly_trait: &rustc_hir::PolyTraitRef<'_>,
5757
opaque_ty: &rustc_hir::OpaqueTy<'_>,
5858
index: usize,
59-
// The bindings that were implied
59+
// The bindings that were implied, used for suggestion purposes since removing a bound with associated types
60+
// means we might need to then move it to a different bound
6061
implied_bindings: &[rustc_hir::TypeBinding<'_>],
61-
// The original bindings that `implied_bindings` are implied from
62-
implied_by_bindings: &[rustc_hir::TypeBinding<'_>],
63-
implied_by_args: &[GenericArg<'_>],
64-
implied_by_span: Span,
62+
bound: &ImplTraitBound<'_>,
6563
) {
66-
let implied_by = snippet(cx, implied_by_span, "..");
64+
let implied_by = snippet(cx, bound.impl_trait_bound_span, "..");
6765

6866
span_lint_and_then(
6967
cx,
@@ -93,17 +91,17 @@ fn emit_lint(
9391
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
9492
let omitted_assoc_tys: Vec<_> = implied_bindings
9593
.iter()
96-
.filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident))
94+
.filter(|binding| !bound.bindings.iter().any(|b| b.ident == binding.ident))
9795
.collect();
9896

9997
if !omitted_assoc_tys.is_empty() {
10098
// `<>` needs to be added if there aren't yet any generic arguments or bindings
101-
let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty();
102-
let insert_span = match (implied_by_args, implied_by_bindings) {
99+
let needs_angle_brackets = bound.args.is_empty() && bound.bindings.is_empty();
100+
let insert_span = match (bound.args, bound.bindings) {
103101
([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
104102
([.., arg], []) => arg.span().shrink_to_hi(),
105103
([], [.., binding]) => binding.span.shrink_to_hi(),
106-
([], []) => implied_by_span.shrink_to_hi(),
104+
([], []) => bound.impl_trait_bound_span.shrink_to_hi(),
107105
};
108106

109107
let mut associated_tys_sugg = if needs_angle_brackets {
@@ -223,42 +221,64 @@ fn is_same_generics<'tcx>(
223221
})
224222
}
225223

224+
struct ImplTraitBound<'tcx> {
225+
/// The span of the bound in the `impl Trait` type
226+
impl_trait_bound_span: Span,
227+
/// The predicates defined in the trait referenced by this bound
228+
predicates: &'tcx [(ty::Clause<'tcx>, Span)],
229+
/// The `DefId` of the trait being referenced by this bound
230+
trait_def_id: DefId,
231+
/// The generic arguments on the `impl Trait` bound
232+
args: &'tcx [GenericArg<'tcx>],
233+
/// The associated types on this bound
234+
bindings: &'tcx [TypeBinding<'tcx>],
235+
}
236+
237+
/// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
238+
///
239+
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
240+
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
241+
/// `Eq`.
242+
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
243+
opaque_ty
244+
.bounds
245+
.iter()
246+
.filter_map(|bound| {
247+
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
248+
&& let [.., path] = poly_trait.trait_ref.path.segments
249+
&& poly_trait.bound_generic_params.is_empty()
250+
&& let Some(trait_def_id) = path.res.opt_def_id()
251+
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
252+
// If the trait has no supertrait, there is no need to collect anything from that bound
253+
&& !predicates.is_empty()
254+
{
255+
Some(ImplTraitBound {
256+
predicates,
257+
args: path.args.map_or([].as_slice(), |p| p.args),
258+
bindings: path.args.map_or([].as_slice(), |p| p.bindings),
259+
trait_def_id,
260+
impl_trait_bound_span: bound.span(),
261+
})
262+
} else {
263+
None
264+
}
265+
})
266+
.collect()
267+
}
268+
226269
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
227270
if let FnRetTy::Return(ty) = decl.output
228-
&&let TyKind::OpaqueDef(item_id, ..) = ty.kind
271+
&& let TyKind::OpaqueDef(item_id, ..) = ty.kind
229272
&& let item = cx.tcx.hir().item(item_id)
230273
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
231274
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232275
// we can avoid doing a bunch of stuff unnecessarily.
233276
&& opaque_ty.bounds.len() > 1
234277
{
235-
// Get all the (implied) trait predicates in the bounds.
236-
// For `impl Deref + DerefMut` this will contain [`Deref`].
237-
// The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
238-
// N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
239-
// Example:
240-
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
241-
// `DerefMut::Target` needs to match `Deref::Target`.
242-
let implied_bounds: Vec<_> = opaque_ty
243-
.bounds
244-
.iter()
245-
.filter_map(|bound| {
246-
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
247-
&& let [.., path] = poly_trait.trait_ref.path.segments
248-
&& poly_trait.bound_generic_params.is_empty()
249-
&& let Some(trait_def_id) = path.res.opt_def_id()
250-
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
251-
&& !predicates.is_empty()
252-
// If the trait has no supertrait, there is nothing to add.
253-
{
254-
Some((bound.span(), path, predicates, trait_def_id))
255-
} else {
256-
None
257-
}
258-
})
259-
.collect();
278+
let supertraits = collect_supertrait_bounds(cx, opaque_ty);
260279

261-
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
280+
// Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
281+
// supertraits of each of the bounds.
262282
// This involves some extra logic when generic arguments are present, since
263283
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264284
for (index, bound) in opaque_ty.bounds.iter().enumerate() {
@@ -267,42 +287,26 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267287
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
268288
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
269289
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
270-
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) =
271-
implied_bounds
272-
.iter()
273-
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
274-
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
275-
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
276-
277-
preds.iter().find_map(|(clause, _)| {
278-
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
279-
&& tr.def_id() == def_id
280-
&& is_same_generics(
281-
cx.tcx,
282-
tr.trait_ref.args,
283-
implied_by_args,
284-
implied_args,
285-
implied_by_def_id,
286-
def_id,
287-
)
288-
{
289-
Some((span, implied_by_args, implied_by_bindings))
290-
} else {
291-
None
292-
}
293-
})
294-
})
290+
&& let Some(bound) = supertraits.iter().find(|bound| {
291+
bound.predicates.iter().any(|(clause, _)| {
292+
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
293+
&& tr.def_id() == def_id
294+
{
295+
is_same_generics(
296+
cx.tcx,
297+
tr.trait_ref.args,
298+
bound.args,
299+
implied_args,
300+
bound.trait_def_id,
301+
def_id,
302+
)
303+
} else {
304+
false
305+
}
306+
})
307+
})
295308
{
296-
emit_lint(
297-
cx,
298-
poly_trait,
299-
opaque_ty,
300-
index,
301-
implied_bindings,
302-
implied_by_bindings,
303-
implied_by_args,
304-
implied_by_span,
305-
);
309+
emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
306310
}
307311
}
308312
}

0 commit comments

Comments
 (0)