@@ -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} ;
@@ -271,61 +271,22 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
271271 }
272272}
273273
274- fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
275- fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
276- let mut map = FxHashMap :: default ( ) ;
277- let mut repeated_spans = false ;
278- for bound in bounds. iter ( ) . filter_map ( get_trait_info_from_bound) {
279- let ( definition, _, span_direct) = bound;
280- if map. insert ( definition, span_direct) . is_some ( ) {
281- repeated_spans = true ;
282- }
283- }
284-
285- if_chain ! {
286- if repeated_spans;
287- if let Some ( first_trait) = bounds. get( 0 ) ;
288- if let Some ( last_trait) = bounds. iter( ) . last( ) ;
289- then {
290- let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
291-
292- let mut traits = map. values( )
293- . filter_map( |span| snippet_opt( cx, * span) )
294- . collect:: <Vec <_>>( ) ;
295- traits. sort_unstable( ) ;
296- let traits = traits. join( " + " ) ;
297-
298- span_lint_and_sugg(
299- cx,
300- REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND ,
301- all_trait_span,
302- msg,
303- "try" ,
304- traits,
305- Applicability :: MachineApplicable
306- ) ;
307- }
308- }
309- }
274+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
275+ struct ComparableTraitRef ( Res , Vec < Res > ) ;
310276
311- if gen. span . from_expansion ( ) || ( gen. params . is_empty ( ) && gen. where_clause . predicates . is_empty ( ) ) {
277+ fn check_bounds_or_where_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
278+ if gen. span . from_expansion ( ) {
312279 return ;
313280 }
314281
315- for param in gen. params {
316- if let ParamName :: Plain ( _) = param. name {
317- // other alternatives are errors and elided which won't have duplicates
318- rollup_traits ( cx, param. bounds , "this trait bound contains repeated elements" ) ;
319- }
320- }
321-
322- for predicate in gen. where_clause . predicates {
282+ for predicate in gen. predicates {
323283 if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate {
324- rollup_traits (
325- cx,
326- bound_predicate. bounds ,
327- "this where clause contains repeated elements" ,
328- ) ;
284+ let msg = if predicate. in_where_clause ( ) {
285+ "these where clauses contain repeated elements"
286+ } else {
287+ "these bounds contain repeated elements"
288+ } ;
289+ rollup_traits ( cx, bound_predicate. bounds , msg) ;
329290 }
330291 }
331292}
@@ -346,3 +307,68 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
346307 None
347308 }
348309}
310+
311+ // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
312+ fn into_comparable_trait_ref ( trait_ref : & TraitRef < ' _ > ) -> ComparableTraitRef {
313+ ComparableTraitRef (
314+ trait_ref. path . res ,
315+ trait_ref
316+ . path
317+ . segments
318+ . iter ( )
319+ . filter_map ( |segment| {
320+ // get trait bound type arguments
321+ Some ( segment. args ?. args . iter ( ) . filter_map ( |arg| {
322+ if_chain ! {
323+ if let GenericArg :: Type ( ty) = arg;
324+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind;
325+ then { return Some ( path. res) }
326+ }
327+ None
328+ } ) )
329+ } )
330+ . flatten ( )
331+ . collect ( ) ,
332+ )
333+ }
334+
335+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
336+ let mut map = FxHashMap :: default ( ) ;
337+ let mut repeated_res = false ;
338+ for bound in bounds. iter ( ) . filter_map ( |bound| {
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+ let ( comparable_bound, span_direct) = bound;
346+ if map. insert ( comparable_bound, span_direct) . is_some ( ) {
347+ repeated_res = true ;
348+ }
349+ }
350+
351+ if_chain ! {
352+ if repeated_res;
353+ if let [ first_trait, .., last_trait] = bounds;
354+ then {
355+ let all_trait_span = first_trait. span( ) . to( last_trait. span( ) ) ;
356+
357+ let mut traits = map. values( )
358+ . filter_map( |span| snippet_opt( cx, * span) )
359+ . collect:: <Vec <_>>( ) ;
360+ traits. sort_unstable( ) ;
361+ let traits = traits. join( " + " ) ;
362+
363+ span_lint_and_sugg(
364+ cx,
365+ REPEATED_WHERE_CLAUSES_OR_TRAIT_BOUNDS ,
366+ all_trait_span,
367+ msg,
368+ "try" ,
369+ traits,
370+ Applicability :: MachineApplicable
371+ ) ;
372+ }
373+ }
374+ }
0 commit comments