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} ;
33use clippy_utils:: { SpanlessEq , SpanlessHash } ;
44use core:: hash:: { Hash , Hasher } ;
55use if_chain:: if_chain;
@@ -9,8 +9,8 @@ use rustc_data_structures::unhash::UnhashMap;
99use rustc_errors:: Applicability ;
1010use rustc_hir:: def:: Res ;
1111use 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 ,
1414} ;
1515use rustc_lint:: { LateContext , LateLintPass } ;
1616use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -36,7 +36,7 @@ declare_clippy_lint! {
3636 #[ clippy:: version = "1.38.0" ]
3737 pub TYPE_REPETITION_IN_BOUNDS ,
3838 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: _`"
4040}
4141
4242declare_clippy_lint ! {
@@ -63,10 +63,26 @@ declare_clippy_lint! {
6363 ///
6464 /// fn func<T>(arg: T) where T: Clone + Default {}
6565 /// ```
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+ /// ```
6682 #[ clippy:: version = "1.47.0" ]
6783 pub TRAIT_DUPLICATION_IN_BOUNDS ,
6884 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"
7086}
7187
7288#[ derive( Copy , Clone ) ]
@@ -87,6 +103,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
87103 fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
88104 self . check_type_repetition ( cx, gen) ;
89105 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+ }
90119 }
91120
92121 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<'_>) {
241270 }
242271}
243272
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+
244293fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
245294 if let GenericBound :: Trait ( t, tbm) = bound {
246295 let trait_path = t. trait_ref . path ;
@@ -257,3 +306,71 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
257306 None
258307 }
259308}
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