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