@@ -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 , Ty , TyKind ,
12+ 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,26 @@ 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- }
293-
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( " + " ) ;
284+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
285+ struct ComparableBound (
286+ Res ,
287+ Vec < Res > ,
288+ // Vec<ComparableBound>
289+ ) ;
306290
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 ( ) ) {
291+ fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
292+ if gen. span . from_expansion ( ) {
321293 return ;
322294 }
323295
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 {
296+ for predicate in gen. predicates {
332297 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- ) ;
298+ let msg = if predicate. in_where_clause ( ) {
299+ "these where clauses contain repeated elements"
300+ } else {
301+ "these bounds contain repeated elements"
302+ } ;
303+ rollup_traits ( cx, bound_predicate. bounds , msg) ;
338304 }
339305 }
340306}
@@ -346,3 +312,76 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
346312 None
347313 }
348314}
315+
316+ fn try_into_comparable_bound ( bound : & GenericBound < ' _ > ) -> Option < ComparableBound > {
317+ if let GenericBound :: Trait ( t, _) = bound {
318+ Some ( ComparableBound (
319+ t. trait_ref . path . res ,
320+ t. trait_ref
321+ . path
322+ . segments
323+ . iter ( )
324+ . filter_map ( |segment| {
325+ // get trait bound type arguments
326+ Some ( segment. args ?. args . iter ( ) . filter_map ( |arg| {
327+ if_chain ! {
328+ if let GenericArg :: Type ( ty) = arg;
329+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind;
330+ then { return Some ( path. res) }
331+ }
332+ None
333+ } ) )
334+ } )
335+ . flatten ( )
336+ . collect ( ) ,
337+ // t.bound_generic_params
338+ // .iter()
339+ // .flat_map(|param| param.bounds.iter().filter_map(try_into_comparable_bound))
340+ // .collect(),
341+ ) )
342+ } else {
343+ None
344+ }
345+ }
346+
347+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
348+ let mut map = FxHashMap :: default ( ) ;
349+ let mut repeated_spans = false ;
350+ for bound in bounds. iter ( ) . filter_map ( |bound| {
351+ if let GenericBound :: Trait ( t, _) = bound {
352+ Some ( ( try_into_comparable_bound ( bound) ?, t. span ) )
353+ } else {
354+ None
355+ }
356+ } ) {
357+ let ( comparable_bound, span_direct) = bound;
358+ if map. insert ( comparable_bound, span_direct) . is_some ( ) {
359+ repeated_spans = true ;
360+ }
361+ }
362+
363+ if_chain ! {
364+ if repeated_spans;
365+ if let Some ( first_trait) = bounds. get( 0 ) ;
366+ if let Some ( last_trait) = bounds. iter( ) . last( ) ;
367+ then {
368+ let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
369+
370+ let mut traits = map. values( )
371+ . filter_map( |span| snippet_opt( cx, * span) )
372+ . collect:: <Vec <_>>( ) ;
373+ traits. sort_unstable( ) ;
374+ let traits = traits. join( " + " ) ;
375+
376+ span_lint_and_sugg(
377+ cx,
378+ REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
379+ all_trait_span,
380+ msg,
381+ "try" ,
382+ traits,
383+ Applicability :: MachineApplicable
384+ ) ;
385+ }
386+ }
387+ }
0 commit comments