@@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_
22use if_chain:: if_chain;
33use rustc_data_structures:: fx:: FxHashMap ;
44use rustc_errors:: Applicability ;
5- use rustc_hir:: { GenericBound , Generics , WherePredicate } ;
5+ use rustc_hir:: { def :: Res , GenericBound , Generics , ParamName , Path , QPath , TyKind , WherePredicate } ;
66use rustc_lint:: { LateContext , LateLintPass } ;
77use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
8+ use rustc_span:: Span ;
89
910declare_clippy_lint ! {
1011 /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
@@ -29,6 +30,35 @@ declare_clippy_lint! {
2930 "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
3031}
3132
33+ declare_clippy_lint ! {
34+ /// **What it does:** Checks for cases where generics are being used and multiple
35+ /// syntax specifications for trait bounds are used simultaneously.
36+ ///
37+ /// **Why is this bad?** Duplicate bounds makes the code
38+ /// less readable than specifing them only once.
39+ ///
40+ /// **Known problems:** None.
41+ ///
42+ /// **Example:**
43+ /// ```rust
44+ /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
45+ /// ```
46+ ///
47+ /// Could be written as:
48+ ///
49+ /// ```rust
50+ /// fn func<T: Clone + Default>(arg: T) {}
51+ /// ```
52+ /// or
53+ /// ///
54+ /// ```rust
55+ /// fn func<T>(arg: T) where T: Clone + Default {}
56+ /// ```
57+ pub TRAIT_DUPLICATION_IN_BOUNDS ,
58+ pedantic,
59+ "Check if the same trait bounds are specified twice during a function declaration"
60+ }
61+
3262#[ derive( Copy , Clone ) ]
3363pub struct TraitBounds {
3464 max_trait_bounds : u64 ,
@@ -41,10 +71,25 @@ impl TraitBounds {
4171 }
4272}
4373
44- impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS ] ) ;
74+ impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS , TRAIT_DUPLICATION_IN_BOUNDS ] ) ;
4575
4676impl < ' tcx > LateLintPass < ' tcx > for TraitBounds {
4777 fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
78+ self . check_type_repetition ( cx, gen) ;
79+ check_trait_bound_duplication ( cx, gen) ;
80+ }
81+ }
82+
83+ fn get_trait_res_span_from_bound ( bound : & GenericBound < ' _ > ) -> Option < ( Res , Span ) > {
84+ if let GenericBound :: Trait ( t, _) = bound {
85+ Some ( ( t. trait_ref . path . res , t. span ) )
86+ } else {
87+ None
88+ }
89+ }
90+
91+ impl TraitBounds {
92+ fn check_type_repetition ( self , cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
4893 if in_macro ( gen. span ) {
4994 return ;
5095 }
@@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
101146 }
102147 }
103148}
149+
150+ fn check_trait_bound_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
151+ if in_macro ( gen. span ) || gen. params . is_empty ( ) || gen. where_clause . predicates . is_empty ( ) {
152+ return ;
153+ }
154+
155+ let mut map = FxHashMap :: default ( ) ;
156+ for param in gen. params {
157+ if let ParamName :: Plain ( ref ident) = param. name {
158+ let res = param
159+ . bounds
160+ . iter ( )
161+ . filter_map ( get_trait_res_span_from_bound)
162+ . collect :: < Vec < _ > > ( ) ;
163+ map. insert ( * ident, res) ;
164+ }
165+ }
166+
167+ for predicate in gen. where_clause . predicates {
168+ if_chain ! {
169+ if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
170+ if !in_macro( bound_predicate. span) ;
171+ if let TyKind :: Path ( ref path) = bound_predicate. bounded_ty. kind;
172+ if let QPath :: Resolved ( _, Path { ref segments, .. } ) = path;
173+ if let Some ( segment) = segments. first( ) ;
174+ if let Some ( trait_resolutions_direct) = map. get( & segment. ident) ;
175+ then {
176+ for ( res_where, _) in bound_predicate. bounds. iter( ) . filter_map( get_trait_res_span_from_bound) {
177+ if let Some ( ( _, span_direct) ) = trait_resolutions_direct
178+ . iter( )
179+ . find( |( res_direct, _) | * res_direct == res_where) {
180+ span_lint_and_help(
181+ cx,
182+ TRAIT_DUPLICATION_IN_BOUNDS ,
183+ * span_direct,
184+ "this trait bound is already specified in the where clause" ,
185+ None ,
186+ "consider removing this trait bound" ,
187+ ) ;
188+ }
189+ }
190+ }
191+ }
192+ }
193+ }
0 commit comments