Skip to content

Commit ec29b0d

Browse files
committed
lint implied bounds in *all* opaque impl Trait types
1 parent c469cb0 commit ec29b0d

File tree

4 files changed

+145
-84
lines changed

4 files changed

+145
-84
lines changed

clippy_lints/src/implied_bounds_in_impls.rs

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@ use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
33
use rustc_errors::{Applicability, SuggestionStyle};
44
use rustc_hir::def_id::DefId;
5-
use rustc_hir::intravisit::FnKind;
6-
use rustc_hir::{
7-
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
8-
TraitItem, TraitItemKind, TyKind, TypeBinding,
9-
};
5+
use rustc_hir::{GenericArg, GenericBound, GenericBounds, ItemKind, TraitBoundModifier, TyKind, TypeBinding};
106
use rustc_hir_analysis::hir_ty_to_ty;
117
use rustc_lint::{LateContext, LateLintPass};
128
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
139
use rustc_session::declare_lint_pass;
14-
use rustc_span::def_id::LocalDefId;
1510
use rustc_span::Span;
1611

1712
declare_clippy_lint! {
@@ -54,7 +49,7 @@ declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
5449
fn emit_lint(
5550
cx: &LateContext<'_>,
5651
poly_trait: &rustc_hir::PolyTraitRef<'_>,
57-
opaque_ty: &rustc_hir::OpaqueTy<'_>,
52+
bounds: GenericBounds<'_>,
5853
index: usize,
5954
// The bindings that were implied, used for suggestion purposes since removing a bound with associated types
6055
// means we might need to then move it to a different bound
@@ -73,10 +68,10 @@ fn emit_lint(
7368
// to include the `+` token that is ahead or behind,
7469
// so we don't end up with something like `impl + B` or `impl A + `
7570

76-
let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
71+
let implied_span_extended = if let Some(next_bound) = bounds.get(index + 1) {
7772
poly_trait.span.to(next_bound.span().shrink_to_lo())
7873
} else if index > 0
79-
&& let Some(prev_bound) = opaque_ty.bounds.get(index - 1)
74+
&& let Some(prev_bound) = bounds.get(index - 1)
8075
{
8176
prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi())
8277
} else {
@@ -240,9 +235,8 @@ struct ImplTraitBound<'tcx> {
240235
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
241236
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
242237
/// `Eq`.
243-
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
244-
opaque_ty
245-
.bounds
238+
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
239+
bounds
246240
.iter()
247241
.filter_map(|bound| {
248242
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
@@ -295,64 +289,49 @@ fn find_bound_in_supertraits<'a, 'tcx>(
295289
})
296290
}
297291

298-
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
299-
if let FnRetTy::Return(ty) = decl.output
300-
&& let TyKind::OpaqueDef(item_id, ..) = ty.kind
301-
&& let item = cx.tcx.hir().item(item_id)
302-
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
292+
fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
293+
if bounds.len() == 1 {
303294
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
304-
// we can avoid doing a bunch of stuff unnecessarily.
305-
&& opaque_ty.bounds.len() > 1
306-
{
307-
let supertraits = collect_supertrait_bounds(cx, opaque_ty);
295+
// we can avoid doing a bunch of stuff unnecessarily; there will trivially be
296+
// no duplicate bounds
297+
return;
298+
}
308299

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.
311-
// This involves some extra logic when generic arguments are present, since
312-
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
313-
for (index, bound) in opaque_ty.bounds.iter().enumerate() {
314-
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
315-
&& let [.., path] = poly_trait.trait_ref.path.segments
316-
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
317-
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
318-
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
319-
&& let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits)
320-
// If the implied bound has a type binding that also exists in the implied-by trait,
321-
// then we shouldn't lint. See #11880 for an example.
322-
&& let assocs = cx.tcx.associated_items(bound.trait_def_id)
323-
&& !implied_bindings.iter().any(|binding| {
324-
assocs
325-
.filter_by_name_unhygienic(binding.ident.name)
326-
.next()
327-
.is_some_and(|assoc| assoc.kind == ty::AssocKind::Type)
328-
})
329-
{
330-
emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
331-
}
300+
let supertraits = collect_supertrait_bounds(cx, bounds);
301+
302+
// Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
303+
// supertraits of each of the bounds.
304+
// This involves some extra logic when generic arguments are present, since
305+
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
306+
for (index, bound) in bounds.iter().enumerate() {
307+
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
308+
&& let [.., path] = poly_trait.trait_ref.path.segments
309+
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
310+
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
311+
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
312+
&& let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits)
313+
// If the implied bound has a type binding that also exists in the implied-by trait,
314+
// then we shouldn't lint. See #11880 for an example.
315+
&& let assocs = cx.tcx.associated_items(bound.trait_def_id)
316+
&& !implied_bindings.iter().any(|binding| {
317+
assocs
318+
.filter_by_name_unhygienic(binding.ident.name)
319+
.next()
320+
.is_some_and(|assoc| assoc.kind == ty::AssocKind::Type)
321+
})
322+
{
323+
emit_lint(cx, poly_trait, bounds, index, implied_bindings, bound);
332324
}
333325
}
334326
}
335327

336-
impl LateLintPass<'_> for ImpliedBoundsInImpls {
337-
fn check_fn(
338-
&mut self,
339-
cx: &LateContext<'_>,
340-
_: FnKind<'_>,
341-
decl: &FnDecl<'_>,
342-
_: &Body<'_>,
343-
_: Span,
344-
_: LocalDefId,
345-
) {
346-
check(cx, decl);
347-
}
348-
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
349-
if let TraitItemKind::Fn(sig, ..) = &item.kind {
350-
check(cx, sig.decl);
351-
}
352-
}
353-
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
354-
if let ImplItemKind::Fn(sig, ..) = &item.kind {
355-
check(cx, sig.decl);
328+
impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
329+
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &rustc_hir::Ty<'_>) {
330+
if let TyKind::OpaqueDef(item_id, ..) = ty.kind
331+
&& let item = cx.tcx.hir().item(item_id)
332+
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
333+
{
334+
check(cx, opaque_ty.bounds);
356335
}
357336
}
358337
}

tests/ui/implied_bounds_in_impls.fixed

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![warn(clippy::implied_bounds_in_impls)]
22
#![allow(dead_code)]
3+
#![feature(impl_trait_in_assoc_type, type_alias_impl_trait)]
34

45
use std::ops::{Deref, DerefMut};
56

@@ -150,4 +151,26 @@ fn issue11880() {
150151
fn f5() -> impl Y<T = u32, U = String> {}
151152
}
152153

154+
fn apit(_: impl Deref + DerefMut) {}
155+
156+
trait Rpitit {
157+
fn f() -> impl DerefMut;
158+
}
159+
160+
trait Atpit {
161+
type Assoc;
162+
fn define() -> Self::Assoc;
163+
}
164+
impl Atpit for () {
165+
type Assoc = impl DerefMut;
166+
fn define() -> Self::Assoc {
167+
&mut [] as &mut [()]
168+
}
169+
}
170+
171+
type Tait = impl DerefMut;
172+
fn define() -> Tait {
173+
&mut [] as &mut [()]
174+
}
175+
153176
fn main() {}

tests/ui/implied_bounds_in_impls.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![warn(clippy::implied_bounds_in_impls)]
22
#![allow(dead_code)]
3+
#![feature(impl_trait_in_assoc_type, type_alias_impl_trait)]
34

45
use std::ops::{Deref, DerefMut};
56

@@ -150,4 +151,26 @@ fn issue11880() {
150151
fn f5() -> impl X<U = String> + Y<T = u32> {}
151152
}
152153

154+
fn apit(_: impl Deref + DerefMut) {}
155+
156+
trait Rpitit {
157+
fn f() -> impl Deref + DerefMut;
158+
}
159+
160+
trait Atpit {
161+
type Assoc;
162+
fn define() -> Self::Assoc;
163+
}
164+
impl Atpit for () {
165+
type Assoc = impl Deref + DerefMut;
166+
fn define() -> Self::Assoc {
167+
&mut [] as &mut [()]
168+
}
169+
}
170+
171+
type Tait = impl Deref + DerefMut;
172+
fn define() -> Tait {
173+
&mut [] as &mut [()]
174+
}
175+
153176
fn main() {}

0 commit comments

Comments
 (0)