1
- use clippy_utils:: diagnostics:: span_lint_and_help;
2
- use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg } ;
2
+ use clippy_utils:: source:: { snippet, snippet_opt , snippet_with_applicability} ;
3
3
use clippy_utils:: { SpanlessEq , SpanlessHash } ;
4
4
use core:: hash:: { Hash , Hasher } ;
5
5
use if_chain:: if_chain;
@@ -9,8 +9,8 @@ use rustc_data_structures::unhash::UnhashMap;
9
9
use rustc_errors:: Applicability ;
10
10
use rustc_hir:: def:: Res ;
11
11
use rustc_hir:: {
12
- GenericBound , Generics , Item , ItemKind , Node , Path , PathSegment , PredicateOrigin , QPath , TraitBoundModifier ,
13
- TraitItem , Ty , TyKind , WherePredicate ,
12
+ GenericArg , GenericBound , Generics , Item , ItemKind , Node , Path , PathSegment , PredicateOrigin , QPath ,
13
+ TraitBoundModifier , TraitItem , TraitRef , Ty , TyKind , WherePredicate ,
14
14
} ;
15
15
use rustc_lint:: { LateContext , LateLintPass } ;
16
16
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -36,7 +36,7 @@ declare_clippy_lint! {
36
36
#[ clippy:: version = "1.38.0" ]
37
37
pub TYPE_REPETITION_IN_BOUNDS ,
38
38
nursery,
39
- "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
39
+ "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
40
40
}
41
41
42
42
declare_clippy_lint ! {
@@ -63,10 +63,26 @@ declare_clippy_lint! {
63
63
///
64
64
/// fn func<T>(arg: T) where T: Clone + Default {}
65
65
/// ```
66
+ ///
67
+ /// ```rust
68
+ /// fn foo<T: Default + Default>(bar: T) {}
69
+ /// ```
70
+ /// Use instead:
71
+ /// ```rust
72
+ /// fn foo<T: Default>(bar: T) {}
73
+ /// ```
74
+ ///
75
+ /// ```rust
76
+ /// fn foo<T>(bar: T) where T: Default + Default {}
77
+ /// ```
78
+ /// Use instead:
79
+ /// ```rust
80
+ /// fn foo<T>(bar: T) where T: Default {}
81
+ /// ```
66
82
#[ clippy:: version = "1.47.0" ]
67
83
pub TRAIT_DUPLICATION_IN_BOUNDS ,
68
84
nursery,
69
- "Check if the same trait bounds are specified twice during a function declaration"
85
+ "check if the same trait bounds are specified more than once during a generic declaration"
70
86
}
71
87
72
88
#[ derive( Copy , Clone ) ]
@@ -87,6 +103,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
87
103
fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
88
104
self . check_type_repetition ( cx, gen) ;
89
105
check_trait_bound_duplication ( cx, gen) ;
106
+ check_bounds_or_where_duplication ( cx, gen) ;
107
+ }
108
+
109
+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
110
+ // special handling for self trait bounds as these are not considered generics
111
+ // ie. trait Foo: Display {}
112
+ if let Item {
113
+ kind : ItemKind :: Trait ( _, _, _, bounds, ..) ,
114
+ ..
115
+ } = item
116
+ {
117
+ rollup_traits ( cx, bounds, "these bounds contain repeated elements" ) ;
118
+ }
90
119
}
91
120
92
121
fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx TraitItem < ' tcx > ) {
@@ -241,6 +270,26 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
241
270
}
242
271
}
243
272
273
+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
274
+ struct ComparableTraitRef ( Res , Vec < Res > ) ;
275
+
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
+
244
293
fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
245
294
if let GenericBound :: Trait ( t, tbm) = bound {
246
295
let trait_path = t. trait_ref . path ;
@@ -257,3 +306,71 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
257
306
None
258
307
}
259
308
}
309
+
310
+ // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
311
+ fn into_comparable_trait_ref ( trait_ref : & TraitRef < ' _ > ) -> ComparableTraitRef {
312
+ ComparableTraitRef (
313
+ trait_ref. path . res ,
314
+ trait_ref
315
+ . path
316
+ . segments
317
+ . iter ( )
318
+ . filter_map ( |segment| {
319
+ // get trait bound type arguments
320
+ Some ( segment. args ?. args . iter ( ) . filter_map ( |arg| {
321
+ if_chain ! {
322
+ if let GenericArg :: Type ( ty) = arg;
323
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind;
324
+ then { return Some ( path. res) }
325
+ }
326
+ None
327
+ } ) )
328
+ } )
329
+ . flatten ( )
330
+ . collect ( ) ,
331
+ )
332
+ }
333
+
334
+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
335
+ let mut map = FxHashMap :: default ( ) ;
336
+ let mut repeated_res = false ;
337
+
338
+ let only_comparable_trait_refs = |bound : & GenericBound < ' _ > | {
339
+ if let GenericBound :: Trait ( t, _) = bound {
340
+ Some ( ( into_comparable_trait_ref ( & t. trait_ref ) , t. span ) )
341
+ } else {
342
+ None
343
+ }
344
+ } ;
345
+
346
+ for bound in bounds. iter ( ) . filter_map ( only_comparable_trait_refs) {
347
+ let ( comparable_bound, span_direct) = bound;
348
+ if map. insert ( comparable_bound, span_direct) . is_some ( ) {
349
+ repeated_res = true ;
350
+ }
351
+ }
352
+
353
+ if_chain ! {
354
+ if repeated_res;
355
+ if let [ first_trait, .., last_trait] = bounds;
356
+ then {
357
+ let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
358
+
359
+ let mut traits = map. values( )
360
+ . filter_map( |span| snippet_opt( cx, * span) )
361
+ . collect:: <Vec <_>>( ) ;
362
+ traits. sort_unstable( ) ;
363
+ let traits = traits. join( " + " ) ;
364
+
365
+ span_lint_and_sugg(
366
+ cx,
367
+ TRAIT_DUPLICATION_IN_BOUNDS ,
368
+ all_trait_span,
369
+ msg,
370
+ "try" ,
371
+ traits,
372
+ Applicability :: MachineApplicable
373
+ ) ;
374
+ }
375
+ }
376
+ }
0 commit comments