Skip to content

Commit aa2c94e

Browse files
committed
Auto merge of #12308 - y21:more_implied_bounds, r=xFrednet
Look for `implied_bounds_in_impls` in more positions With this, we lint `impl Trait` implied bounds in more positions: - Type alias impl trait - Associated type position impl trait - Argument position impl trait - these are not opaque types, but instead are desugared to `where` clauses, so we need extra logic for finding them (`check_generics`), however the rest of the logic is the same Before this, we'd only lint RPIT `impl Trait`s. "Hide whitespaces" and reviewing commits individually might make this easier changelog: [`implied_bounds_in_impls`]: start linting implied bounds in APIT, ATPIT, TAIT
2 parents 7353bd0 + bbfe1c1 commit aa2c94e

File tree

4 files changed

+169
-80
lines changed

4 files changed

+169
-80
lines changed

clippy_lints/src/implied_bounds_in_impls.rs

+55-60
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ 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;
65
use rustc_hir::{
7-
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
8-
TraitItem, TraitItemKind, TyKind, TypeBinding,
6+
GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, TypeBinding,
7+
WherePredicate,
98
};
109
use rustc_hir_analysis::hir_ty_to_ty;
1110
use rustc_lint::{LateContext, LateLintPass};
1211
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
1312
use rustc_session::declare_lint_pass;
14-
use rustc_span::def_id::LocalDefId;
1513
use rustc_span::Span;
1614

1715
declare_clippy_lint! {
@@ -54,7 +52,7 @@ declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
5452
fn emit_lint(
5553
cx: &LateContext<'_>,
5654
poly_trait: &rustc_hir::PolyTraitRef<'_>,
57-
opaque_ty: &rustc_hir::OpaqueTy<'_>,
55+
bounds: GenericBounds<'_>,
5856
index: usize,
5957
// The bindings that were implied, used for suggestion purposes since removing a bound with associated types
6058
// means we might need to then move it to a different bound
@@ -73,10 +71,10 @@ fn emit_lint(
7371
// to include the `+` token that is ahead or behind,
7472
// so we don't end up with something like `impl + B` or `impl A + `
7573

76-
let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
74+
let implied_span_extended = if let Some(next_bound) = bounds.get(index + 1) {
7775
poly_trait.span.to(next_bound.span().shrink_to_lo())
7876
} else if index > 0
79-
&& let Some(prev_bound) = opaque_ty.bounds.get(index - 1)
77+
&& let Some(prev_bound) = bounds.get(index - 1)
8078
{
8179
prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi())
8280
} else {
@@ -240,9 +238,8 @@ struct ImplTraitBound<'tcx> {
240238
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
241239
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
242240
/// `Eq`.
243-
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
244-
opaque_ty
245-
.bounds
241+
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
242+
bounds
246243
.iter()
247244
.filter_map(|bound| {
248245
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
@@ -295,64 +292,62 @@ fn find_bound_in_supertraits<'a, 'tcx>(
295292
})
296293
}
297294

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
295+
fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
296+
if bounds.len() == 1 {
303297
// 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);
298+
// we can avoid doing a bunch of stuff unnecessarily; there will trivially be
299+
// no duplicate bounds
300+
return;
301+
}
308302

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

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);
331+
impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
332+
fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &rustc_hir::Generics<'tcx>) {
333+
for predicate in generics.predicates {
334+
if let WherePredicate::BoundPredicate(predicate) = predicate
335+
// In theory, the origin doesn't really matter,
336+
// we *could* also lint on explicit where clauses written out by the user,
337+
// not just impl trait desugared ones, but that contradicts with the lint name...
338+
&& let PredicateOrigin::ImplTrait = predicate.origin
339+
{
340+
check(cx, predicate.bounds);
341+
}
351342
}
352343
}
353-
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
354-
if let ImplItemKind::Fn(sig, ..) = &item.kind {
355-
check(cx, sig.decl);
344+
345+
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &rustc_hir::Ty<'_>) {
346+
if let TyKind::OpaqueDef(item_id, ..) = ty.kind
347+
&& let item = cx.tcx.hir().item(item_id)
348+
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind
349+
{
350+
check(cx, opaque_ty.bounds);
356351
}
357352
}
358353
}

tests/ui/implied_bounds_in_impls.fixed

+23
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 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

+23
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)