diff options
| author | bors <bors@rust-lang.org> | 2022-08-14 14:46:26 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-08-14 14:46:26 +0000 |
| commit | 84df61c06eb36602022e758878afe66479ba0769 (patch) | |
| tree | a27f76ca61d8ba7e733a31346a53d2d298780eea | |
| parent | 4d5d191f6af4d4697e9b983b655aa7dd96fd966f (diff) | |
| parent | 8bae517c2d540dcf62bff12ba7404a23a4233d99 (diff) | |
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.
| -rw-r--r-- | clippy_lints/src/trait_bounds.rs | 82 | ||||
| -rw-r--r-- | tests/compile-test.rs | 1 | ||||
| -rw-r--r-- | tests/ui/trait_duplication_in_bounds.fixed | 112 | ||||
| -rw-r--r-- | tests/ui/trait_duplication_in_bounds.rs | 218 | ||||
| -rw-r--r-- | tests/ui/trait_duplication_in_bounds.stderr | 165 | ||||
| -rw-r--r-- | tests/ui/trait_duplication_in_bounds_unfixable.rs | 166 | ||||
| -rw-r--r-- | tests/ui/trait_duplication_in_bounds_unfixable.stderr | 71 |
7 files changed, 481 insertions, 334 deletions
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 0a42a31fb8c..0434720f79b 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -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 } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 92ac1a2be56..610f1f14563 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -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", diff --git a/tests/ui/trait_duplication_in_bounds.fixed b/tests/ui/trait_duplication_in_bounds.fixed new file mode 100644 index 00000000000..b4e6bf0ea1c --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.fixed @@ -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() {} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index a5751c58aab..7f2e96a22e6 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -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 GoodTraitBound<T: Clone + Copy, U: Clone + Copy> { + fn f(); } -trait ZZ: Default { - fn g(); - fn h(); +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; + T: Clone + Clone + Clone + Copy, + U: Clone + Copy; } -#[derive(Default, Clone)] -struct Life; +struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> { + t: T, + u: U, +} -impl T for Life { +impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> { // this should not warn fn f() {} } -impl U for Life { +struct GoodStructWhereClause; + +impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause +where + T: Clone + Copy, + U: Clone + Copy, +{ // 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!(); - } -} +fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {} -struct Foo; +trait GenericTrait<T> {} -trait FooIter: Iterator<Item = Foo> { - fn bar() - where - Self: Iterator<Item = Foo>, - { - } +fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { + unimplemented!(); } -// 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!(); - } - - 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 + Clone + Clone { - fn f(); - } - - trait BadSelfWhereClause { - fn f() - where - Self: Clone + Clone + 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 + Clone + Clone + Copy, U: Clone + Copy> { - fn f(); - } - - 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!(); - } +fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { + unimplemented!(); +} - mod foo { - pub trait Clone {} - } +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() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 7ef04e52708..86c593811a7 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -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` +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:39:26 | -LL | trait BadSelfTraitBound: Clone + Clone + Clone { - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` +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 + --> $DIR/trait_duplication_in_bounds.rs:46:15 | -LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { - | ^^^^^ - | - = help: consider removing this trait bound +LL | Self: Clone + Clone + Clone; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:158:28 + --> $DIR/trait_duplication_in_bounds.rs:60:24 | -LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` +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: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 + --> $DIR/trait_duplication_in_bounds.rs:67:12 | -LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { - | ^^^^^^^^^^^^^^^^^ - | - = help: consider removing this trait bound +LL | T: Clone + Clone + Clone + Copy, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` 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) { - | ^^^^^^^^^^^^^^^^^ + --> $DIR/trait_duplication_in_bounds.rs:100:19 | - = 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:207:26 + --> $DIR/trait_duplication_in_bounds.rs:108:22 | -LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone` +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 diff --git a/tests/ui/trait_duplication_in_bounds_unfixable.rs b/tests/ui/trait_duplication_in_bounds_unfixable.rs new file mode 100644 index 00000000000..5630a0345ad --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds_unfixable.rs @@ -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() {} diff --git a/tests/ui/trait_duplication_in_bounds_unfixable.stderr b/tests/ui/trait_duplication_in_bounds_unfixable.stderr new file mode 100644 index 00000000000..aa44114eb6c --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds_unfixable.stderr @@ -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 + |
