diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a377763983a4b..979c1d0ebbd60 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -350,10 +350,7 @@ pub fn walk_enum_def<'a, V: Visitor<'a>>( walk_list!(visitor, visit_variant, &enum_definition.variants); } -pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) -where - V: Visitor<'a>, -{ +pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) { visitor.visit_ident(variant.ident); visitor.visit_vis(&variant.vis); visitor.visit_variant_data(&variant.data); diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index ccf8bd69ebd06..76d6a9c2f88a4 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -77,7 +77,7 @@ impl TransitiveRelation { pub fn maybe_map(&self, mut f: F) -> Option> where F: FnMut(&T) -> Option, - U: Clone + Debug + Eq + Hash + Clone, + U: Clone + Debug + Eq + Hash, { let mut result = TransitiveRelation::default(); for edge in &self.edges { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 77c7040e6a76d..110a345788a4a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -57,6 +57,7 @@ use rustc_trait_selection::traits::misc::can_type_implement_copy; use crate::nonstandard_style::{method_context, MethodLateContext}; use std::fmt::Write; +use std::hash::{Hash, Hasher}; use tracing::{debug, trace}; // hardwired lints from librustc_middle @@ -3140,3 +3141,153 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { } } } + +declare_lint! { + /// ### What it does + /// Checks for cases where the same trait or lifetime bound is specified more than once. + /// + /// ### Why is this bad? + /// Duplicate bounds makes the code less readable than specifing them only once. + /// + /// ### Example + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + /// + /// could be written as: + /// + /// ```rust + /// fn func(arg: T) {} + /// ``` + /// or + /// + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + pub DUPLICATE_BOUNDS, + Warn, + "Check if the same bound is specified more than once" +} + +declare_lint_pass!(DuplicateBounds => [DUPLICATE_BOUNDS]); + +impl<'tcx> LateLintPass<'tcx> for DuplicateBounds { + fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx hir::Generics<'_>) { + struct Bound { + kind: BoundKind, + span: Span, + } + + #[derive(Hash, PartialEq, Eq)] + enum BoundKind { + Trait(Res), + Lifetime(hir::LifetimeName), + } + + impl BoundKind { + fn as_str(&self) -> &'static str { + match self { + BoundKind::Trait(_) => "trait", + BoundKind::Lifetime(_) => "lifetime", + } + } + } + + impl Bound { + fn from_generic(bound: &hir::GenericBound<'_>) -> Option { + match bound { + hir::GenericBound::Trait(t, _) => { + Some(Self { kind: BoundKind::Trait(t.trait_ref.path.res), span: t.span }) + } + hir::GenericBound::Outlives(lifetime) => { + Some(Self { kind: BoundKind::Lifetime(lifetime.name), span: lifetime.span }) + } + _ => None, + } + } + } + + impl PartialEq for Bound { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind + } + } + + impl Hash for Bound { + fn hash(&self, state: &mut H) { + self.kind.hash(state) + } + } + + impl Eq for Bound {} + + if gen.span.from_expansion() { + return; + } + + let mut bounds = FxHashMap::default(); + for param in gen.params { + if let hir::ParamName::Plain(ref ident) = param.name { + let mut uniq_bounds = FxHashSet::default(); + + for res in param.bounds.iter().filter_map(Bound::from_generic) { + let span = res.span.clone(); + let kind = res.kind.as_str(); + if !uniq_bounds.insert(res) { + cx.struct_span_lint(DUPLICATE_BOUNDS, span, |lint| { + lint.build(&format!("this {} bound has already been specified", kind)) + .help(&format!("consider removing this {} bound", kind)) + .emit() + }); + } + } + + bounds.insert(*ident, uniq_bounds); + } + } + + for predicate in gen.where_clause.predicates { + let res = match &predicate { + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bounded_ty: + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })), + .. + }, + bounds, + .. + }) => segments.first().map(|s| (s.ident, *bounds)), + hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + lifetime: + hir::Lifetime { + name: hir::LifetimeName::Param(hir::ParamName::Plain(ident)), + .. + }, + bounds, + .. + }) => Some((*ident, *bounds)), + _ => None, + }; + + if let Some((ident, where_predicate_bounds)) = res { + if let Some(bounds) = bounds.get_mut(&ident) { + for res in where_predicate_bounds.iter().filter_map(Bound::from_generic) { + let span = res.span.clone(); + let kind = res.kind.as_str(); + if !bounds.insert(res) { + cx.struct_span_lint(DUPLICATE_BOUNDS, span, |lint| { + lint.build(&format!( + "this {} bound has already been specified", + kind + )) + .help(&format!("consider removing this {} bound", kind)) + .emit() + }); + } + } + } + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 79f850a781bd8..54a919d096bf2 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -197,6 +197,7 @@ macro_rules! late_lint_mod_passes { // Depends on referenced function signatures in expressions MutableTransmutes: MutableTransmutes, TypeAliasBounds: TypeAliasBounds, + DuplicateBounds: DuplicateBounds, TrivialConstraints: TrivialConstraints, TypeLimits: TypeLimits::new(), NonSnakeCase: NonSnakeCase, diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 524d8f857e2a5..6269f79f6df3d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2434,6 +2434,7 @@ pub trait Iterator { /// assert!(result.is_err()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), allow(duplicate_bounds))] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] fn try_find(&mut self, f: F) -> Result, E> where diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index f25e257bf3168..27d4ec539083b 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1233,6 +1233,7 @@ pub mod tracked_env { /// Besides the dependency tracking this function should be equivalent to `env::var` from the /// standard library, except that the argument must be UTF-8. #[unstable(feature = "proc_macro_tracked_env", issue = "74690")] + #[cfg_attr(not(bootstrap), allow(duplicate_bounds))] pub fn var + AsRef>(key: K) -> Result { let key: &str = key.as_ref(); let value = env::var(key); diff --git a/src/test/ui/lint/duplicate_bounds.rs b/src/test/ui/lint/duplicate_bounds.rs new file mode 100644 index 0000000000000..58157896ead7f --- /dev/null +++ b/src/test/ui/lint/duplicate_bounds.rs @@ -0,0 +1,82 @@ +#![deny(duplicate_bounds)] + +trait DupDirectAndWhere {} +fn dup_direct_and_where(t: T) +where + T: DupDirectAndWhere, + //~^ ERROR this trait bound has already been specified + T: DupDirectAndWhere, + //~^ ERROR this trait bound has already been specified +{ + unimplemented!(); +} + +trait DupDirect {} +fn dup_direct(t: T) { + //~^ ERROR this trait bound has already been specified + unimplemented!(); +} + +trait DupWhere {} +fn dup_where(t: T) +where + T: DupWhere + DupWhere, + //~^ ERROR this trait bound has already been specified +{ + unimplemented!(); +} + +trait NotDup {} +fn not_dup((t, u): (T, U)) { + unimplemented!(); +} + +fn dup_lifetimes<'a, 'b: 'a + 'a>() +//~^ ERROR this lifetime bound has already been specified +where + 'b: 'a, + //~^ ERROR this lifetime bound has already been specified +{ +} + +fn dup_lifetimes_generic<'a, T: 'a + 'a>() +//~^ ERROR this lifetime bound has already been specified +where + T: 'a, + //~^ ERROR this lifetime bound has already been specified +{ +} + +trait Everything {} +fn everything((t, u): (T, U)) +//~^ ERROR this trait bound has already been specified +//~| ERROR this trait bound has already been specified +where + T: Everything + Everything + Everything, + //~^ ERROR this trait bound has already been specified + //~| ERROR this trait bound has already been specified + //~| ERROR this trait bound has already been specified + U: Everything, + //~^ ERROR this trait bound has already been specified +{ + unimplemented!(); +} + +trait DupStructBound {} +struct DupStruct(T) +//~^ ERROR this trait bound has already been specified +where + T: DupStructBound; +//~^ ERROR this trait bound has already been specified + +impl<'a, T: 'a + DupStructBound + DupStructBound> DupStruct +//~^ ERROR this trait bound has already been specified +where + T: 'a + DupStructBound, + //~^ ERROR this lifetime bound has already been specified + //~| ERROR this trait bound has already been specified +{ + fn _x() {} +} + +fn main() {} diff --git a/src/test/ui/lint/duplicate_bounds.stderr b/src/test/ui/lint/duplicate_bounds.stderr new file mode 100644 index 0000000000000..3cd7704b98f8e --- /dev/null +++ b/src/test/ui/lint/duplicate_bounds.stderr @@ -0,0 +1,159 @@ +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:6:8 + | +LL | T: DupDirectAndWhere, + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/duplicate_bounds.rs:1:9 + | +LL | #![deny(duplicate_bounds)] + | ^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:8:8 + | +LL | T: DupDirectAndWhere, + | ^^^^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:15:30 + | +LL | fn dup_direct(t: T) { + | ^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:23:19 + | +LL | T: DupWhere + DupWhere, + | ^^^^^^^^ + | + = help: consider removing this trait bound + +error: this lifetime bound has already been specified + --> $DIR/duplicate_bounds.rs:34:31 + | +LL | fn dup_lifetimes<'a, 'b: 'a + 'a>() + | ^^ + | + = help: consider removing this lifetime bound + +error: this lifetime bound has already been specified + --> $DIR/duplicate_bounds.rs:37:9 + | +LL | 'b: 'a, + | ^^ + | + = help: consider removing this lifetime bound + +error: this lifetime bound has already been specified + --> $DIR/duplicate_bounds.rs:42:38 + | +LL | fn dup_lifetimes_generic<'a, T: 'a + 'a>() + | ^^ + | + = help: consider removing this lifetime bound + +error: this lifetime bound has already been specified + --> $DIR/duplicate_bounds.rs:45:8 + | +LL | T: 'a, + | ^^ + | + = help: consider removing this lifetime bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:51:31 + | +LL | fn everything((t, u): (T, U)) + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:51:59 + | +LL | fn everything((t, u): (T, U)) + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:55:8 + | +LL | T: Everything + Everything + Everything, + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:55:21 + | +LL | T: Everything + Everything + Everything, + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:55:34 + | +LL | T: Everything + Everything + Everything, + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:59:8 + | +LL | U: Everything, + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:66:38 + | +LL | struct DupStruct(T) + | ^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:69:8 + | +LL | T: DupStructBound; + | ^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:72:35 + | +LL | impl<'a, T: 'a + DupStructBound + DupStructBound> DupStruct + | ^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: this lifetime bound has already been specified + --> $DIR/duplicate_bounds.rs:75:8 + | +LL | T: 'a + DupStructBound, + | ^^ + | + = help: consider removing this lifetime bound + +error: this trait bound has already been specified + --> $DIR/duplicate_bounds.rs:75:13 + | +LL | T: 'a + DupStructBound, + | ^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 6f73a00d1f782..608383b214ac1 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -925,7 +925,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, to_string_in_display::TO_STRING_IN_DISPLAY, - trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, transmute::CROSSPOINTER_TRANSMUTE, transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, @@ -1133,7 +1132,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(shadow::SHADOW_UNRELATED), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), @@ -2182,6 +2180,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::panic_params", "non_fmt_panics"); ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); + ls.register_renamed("clippy::trait_duplication_in_bounds", "trait_duplication_in_bounds"); } // only exists to let the dogfood integration test works. diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 79367c4230c2a..91fe47437f6e6 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -2,13 +2,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{in_macro, SpanlessHash}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; +use rustc_hir::{GenericBound, Generics, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -33,35 +31,6 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for cases where generics are being used and multiple - /// syntax specifications for trait bounds are used simultaneously. - /// - /// ### Why is this bad? - /// Duplicate bounds makes the code - /// less readable than specifing them only once. - /// - /// ### Example - /// ```rust - /// fn func(arg: T) where T: Clone + Default {} - /// ``` - /// - /// Could be written as: - /// - /// ```rust - /// fn func(arg: T) {} - /// ``` - /// or - /// - /// ```rust - /// fn func(arg: T) where T: Clone + Default {} - /// ``` - pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, - "Check if the same trait bounds are specified twice during a function declaration" -} - #[derive(Copy, Clone)] pub struct TraitBounds { max_trait_bounds: u64, @@ -74,20 +43,11 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); 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); - } -} - -fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { - if let GenericBound::Trait(t, _) = bound { - Some((t.trait_ref.path.res, t.span)) - } else { - None } } @@ -149,47 +109,3 @@ impl TraitBounds { } } } - -fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { - return; - } - - let mut map = FxHashMap::default(); - for param in gen.params { - if let ParamName::Plain(ref ident) = param.name { - let res = param - .bounds - .iter() - .filter_map(get_trait_res_span_from_bound) - .collect::>(); - map.insert(*ident, res); - } - } - - for predicate in gen.where_clause.predicates { - if_chain! { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; - if !in_macro(bound_predicate.span); - if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; - if let Some(segment) = segments.first(); - if let Some(trait_resolutions_direct) = map.get(&segment.ident); - then { - for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { - if let Some((_, span_direct)) = trait_resolutions_direct - .iter() - .find(|(res_direct, _)| *res_direct == res_where) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - *span_direct, - "this trait bound is already specified in the where clause", - None, - "consider removing this trait bound", - ); - } - } - } - } - } -} diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 1943d0092e624..b4c0b25026834 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -15,5 +15,6 @@ #[warn(clippy::pub_enum_variant_names)] #[warn(clippy::wrong_pub_self_convention)] #[warn(clippy::invalid_atomic_ordering)] +#[warn(clippy::trait_duplication_in_bounds)] fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index 51048e45c0677..4aa3efe247ec6 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -102,5 +102,11 @@ error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomi LL | #[warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +error: lint `clippy::trait_duplication_in_bounds` has been renamed to `duplicate_bounds` + --> $DIR/deprecated.rs:17:8 + | +LL | #[warn(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `duplicate_bounds` + error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs deleted file mode 100644 index cb2b0054e352b..0000000000000 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![deny(clippy::trait_duplication_in_bounds)] - -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -fn bad_foo(arg0: T, arg1: Z) -where - T: Clone, - T: Default, -{ - unimplemented!(); -} - -fn good_bar(arg: T) { - unimplemented!(); -} - -fn good_foo(arg: T) -where - T: Clone + Default, -{ - unimplemented!(); -} - -fn good_foobar(arg: T) -where - T: Clone, -{ - unimplemented!(); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr deleted file mode 100644 index 027e1c7520412..0000000000000 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:5:15 - | -LL | fn bad_foo(arg0: T, arg1: Z) - | ^^^^^ - | -note: the lint level is defined here - --> $DIR/trait_duplication_in_bounds.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.rs:5:23 - | -LL | fn bad_foo(arg0: T, arg1: Z) - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: aborting due to 2 previous errors -