@@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
4
4
use core:: hash:: { Hash , Hasher } ;
5
5
use if_chain:: if_chain;
6
6
use itertools:: Itertools ;
7
- use rustc_data_structures:: fx:: FxHashMap ;
7
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
8
8
use rustc_data_structures:: unhash:: UnhashMap ;
9
9
use rustc_errors:: Applicability ;
10
10
use rustc_hir:: def:: Res ;
@@ -103,7 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
103
103
fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
104
104
self . check_type_repetition ( cx, gen) ;
105
105
check_trait_bound_duplication ( cx, gen) ;
106
- check_bounds_or_where_duplication ( cx, gen) ;
107
106
}
108
107
109
108
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
@@ -234,35 +233,61 @@ impl TraitBounds {
234
233
}
235
234
236
235
fn check_trait_bound_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
237
- if gen. span . from_expansion ( ) || gen . params . is_empty ( ) || gen . predicates . is_empty ( ) {
236
+ if gen. span . from_expansion ( ) {
238
237
return ;
239
238
}
240
239
241
- let mut map = FxHashMap :: < _ , Vec < _ > > :: default ( ) ;
242
- for predicate in gen. predicates {
240
+ // Explanation:
241
+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
242
+ // where T: Clone + Default, { unimplemented!(); }
243
+ // ^^^^^^^^^^^^^^^^^^
244
+ // |
245
+ // collects each of these where clauses into a set keyed by generic name and comparable trait
246
+ // eg. (T, Clone)
247
+ let where_predicates = gen
248
+ . predicates
249
+ . iter ( )
250
+ . filter_map ( |pred| {
251
+ if_chain ! {
252
+ if pred. in_where_clause( ) ;
253
+ if let WherePredicate :: BoundPredicate ( bound_predicate) = pred;
254
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
255
+ then {
256
+ return Some (
257
+ rollup_traits( cx, bound_predicate. bounds, "these where clauses contain repeated elements" )
258
+ . into_keys( ) . map( |trait_ref| ( path. res, trait_ref) ) )
259
+ }
260
+ }
261
+ None
262
+ } )
263
+ . flatten ( )
264
+ . collect :: < FxHashSet < _ > > ( ) ;
265
+
266
+ // Explanation:
267
+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
268
+ // ^^^^^^^^^^^^^^^^^^ ^^^^^^^
269
+ // |
270
+ // compare trait bounds keyed by generic name and comparable trait to collected where
271
+ // predicates eg. (T, Clone)
272
+ for predicate in gen. predicates . iter ( ) . filter ( |pred| !pred. in_where_clause ( ) ) {
243
273
if_chain ! {
244
- if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
274
+ if let WherePredicate :: BoundPredicate ( bound_predicate) = predicate;
245
275
if bound_predicate. origin != PredicateOrigin :: ImplTrait ;
246
276
if !bound_predicate. span. from_expansion( ) ;
247
- if let TyKind :: Path ( QPath :: Resolved ( _, Path { segments, .. } ) ) = bound_predicate. bounded_ty. kind;
248
- if let Some ( segment) = segments. first( ) ;
277
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
249
278
then {
250
- for ( res_where, _, span_where) in bound_predicate. bounds. iter( ) . filter_map( get_trait_info_from_bound) {
251
- let trait_resolutions_direct = map. entry( segment. ident) . or_default( ) ;
252
- if let Some ( ( _, span_direct) ) = trait_resolutions_direct
253
- . iter( )
254
- . find( |( res_direct, _) | * res_direct == res_where) {
279
+ let traits = rollup_traits( cx, bound_predicate. bounds, "these bounds contain repeated elements" ) ;
280
+ for ( trait_ref, span) in traits {
281
+ let key = ( path. res, trait_ref) ;
282
+ if where_predicates. contains( & key) {
255
283
span_lint_and_help(
256
284
cx,
257
285
TRAIT_DUPLICATION_IN_BOUNDS ,
258
- * span_direct ,
286
+ span ,
259
287
"this trait bound is already specified in the where clause" ,
260
288
None ,
261
289
"consider removing this trait bound" ,
262
- ) ;
263
- }
264
- else {
265
- trait_resolutions_direct. push( ( res_where, span_where) ) ;
290
+ ) ;
266
291
}
267
292
}
268
293
}
@@ -273,23 +298,6 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
273
298
#[ derive( PartialEq , Eq , Hash , Debug ) ]
274
299
struct ComparableTraitRef ( Res , Vec < Res > ) ;
275
300
276
- fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
277
- if gen. span . from_expansion ( ) {
278
- return ;
279
- }
280
-
281
- for predicate in gen. predicates {
282
- if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate {
283
- let msg = if predicate. in_where_clause ( ) {
284
- "these where clauses contain repeated elements"
285
- } else {
286
- "these bounds contain repeated elements"
287
- } ;
288
- rollup_traits ( cx, bound_predicate. bounds , msg) ;
289
- }
290
- }
291
- }
292
-
293
301
fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
294
302
if let GenericBound :: Trait ( t, tbm) = bound {
295
303
let trait_path = t. trait_ref . path ;
@@ -331,7 +339,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
331
339
)
332
340
}
333
341
334
- fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
342
+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) -> FxHashMap < ComparableTraitRef , Span > {
335
343
let mut map = FxHashMap :: default ( ) ;
336
344
let mut repeated_res = false ;
337
345
@@ -373,4 +381,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
373
381
) ;
374
382
}
375
383
}
384
+
385
+ map
376
386
}
0 commit comments