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