@@ -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 ;
@@ -238,31 +238,58 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
238238 return ;
239239 }
240240
241- let mut map = FxHashMap :: < _ , Vec < _ > > :: default ( ) ;
242- for predicate in gen. predicates {
241+ // Explanation:
242+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
243+ // where T: Clone + Default, { unimplemented!(); }
244+ // ^^^^^^^^^^^^^^^^^^
245+ // |
246+ // collects each of these where clauses into a set keyed by generic name and comparable trait
247+ // eg. (T, Clone)
248+ let where_predicates = gen
249+ . predicates
250+ . iter ( )
251+ . filter_map ( |pred| {
252+ if_chain ! {
253+ if pred. in_where_clause( ) ;
254+ if let WherePredicate :: BoundPredicate ( bound_predicate) = pred;
255+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
256+ then {
257+ return Some ( bound_predicate. bounds. iter( ) . filter_map( |t| {
258+ Some ( ( path. res, into_comparable_trait_ref( t. trait_ref( ) ?) ) )
259+ } ) )
260+ }
261+ }
262+ None
263+ } )
264+ . flatten ( )
265+ . collect :: < FxHashSet < _ > > ( ) ;
266+
267+ // Explanation:
268+ // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
269+ // ^^^^^^^^^^^^^^^^^^ ^^^^^^^
270+ // |
271+ // compare trait bounds keyed by generic name and comparable trait to collected where
272+ // predicates eg. (T, Clone)
273+ for predicate in gen. predicates . iter ( ) . filter ( |pred| !pred. in_where_clause ( ) ) {
243274 if_chain ! {
244- if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
275+ if let WherePredicate :: BoundPredicate ( bound_predicate) = predicate;
245276 if bound_predicate. origin != PredicateOrigin :: ImplTrait ;
246277 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( ) ;
278+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = bound_predicate. bounded_ty. kind;
249279 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) {
255- span_lint_and_help(
256- cx,
257- TRAIT_DUPLICATION_IN_BOUNDS ,
258- * span_direct,
259- "this trait bound is already specified in the where clause" ,
260- None ,
261- "consider removing this trait bound" ,
262- ) ;
263- }
264- else {
265- trait_resolutions_direct. push( ( res_where, span_where) ) ;
280+ for t in bound_predicate. bounds {
281+ if let Some ( trait_ref) = t. trait_ref( ) {
282+ let key = ( path. res, into_comparable_trait_ref( trait_ref) ) ;
283+ if where_predicates. contains( & key) {
284+ span_lint_and_help(
285+ cx,
286+ TRAIT_DUPLICATION_IN_BOUNDS ,
287+ t. span( ) ,
288+ "this trait bound is already specified in the where clause" ,
289+ None ,
290+ "consider removing this trait bound" ,
291+ ) ;
292+ }
266293 }
267294 }
268295 }
0 commit comments