Skip to content

Commit 9a253fa

Browse files
committed
Auto merge of #12269 - y21:implied_bounds_in_impls_refactor, r=xFrednet
Refactor `implied_bounds_in_impls` lint Some refactors in `implied_bounds_in_impls` that I wanted to make while working on something else in that file, but I found them "large" enough that I didn't want them in the same PR and instead wanted them reviewed separately (since itd just be distracting). This just splits up the two phases of "collect all the supertraits from each of the `impl Trait` bounds" and "find those `impl Trait` bounds that are mentioned in one of the previously-collected supertraits" into separate functions. Before, this was all in a single function. Reviewing it commit by commit might make it easier. I can squash it down later. changelog: none
2 parents 9b20212 + a38f44c commit 9a253fa

File tree

1 file changed

+92
-76
lines changed

1 file changed

+92
-76
lines changed

clippy_lints/src/implied_bounds_in_impls.rs

Lines changed: 92 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.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.span.shrink_to_hi(),
107105
};
108106

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

224+
struct ImplTraitBound<'tcx> {
225+
/// The span of the bound in the `impl Trait` type
226+
span: Span,
227+
/// The predicates defined in the trait referenced by this bound. This also contains the actual
228+
/// supertrait bounds
229+
predicates: &'tcx [(ty::Clause<'tcx>, Span)],
230+
/// The `DefId` of the trait being referenced by this bound
231+
trait_def_id: DefId,
232+
/// The generic arguments on the `impl Trait` bound
233+
args: &'tcx [GenericArg<'tcx>],
234+
/// The associated types on this bound
235+
bindings: &'tcx [TypeBinding<'tcx>],
236+
}
237+
238+
/// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
239+
///
240+
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
241+
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
242+
/// `Eq`.
243+
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
244+
opaque_ty
245+
.bounds
246+
.iter()
247+
.filter_map(|bound| {
248+
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
249+
&& let [.., path] = poly_trait.trait_ref.path.segments
250+
&& poly_trait.bound_generic_params.is_empty()
251+
&& let Some(trait_def_id) = path.res.opt_def_id()
252+
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
253+
// If the trait has no supertrait, there is no need to collect anything from that bound
254+
&& !predicates.is_empty()
255+
{
256+
Some(ImplTraitBound {
257+
predicates,
258+
args: path.args.map_or([].as_slice(), |p| p.args),
259+
bindings: path.args.map_or([].as_slice(), |p| p.bindings),
260+
trait_def_id,
261+
span: bound.span(),
262+
})
263+
} else {
264+
None
265+
}
266+
})
267+
.collect()
268+
}
269+
270+
/// Given a bound in an `impl Trait` type, looks for a trait in the set of supertraits (previously
271+
/// collected in [`collect_supertrait_bounds`]) that matches (same trait and generic arguments).
272+
fn find_bound_in_supertraits<'a, 'tcx>(
273+
cx: &LateContext<'tcx>,
274+
trait_def_id: DefId,
275+
args: &'tcx [GenericArg<'tcx>],
276+
bounds: &'a [ImplTraitBound<'tcx>],
277+
) -> Option<&'a ImplTraitBound<'tcx>> {
278+
bounds.iter().find(|bound| {
279+
bound.predicates.iter().any(|(clause, _)| {
280+
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
281+
&& tr.def_id() == trait_def_id
282+
{
283+
is_same_generics(
284+
cx.tcx,
285+
tr.trait_ref.args,
286+
bound.args,
287+
args,
288+
bound.trait_def_id,
289+
trait_def_id,
290+
)
291+
} else {
292+
false
293+
}
294+
})
295+
})
296+
}
297+
226298
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
227299
if let FnRetTy::Return(ty) = decl.output
228-
&&let TyKind::OpaqueDef(item_id, ..) = ty.kind
300+
&& let TyKind::OpaqueDef(item_id, ..) = ty.kind
229301
&& let item = cx.tcx.hir().item(item_id)
230302
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
231303
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232304
// we can avoid doing a bunch of stuff unnecessarily.
233305
&& opaque_ty.bounds.len() > 1
234306
{
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();
307+
let supertraits = collect_supertrait_bounds(cx, opaque_ty);
260308

261-
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
309+
// Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
310+
// supertraits of each of the bounds.
262311
// This involves some extra logic when generic arguments are present, since
263312
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264313
for (index, bound) in opaque_ty.bounds.iter().enumerate() {
@@ -267,42 +316,9 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267316
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
268317
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
269318
&& 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-
})
319+
&& let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits)
295320
{
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-
);
321+
emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
306322
}
307323
}
308324
}

0 commit comments

Comments
 (0)