@@ -8,8 +8,8 @@ use rustc_data_structures::unhash::UnhashMap;
88use rustc_errors:: Applicability ;
99use rustc_hir:: def:: Res ;
1010use 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 ,
1313} ;
1414use rustc_lint:: { LateContext , LateLintPass } ;
1515use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -95,7 +95,7 @@ declare_clippy_lint! {
9595 /// ```
9696 #[ clippy:: version = "1.62.0" ]
9797 pub REPEATED_WHERE_CLAUSES_OR_TRAIT_BOUNDS ,
98- pedantic ,
98+ nursery ,
9999 "Traits are repeated within trait bounds or where clause"
100100}
101101
@@ -284,61 +284,22 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
284284 }
285285}
286286
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 > ) ;
297289
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 ( ) {
325292 return ;
326293 }
327294
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 {
336296 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) ;
342303 }
343304 }
344305}
@@ -350,3 +311,68 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
350311 None
351312 }
352313}
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_CLAUSES_OR_TRAIT_BOUNDS ,
370+ all_trait_span,
371+ msg,
372+ "try" ,
373+ traits,
374+ Applicability :: MachineApplicable
375+ ) ;
376+ }
377+ }
378+ }
0 commit comments