@@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_
2
2
use if_chain:: if_chain;
3
3
use rustc_data_structures:: fx:: FxHashMap ;
4
4
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { GenericBound , Generics , WherePredicate } ;
5
+ use rustc_hir:: { def :: Res , GenericBound , Generics , ParamName , Path , QPath , TyKind , WherePredicate } ;
6
6
use rustc_lint:: { LateContext , LateLintPass } ;
7
7
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
8
+ use rustc_span:: Span ;
8
9
9
10
declare_clippy_lint ! {
10
11
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
@@ -29,6 +30,35 @@ declare_clippy_lint! {
29
30
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
30
31
}
31
32
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
+
32
62
#[ derive( Copy , Clone ) ]
33
63
pub struct TraitBounds {
34
64
max_trait_bounds : u64 ,
@@ -41,10 +71,25 @@ impl TraitBounds {
41
71
}
42
72
}
43
73
44
- impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS ] ) ;
74
+ impl_lint_pass ! ( TraitBounds => [ TYPE_REPETITION_IN_BOUNDS , TRAIT_DUPLICATION_IN_BOUNDS ] ) ;
45
75
46
76
impl < ' tcx > LateLintPass < ' tcx > for TraitBounds {
47
77
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 < ' _ > ) {
48
93
if in_macro ( gen. span ) {
49
94
return ;
50
95
}
@@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
101
146
}
102
147
}
103
148
}
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