@@ -8,8 +8,8 @@ use rustc_data_structures::unhash::UnhashMap;
8
8
use rustc_errors:: Applicability ;
9
9
use rustc_hir:: def:: Res ;
10
10
use rustc_hir:: {
11
- GenericBound , Generics , Item , ItemKind , Node , Path , PathSegment , PredicateOrigin , QPath , TraitItem , Ty , TyKind ,
12
- WherePredicate ,
11
+ GenericArg , GenericBound , Generics , Item , ItemKind , Node , Path , PathSegment , PredicateOrigin , QPath , TraitItem ,
12
+ TraitRef , Ty , TyKind , WherePredicate ,
13
13
} ;
14
14
use rustc_lint:: { LateContext , LateLintPass } ;
15
15
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -95,7 +95,7 @@ declare_clippy_lint! {
95
95
/// ```
96
96
#[ clippy:: version = "1.62.0" ]
97
97
pub REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
98
- pedantic ,
98
+ nursery ,
99
99
"Traits are repeated within trait bounds or where clause"
100
100
}
101
101
@@ -284,61 +284,22 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
284
284
}
285
285
}
286
286
287
- fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
288
- fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
289
- let mut map = FxHashMap :: default ( ) ;
290
- let mut repeated_spans = false ;
291
- for bound in bounds. iter ( ) . filter_map ( get_trait_info_from_bound) {
292
- let ( definition, _, span_direct) = bound;
293
- if map. insert ( definition, span_direct) . is_some ( ) {
294
- repeated_spans = true ;
295
- }
296
- }
287
+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
288
+ struct ComparableTraitRef ( Res , Vec < Res > ) ;
297
289
298
- if_chain ! {
299
- if repeated_spans;
300
- if let Some ( first_trait) = bounds. get( 0 ) ;
301
- if let Some ( last_trait) = bounds. iter( ) . last( ) ;
302
- then {
303
- let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
304
-
305
- let mut traits = map. values( )
306
- . filter_map( |span| snippet_opt( cx, * span) )
307
- . collect:: <Vec <_>>( ) ;
308
- traits. sort_unstable( ) ;
309
- let traits = traits. join( " + " ) ;
310
-
311
- span_lint_and_sugg(
312
- cx,
313
- REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
314
- all_trait_span,
315
- msg,
316
- "try" ,
317
- traits,
318
- Applicability :: MachineApplicable
319
- ) ;
320
- }
321
- }
322
- }
323
-
324
- if gen. span . from_expansion ( ) || ( gen. params . is_empty ( ) && gen. where_clause . predicates . is_empty ( ) ) {
290
+ fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
291
+ if gen. span . from_expansion ( ) {
325
292
return ;
326
293
}
327
294
328
- for param in gen. params {
329
- if let ParamName :: Plain ( _) = param. name {
330
- // other alternatives are errors and elided which won't have duplicates
331
- rollup_traits ( cx, param. bounds , "this trait bound contains repeated elements" ) ;
332
- }
333
- }
334
-
335
- for predicate in gen. where_clause . predicates {
295
+ for predicate in gen. predicates {
336
296
if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate {
337
- rollup_traits (
338
- cx,
339
- bound_predicate. bounds ,
340
- "this where clause contains repeated elements" ,
341
- ) ;
297
+ let msg = if predicate. in_where_clause ( ) {
298
+ "these where clauses contain repeated elements"
299
+ } else {
300
+ "these bounds contain repeated elements"
301
+ } ;
302
+ rollup_traits ( cx, bound_predicate. bounds , msg) ;
342
303
}
343
304
}
344
305
}
@@ -350,3 +311,68 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
350
311
None
351
312
}
352
313
}
314
+
315
+ // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
316
+ fn into_comparable_trait_ref ( trait_ref : & TraitRef < ' _ > ) -> ComparableTraitRef {
317
+ ComparableTraitRef (
318
+ trait_ref. path . res ,
319
+ trait_ref
320
+ . path
321
+ . segments
322
+ . iter ( )
323
+ . filter_map ( |segment| {
324
+ // get trait bound type arguments
325
+ Some ( segment. args ?. args . iter ( ) . filter_map ( |arg| {
326
+ if_chain ! {
327
+ if let GenericArg :: Type ( ty) = arg;
328
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind;
329
+ then { return Some ( path. res) }
330
+ }
331
+ None
332
+ } ) )
333
+ } )
334
+ . flatten ( )
335
+ . collect ( ) ,
336
+ )
337
+ }
338
+
339
+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
340
+ let mut map = FxHashMap :: default ( ) ;
341
+ let mut repeated_res = false ;
342
+ for bound in bounds. iter ( ) . filter_map ( |bound| {
343
+ if let GenericBound :: Trait ( t, _) = bound {
344
+ Some ( ( into_comparable_trait_ref ( & t. trait_ref ) , t. span ) )
345
+ } else {
346
+ None
347
+ }
348
+ } ) {
349
+ let ( comparable_bound, span_direct) = bound;
350
+ if map. insert ( comparable_bound, span_direct) . is_some ( ) {
351
+ repeated_res = true ;
352
+ }
353
+ }
354
+
355
+ if_chain ! {
356
+ if repeated_res;
357
+ if let [ first_trait, .., last_trait] = bounds;
358
+ then {
359
+ let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
360
+
361
+ let mut traits = map. values( )
362
+ . filter_map( |span| snippet_opt( cx, * span) )
363
+ . collect:: <Vec <_>>( ) ;
364
+ traits. sort_unstable( ) ;
365
+ let traits = traits. join( " + " ) ;
366
+
367
+ span_lint_and_sugg(
368
+ cx,
369
+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
370
+ all_trait_span,
371
+ msg,
372
+ "try" ,
373
+ traits,
374
+ Applicability :: MachineApplicable
375
+ ) ;
376
+ }
377
+ }
378
+ }
0 commit comments