@@ -70,6 +70,35 @@ declare_clippy_lint! {
7070 "Check if the same trait bounds are specified twice during a function declaration"
7171}
7272
73+ declare_clippy_lint ! {
74+ /// ### What it does
75+ /// Checks for multiple instances of the same where clause or trait bound
76+ ///
77+ /// ### Why is this bad?
78+ /// Adds to code noise
79+ ///
80+ /// ### Example
81+ /// ```rust
82+ /// fn foo<T: Default + Default>(bar: T) {}
83+ /// ```
84+ /// Use instead:
85+ /// ```rust
86+ /// fn foo<T: Default>(bar: T) {}
87+ /// ```
88+ ///
89+ /// ```rust
90+ /// fn foo<T>(bar: T) where T: Default + Default {}
91+ /// ```
92+ /// Use instead:
93+ /// ```rust
94+ /// fn foo<T>(bar: T) where T: Default {}
95+ /// ```
96+ #[ clippy:: version = "1.62.0" ]
97+ pub REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
98+ pedantic,
99+ "Traits are repeated within trait bounds or where clause"
100+ }
101+
73102#[ derive( Copy , Clone ) ]
74103pub struct TraitBounds {
75104 max_trait_bounds : u64 ,
@@ -82,12 +111,13 @@ impl TraitBounds {
82111 }
83112}
84113
85- impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS , TRAIT_DUPLICATION_IN_BOUNDS ] ) ;
114+ impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS , TRAIT_DUPLICATION_IN_BOUNDS , REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ] ) ;
86115
87116impl < ' tcx > LateLintPass < ' tcx > for TraitBounds {
88117 fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
89118 self . check_type_repetition ( cx, gen) ;
90119 check_trait_bound_duplication ( cx, gen) ;
120+ check_bounds_or_where_duplication ( cx, gen) ;
91121 }
92122
93123 fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx TraitItem < ' tcx > ) {
@@ -254,6 +284,53 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
254284 }
255285}
256286
287+ fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
288+ if gen. span . from_expansion ( ) {
289+ return ;
290+ }
291+
292+ for param in gen. params {
293+ let mut map = FxHashMap :: default ( ) ;
294+ if let ParamName :: Plain ( name) = param. name {
295+ for ( definition, _, span_direct) in param. bounds . iter ( ) . rev ( ) . filter_map ( get_trait_info_from_bound) {
296+ if let Some ( span_direct) = map. insert ( ( name, definition) , span_direct) {
297+ span_lint_and_help (
298+ cx,
299+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
300+ span_direct,
301+ "this trait bound has already been specified" ,
302+ None ,
303+ "consider removing this trait bound" ,
304+ ) ;
305+ }
306+ }
307+ }
308+ }
309+
310+ for predicate in gen. where_clause . predicates {
311+ if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate {
312+ let mut where_clauses = FxHashMap :: default ( ) ;
313+ for ( definition, _, span_direct) in bound_predicate
314+ . bounds
315+ . iter ( )
316+ . filter_map ( get_trait_info_from_bound)
317+ . rev ( )
318+ {
319+ if let Some ( span_direct) = where_clauses. insert ( definition, span_direct) {
320+ span_lint_and_help (
321+ cx,
322+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
323+ span_direct,
324+ "this where clause has already been specified" ,
325+ None ,
326+ "consider removing this where clause" ,
327+ ) ;
328+ }
329+ }
330+ }
331+ }
332+ }
333+
257334fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
258335 if let GenericBound :: Trait ( t, _) = bound {
259336 Some ( ( t. trait_ref . path . res , t. trait_ref . path . segments , t. span ) )
0 commit comments