@@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
44use core:: hash:: { Hash , Hasher } ;
55use if_chain:: if_chain;
66use itertools:: Itertools ;
7- use rustc_data_structures:: fx:: FxHashMap ;
7+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
88use rustc_data_structures:: unhash:: UnhashMap ;
99use rustc_errors:: Applicability ;
1010use rustc_hir:: def:: Res ;
@@ -103,7 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
103103 fn check_generics ( & mut self , cx : & LateContext < ' tcx > , gen : & ' tcx Generics < ' _ > ) {
104104 self . check_type_repetition ( cx, gen) ;
105105 check_trait_bound_duplication ( cx, gen) ;
106- check_bounds_or_where_duplication ( cx, gen) ;
107106 }
108107
109108 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
@@ -234,35 +233,61 @@ impl TraitBounds {
234233}
235234
236235fn check_trait_bound_duplication ( cx : & LateContext < ' _ > , gen : & ' _ Generics < ' _ > ) {
237- if gen. span . from_expansion ( ) || gen . params . is_empty ( ) || gen . predicates . is_empty ( ) {
236+ if gen. span . from_expansion ( ) {
238237 return ;
239238 }
240239
241- let mut map = FxHashMap :: < _ , Vec < _ > > :: default ( ) ;
242- for predicate in gen. predicates {
240+ // Explanation:
241+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
242+ // where T: Clone + Default, { unimplemented!(); }
243+ // ^^^^^^^^^^^^^^^^^^
244+ // |
245+ // collects each of these where clauses into a set keyed by generic name and comparable trait
246+ // eg. (T, Clone)
247+ let where_predicates = gen
248+ . predicates
249+ . iter ( )
250+ . filter_map ( |pred| {
251+ if_chain ! {
252+ if pred. in_where_clause( ) ;
253+ if let WherePredicate :: BoundPredicate ( bound_predicate) = pred;
254+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
255+ then {
256+ return Some (
257+ rollup_traits( cx, bound_predicate. bounds, "these where clauses contain repeated elements" )
258+ . into_keys( ) . map( |trait_ref| ( path. res, trait_ref) ) )
259+ }
260+ }
261+ None
262+ } )
263+ . flatten ( )
264+ . collect :: < FxHashSet < _ > > ( ) ;
265+
266+ // Explanation:
267+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
268+ // ^^^^^^^^^^^^^^^^^^ ^^^^^^^
269+ // |
270+ // compare trait bounds keyed by generic name and comparable trait to collected where
271+ // predicates eg. (T, Clone)
272+ for predicate in gen. predicates . iter ( ) . filter ( |pred| !pred. in_where_clause ( ) ) {
243273 if_chain ! {
244- if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
274+ if let WherePredicate :: BoundPredicate ( bound_predicate) = predicate;
245275 if bound_predicate. origin != PredicateOrigin :: ImplTrait ;
246276 if !bound_predicate. span. from_expansion( ) ;
247- if let TyKind :: Path ( QPath :: Resolved ( _, Path { segments, .. } ) ) = bound_predicate. bounded_ty. kind;
248- if let Some ( segment) = segments. first( ) ;
277+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
249278 then {
250- for ( res_where, _, span_where) in bound_predicate. bounds. iter( ) . filter_map( get_trait_info_from_bound) {
251- let trait_resolutions_direct = map. entry( segment. ident) . or_default( ) ;
252- if let Some ( ( _, span_direct) ) = trait_resolutions_direct
253- . iter( )
254- . find( |( res_direct, _) | * res_direct == res_where) {
279+ let traits = rollup_traits( cx, bound_predicate. bounds, "these bounds contain repeated elements" ) ;
280+ for ( trait_ref, span) in traits {
281+ let key = ( path. res, trait_ref) ;
282+ if where_predicates. contains( & key) {
255283 span_lint_and_help(
256284 cx,
257285 TRAIT_DUPLICATION_IN_BOUNDS ,
258- * span_direct ,
286+ span ,
259287 "this trait bound is already specified in the where clause" ,
260288 None ,
261289 "consider removing this trait bound" ,
262- ) ;
263- }
264- else {
265- trait_resolutions_direct. push( ( res_where, span_where) ) ;
290+ ) ;
266291 }
267292 }
268293 }
@@ -273,23 +298,6 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
273298#[ derive( PartialEq , Eq , Hash , Debug ) ]
274299struct ComparableTraitRef ( Res , Vec < Res > ) ;
275300
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-
293301fn get_trait_info_from_bound < ' a > ( bound : & ' a GenericBound < ' _ > ) -> Option < ( Res , & ' a [ PathSegment < ' a > ] , Span ) > {
294302 if let GenericBound :: Trait ( t, tbm) = bound {
295303 let trait_path = t. trait_ref . path ;
@@ -331,7 +339,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
331339 )
332340}
333341
334- fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) {
342+ fn rollup_traits ( cx : & LateContext < ' _ > , bounds : & [ GenericBound < ' _ > ] , msg : & str ) -> FxHashMap < ComparableTraitRef , Span > {
335343 let mut map = FxHashMap :: default ( ) ;
336344 let mut repeated_res = false ;
337345
@@ -373,4 +381,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
373381 ) ;
374382 }
375383 }
384+
385+ map
376386}
0 commit comments