Auto merge of #9167 - aldhsu:fix-trait-duplication-false-pos, r=flip1995
Fixes [`trait_duplication_in_bounds`] false positives Fixes #9076 #9151 #8757. Partially fixes #8771. changelog: [`trait_duplication_in_bounds`]: Reduce number of false positives.
This commit is contained in:
commit
84df61c06e
@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
@ -103,7 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
||||
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
|
||||
self.check_type_repetition(cx, gen);
|
||||
check_trait_bound_duplication(cx, gen);
|
||||
check_bounds_or_where_duplication(cx, gen);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
@ -234,35 +233,61 @@ impl TraitBounds {
|
||||
}
|
||||
|
||||
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
|
||||
if gen.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut map = FxHashMap::<_, Vec<_>>::default();
|
||||
for predicate in gen.predicates {
|
||||
// Explanation:
|
||||
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
// where T: Clone + Default, { unimplemented!(); }
|
||||
// ^^^^^^^^^^^^^^^^^^
|
||||
// |
|
||||
// collects each of these where clauses into a set keyed by generic name and comparable trait
|
||||
// eg. (T, Clone)
|
||||
let where_predicates = gen
|
||||
.predicates
|
||||
.iter()
|
||||
.filter_map(|pred| {
|
||||
if_chain! {
|
||||
if pred.in_where_clause();
|
||||
if let WherePredicate::BoundPredicate(bound_predicate) = pred;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
|
||||
then {
|
||||
return Some(
|
||||
rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
|
||||
.into_keys().map(|trait_ref| (path.res, trait_ref)))
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
.collect::<FxHashSet<_>>();
|
||||
|
||||
// Explanation:
|
||||
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
|
||||
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^
|
||||
// |
|
||||
// compare trait bounds keyed by generic name and comparable trait to collected where
|
||||
// predicates eg. (T, Clone)
|
||||
for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
|
||||
if_chain! {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||
if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
|
||||
if bound_predicate.origin != PredicateOrigin::ImplTrait;
|
||||
if !bound_predicate.span.from_expansion();
|
||||
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
||||
if let Some(segment) = segments.first();
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
|
||||
then {
|
||||
for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
|
||||
let trait_resolutions_direct = map.entry(segment.ident).or_default();
|
||||
if let Some((_, span_direct)) = trait_resolutions_direct
|
||||
.iter()
|
||||
.find(|(res_direct, _)| *res_direct == res_where) {
|
||||
let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
|
||||
for (trait_ref, span) in traits {
|
||||
let key = (path.res, trait_ref);
|
||||
if where_predicates.contains(&key) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
*span_direct,
|
||||
span,
|
||||
"this trait bound is already specified in the where clause",
|
||||
None,
|
||||
"consider removing this trait bound",
|
||||
);
|
||||
}
|
||||
else {
|
||||
trait_resolutions_direct.push((res_where, span_where));
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,23 +298,6 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
struct ComparableTraitRef(Res, Vec<Res>);
|
||||
|
||||
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if gen.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
for predicate in gen.predicates {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
|
||||
let msg = if predicate.in_where_clause() {
|
||||
"these where clauses contain repeated elements"
|
||||
} else {
|
||||
"these bounds contain repeated elements"
|
||||
};
|
||||
rollup_traits(cx, bound_predicate.bounds, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
|
||||
if let GenericBound::Trait(t, tbm) = bound {
|
||||
let trait_path = t.trait_ref.path;
|
||||
@ -331,7 +339,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
|
||||
)
|
||||
}
|
||||
|
||||
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
|
||||
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> FxHashMap<ComparableTraitRef, Span> {
|
||||
let mut map = FxHashMap::default();
|
||||
let mut repeated_res = false;
|
||||
|
||||
@ -373,4 +381,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
@ -394,7 +394,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
|
||||
"single_component_path_imports_nested_first.rs",
|
||||
"string_add.rs",
|
||||
"toplevel_ref_arg_non_rustfix.rs",
|
||||
"trait_duplication_in_bounds.rs",
|
||||
"unit_arg.rs",
|
||||
"unnecessary_clone.rs",
|
||||
"unnecessary_lazy_eval_unfixable.rs",
|
||||
|
112
tests/ui/trait_duplication_in_bounds.fixed
Normal file
112
tests/ui/trait_duplication_in_bounds.fixed
Normal file
@ -0,0 +1,112 @@
|
||||
// run-rustfix
|
||||
#![deny(clippy::trait_duplication_in_bounds)]
|
||||
#![allow(unused)]
|
||||
|
||||
fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bad_bar<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_foo<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
trait GoodSelfTraitBound: Clone + Copy {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait GoodSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone + Copy;
|
||||
}
|
||||
|
||||
trait BadSelfTraitBound: Clone {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait BadSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone;
|
||||
}
|
||||
|
||||
trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait GoodWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy;
|
||||
}
|
||||
|
||||
trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait BadWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy;
|
||||
}
|
||||
|
||||
struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
t: T,
|
||||
u: U,
|
||||
}
|
||||
|
||||
impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
struct GoodStructWhereClause;
|
||||
|
||||
impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
|
||||
|
||||
trait GenericTrait<T> {}
|
||||
|
||||
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bad_generic<T: GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
mod foo {
|
||||
pub trait Clone {}
|
||||
}
|
||||
|
||||
fn qualified_path<T: Clone + foo::Clone>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,212 +1,112 @@
|
||||
// run-rustfix
|
||||
#![deny(clippy::trait_duplication_in_bounds)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
fn bad_bar<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone,
|
||||
T: Default,
|
||||
T: Clone + Clone + Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_bar<T: Clone + Default>(arg: T) {
|
||||
fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_foo<T>(arg: T)
|
||||
fn good_foo<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone + Default,
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_foobar<T: Default>(arg: T)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
unimplemented!();
|
||||
trait GoodSelfTraitBound: Clone + Copy {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait T: Default {
|
||||
trait GoodSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Default;
|
||||
Self: Clone + Copy;
|
||||
}
|
||||
|
||||
trait U: Default {
|
||||
trait BadSelfTraitBound: Clone + Clone + Clone {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait BadSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone;
|
||||
Self: Clone + Clone + Clone;
|
||||
}
|
||||
|
||||
trait ZZ: Default {
|
||||
fn g();
|
||||
fn h();
|
||||
trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait GoodWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
Self: Default + Clone;
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy;
|
||||
}
|
||||
|
||||
trait BadTrait: Default + Clone {
|
||||
trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
|
||||
trait BadWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
Self: Default + Clone;
|
||||
fn g()
|
||||
where
|
||||
Self: Default;
|
||||
fn h()
|
||||
where
|
||||
Self: Copy;
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Life;
|
||||
|
||||
impl T for Life {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
impl U for Life {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
// should not warn
|
||||
trait Iter: Iterator {
|
||||
fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
|
||||
where
|
||||
Self: Iterator<Item = (K, V)> + Sized,
|
||||
K: Ord + Eq,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
trait FooIter: Iterator<Item = Foo> {
|
||||
fn bar()
|
||||
where
|
||||
Self: Iterator<Item = Foo>,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// This should not lint
|
||||
fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
|
||||
|
||||
mod repeated_where_clauses_or_trait_bounds {
|
||||
fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bad_bar<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone + Clone + Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
U: Clone + Copy;
|
||||
}
|
||||
|
||||
fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
|
||||
unimplemented!();
|
||||
}
|
||||
struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
t: T,
|
||||
u: U,
|
||||
}
|
||||
|
||||
fn good_foo<T, U>(arg0: T, arg1: U)
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
trait GoodSelfTraitBound: Clone + Copy {
|
||||
fn f();
|
||||
}
|
||||
struct GoodStructWhereClause;
|
||||
|
||||
trait GoodSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone + Copy;
|
||||
}
|
||||
impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
trait BadSelfTraitBound: Clone + Clone + Clone {
|
||||
fn f();
|
||||
}
|
||||
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
|
||||
|
||||
trait BadSelfWhereClause {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone + Clone + Clone;
|
||||
}
|
||||
trait GenericTrait<T> {}
|
||||
|
||||
trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
trait GoodWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy;
|
||||
}
|
||||
fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
|
||||
fn f();
|
||||
}
|
||||
mod foo {
|
||||
pub trait Clone {}
|
||||
}
|
||||
|
||||
trait BadWhereClause<T, U> {
|
||||
fn f()
|
||||
where
|
||||
T: Clone + Clone + Clone + Copy,
|
||||
U: Clone + Copy;
|
||||
}
|
||||
|
||||
struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
|
||||
t: T,
|
||||
u: U,
|
||||
}
|
||||
|
||||
impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
struct GoodStructWhereClause;
|
||||
|
||||
impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
|
||||
where
|
||||
T: Clone + Copy,
|
||||
U: Clone + Copy,
|
||||
{
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
|
||||
|
||||
trait GenericTrait<T> {}
|
||||
|
||||
// This should not warn but currently does see #8757
|
||||
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
mod foo {
|
||||
pub trait Clone {}
|
||||
}
|
||||
|
||||
fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,167 +1,56 @@
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:7:15
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:5:15
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
| ^^^^^
|
||||
LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/trait_duplication_in_bounds.rs:1:9
|
||||
--> $DIR/trait_duplication_in_bounds.rs:2:9
|
||||
|
|
||||
LL | #![deny(clippy::trait_duplication_in_bounds)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:7:23
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:36:15
|
||||
|
|
||||
LL | Self: Default;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:50:15
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:56:15
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:56:25
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:59:15
|
||||
|
|
||||
LL | Self: Default;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds.rs:94:15
|
||||
|
|
||||
LL | Self: Iterator<Item = Foo>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:103:19
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:103:19
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:109:12
|
||||
|
|
||||
LL | T: Clone + Clone + Clone + Copy,
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: these where clauses contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:109:12
|
||||
--> $DIR/trait_duplication_in_bounds.rs:11:8
|
||||
|
|
||||
LL | T: Clone + Clone + Clone + Copy,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:39:26
|
||||
|
|
||||
LL | trait BadSelfTraitBound: Clone + Clone + Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
|
||||
|
||||
error: these where clauses contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:46:15
|
||||
|
|
||||
LL | Self: Clone + Clone + Clone;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:60:24
|
||||
|
|
||||
LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
||||
error: these where clauses contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:67:12
|
||||
|
|
||||
LL | T: Clone + Clone + Clone + Copy,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:137:30
|
||||
--> $DIR/trait_duplication_in_bounds.rs:100:19
|
||||
|
|
||||
LL | trait BadSelfTraitBound: Clone + Clone + Clone {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
|
||||
|
||||
error: these where clauses contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:144:19
|
||||
|
|
||||
LL | Self: Clone + Clone + Clone;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:158:28
|
||||
|
|
||||
LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:158:28
|
||||
--> $DIR/trait_duplication_in_bounds.rs:108:22
|
||||
|
|
||||
LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
|
||||
|
||||
error: these where clauses contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:165:16
|
||||
|
|
||||
LL | T: Clone + Clone + Clone + Copy,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:195:24
|
||||
|
|
||||
LL | fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:199:23
|
||||
|
|
||||
LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:199:23
|
||||
|
|
||||
LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds.rs:207:26
|
||||
|
|
||||
LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: these bounds contain repeated elements
|
||||
--> $DIR/trait_duplication_in_bounds.rs:207:26
|
||||
|
|
||||
LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
|
||||
|
||||
error: aborting due to 22 previous errors
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
166
tests/ui/trait_duplication_in_bounds_unfixable.rs
Normal file
166
tests/ui/trait_duplication_in_bounds_unfixable.rs
Normal file
@ -0,0 +1,166 @@
|
||||
#![deny(clippy::trait_duplication_in_bounds)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
where
|
||||
T: Clone,
|
||||
T: Default,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_bar<T: Clone + Default>(arg: T) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_foo<T>(arg: T)
|
||||
where
|
||||
T: Clone + Default,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn good_foobar<T: Default>(arg: T)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
trait T: Default {
|
||||
fn f()
|
||||
where
|
||||
Self: Default;
|
||||
}
|
||||
|
||||
trait U: Default {
|
||||
fn f()
|
||||
where
|
||||
Self: Clone;
|
||||
}
|
||||
|
||||
trait ZZ: Default {
|
||||
fn g();
|
||||
fn h();
|
||||
fn f()
|
||||
where
|
||||
Self: Default + Clone;
|
||||
}
|
||||
|
||||
trait BadTrait: Default + Clone {
|
||||
fn f()
|
||||
where
|
||||
Self: Default + Clone;
|
||||
fn g()
|
||||
where
|
||||
Self: Default;
|
||||
fn h()
|
||||
where
|
||||
Self: Copy;
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Life;
|
||||
|
||||
impl T for Life {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
impl U for Life {
|
||||
// this should not warn
|
||||
fn f() {}
|
||||
}
|
||||
|
||||
// should not warn
|
||||
trait Iter: Iterator {
|
||||
fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
|
||||
where
|
||||
Self: Iterator<Item = (K, V)> + Sized,
|
||||
K: Ord + Eq,
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
trait FooIter: Iterator<Item = Foo> {
|
||||
fn bar()
|
||||
where
|
||||
Self: Iterator<Item = Foo>,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// The below should not lint and exist to guard against false positives
|
||||
fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
|
||||
|
||||
pub mod one {
|
||||
#[derive(Clone, Debug)]
|
||||
struct MultiProductIter<I>
|
||||
where
|
||||
I: Iterator + Clone,
|
||||
I::Item: Clone,
|
||||
{
|
||||
_marker: I,
|
||||
}
|
||||
|
||||
pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
|
||||
where
|
||||
I: Iterator + Clone,
|
||||
I::Item: Clone;
|
||||
|
||||
pub fn multi_cartesian_product<H>(_: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
|
||||
where
|
||||
H: Iterator,
|
||||
H::Item: IntoIterator,
|
||||
<H::Item as IntoIterator>::IntoIter: Clone,
|
||||
<H::Item as IntoIterator>::Item: Clone,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod two {
|
||||
use std::iter::Peekable;
|
||||
|
||||
pub struct MergeBy<I, J, F>
|
||||
where
|
||||
I: Iterator,
|
||||
J: Iterator<Item = I::Item>,
|
||||
{
|
||||
_i: Peekable<I>,
|
||||
_j: Peekable<J>,
|
||||
_f: F,
|
||||
}
|
||||
|
||||
impl<I, J, F> Clone for MergeBy<I, J, F>
|
||||
where
|
||||
I: Iterator,
|
||||
J: Iterator<Item = I::Item>,
|
||||
std::iter::Peekable<I>: Clone,
|
||||
std::iter::Peekable<J>: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
_i: self._i.clone(),
|
||||
_j: self._j.clone(),
|
||||
_f: self._f.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait {}
|
||||
|
||||
pub fn f(_a: impl Trait, _b: impl Trait) {}
|
||||
|
||||
pub trait ImplTrait<T> {}
|
||||
|
||||
impl<A, B> ImplTrait<(A, B)> for Foo where Foo: ImplTrait<A> + ImplTrait<B> {}
|
||||
|
||||
fn main() {}
|
71
tests/ui/trait_duplication_in_bounds_unfixable.stderr
Normal file
71
tests/ui/trait_duplication_in_bounds_unfixable.stderr
Normal file
@ -0,0 +1,71 @@
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:6:23
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::trait_duplication_in_bounds)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in the where clause
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:6:15
|
||||
|
|
||||
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:35:15
|
||||
|
|
||||
LL | Self: Default;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:49:15
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:55:15
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:55:25
|
||||
|
|
||||
LL | Self: Default + Clone;
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:58:15
|
||||
|
|
||||
LL | Self: Default;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: this trait bound is already specified in trait declaration
|
||||
--> $DIR/trait_duplication_in_bounds_unfixable.rs:93:15
|
||||
|
|
||||
LL | Self: Iterator<Item = Foo>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing this trait bound
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user