@@ -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 > ) {
@@ -259,6 +289,53 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
259289 }
260290}
261291
292+ fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
293+ if gen. span . from_expansion ( ) {
294+ return ;
295+ }
296+
297+ for param in gen. params {
298+ let mut map = FxHashMap :: default ( ) ;
299+ if let ParamName :: Plain ( name) = param. name {
300+ for ( definition, _, span_direct) in param. bounds . iter ( ) . rev ( ) . filter_map ( get_trait_info_from_bound) {
301+ if let Some ( span_direct) = map. insert ( ( name, definition) , span_direct) {
302+ span_lint_and_help (
303+ cx,
304+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
305+ span_direct,
306+ "this trait bound has already been specified" ,
307+ None ,
308+ "consider removing this trait bound" ,
309+ ) ;
310+ }
311+ }
312+ }
313+ }
314+
315+ for predicate in gen. where_clause . predicates {
316+ if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate {
317+ let mut where_clauses = FxHashMap :: default ( ) ;
318+ for ( definition, _, span_direct) in bound_predicate
319+ . bounds
320+ . iter ( )
321+ . filter_map ( get_trait_info_from_bound)
322+ . rev ( )
323+ {
324+ if let Some ( span_direct) = where_clauses. insert ( definition, span_direct) {
325+ span_lint_and_help (
326+ cx,
327+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
328+ span_direct,
329+ "this where clause has already been specified" ,
330+ None ,
331+ "consider removing this where clause" ,
332+ ) ;
333+ }
334+ }
335+ }
336+ }
337+ }
338+
262339fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
263340 if let GenericBound :: Trait ( t, _) = bound {
264341 Some ( ( t. trait_ref . path . res , t. trait_ref . path . segments , t. span ) )
0 commit comments