@@ -3,10 +3,14 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
3
3
use clippy_utils:: { SpanlessEq , SpanlessHash } ;
4
4
use core:: hash:: { Hash , Hasher } ;
5
5
use if_chain:: if_chain;
6
- use rustc_data_structures:: fx:: FxHashMap ;
6
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
7
7
use rustc_data_structures:: unhash:: UnhashMap ;
8
8
use rustc_errors:: Applicability ;
9
- use rustc_hir:: { def:: Res , GenericBound , Generics , ParamName , Path , QPath , Ty , TyKind , WherePredicate } ;
9
+ use rustc_hir:: def:: Res ;
10
+ use rustc_hir:: {
11
+ GenericBound , Generics , Item , ItemKind , Node , ParamName , Path , PathSegment , QPath , TraitItem , Ty , TyKind ,
12
+ WherePredicate ,
13
+ } ;
10
14
use rustc_lint:: { LateContext , LateLintPass } ;
11
15
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
12
16
use rustc_span:: Span ;
@@ -84,6 +88,46 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
84
88
self . check_type_repetition ( cx, gen) ;
85
89
check_trait_bound_duplication ( cx, gen) ;
86
90
}
91
+
92
+ fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx TraitItem < ' tcx > ) {
93
+ let Generics { where_clause, .. } = & item. generics ;
94
+ let mut self_bounds_set = FxHashSet :: default ( ) ;
95
+
96
+ for predicate in where_clause. predicates {
97
+ if_chain ! {
98
+ if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
99
+ if !bound_predicate. span. from_expansion( ) ;
100
+ if let TyKind :: Path ( QPath :: Resolved ( _, Path { segments, .. } ) ) = bound_predicate. bounded_ty. kind;
101
+ if let Some ( PathSegment { res: Some ( Res :: SelfTy ( Some ( def_id) , _) ) , .. } ) = segments. first( ) ;
102
+
103
+ if let Some (
104
+ Node :: Item (
105
+ Item {
106
+ kind: ItemKind :: Trait ( _, _, _, self_bounds, _) ,
107
+ .. }
108
+ )
109
+ ) = cx. tcx. hir( ) . get_if_local( * def_id) ;
110
+ then {
111
+ if self_bounds_set. is_empty( ) {
112
+ for bound in self_bounds. iter( ) {
113
+ let Some ( ( self_res, _) ) = get_trait_res_span_from_bound( bound) else { continue } ;
114
+ self_bounds_set. insert( self_res) ;
115
+ }
116
+ }
117
+
118
+ bound_predicate
119
+ . bounds
120
+ . iter( )
121
+ . filter_map( get_trait_res_span_from_bound)
122
+ . for_each( |( trait_item_res, span) | {
123
+ if self_bounds_set. get( & trait_item_res) . is_some( ) {
124
+ emit_lint( cx, span) ;
125
+ }
126
+ } ) ;
127
+ }
128
+ }
129
+ }
130
+ }
87
131
}
88
132
89
133
fn get_trait_res_span_from_bound ( bound : & GenericBound < ' _ > ) -> Option < ( Res , Span ) > {
@@ -198,17 +242,21 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
198
242
if let Some ( ( _, span_direct) ) = trait_resolutions_direct
199
243
. iter( )
200
244
. find( |( res_direct, _) | * res_direct == res_where) {
201
- span_lint_and_help(
202
- cx,
203
- TRAIT_DUPLICATION_IN_BOUNDS ,
204
- * span_direct,
205
- "this trait bound is already specified in the where clause" ,
206
- None ,
207
- "consider removing this trait bound" ,
208
- ) ;
245
+ emit_lint( cx, * span_direct) ;
209
246
}
210
247
}
211
248
}
212
249
}
213
250
}
214
251
}
252
+
253
+ fn emit_lint ( cx : & LateContext < ' _ > , span : Span ) {
254
+ span_lint_and_help (
255
+ cx,
256
+ TRAIT_DUPLICATION_IN_BOUNDS ,
257
+ span,
258
+ "this trait bound is already specified in the where clause" ,
259
+ None ,
260
+ "consider removing this trait bound" ,
261
+ ) ;
262
+ }
0 commit comments