@@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
4
4
use core:: hash:: { Hash , Hasher } ;
5
5
use if_chain:: if_chain;
6
6
use itertools:: Itertools ;
7
- use rustc_data_structures:: fx:: FxHashMap ;
7
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
8
8
use rustc_data_structures:: unhash:: UnhashMap ;
9
9
use rustc_errors:: Applicability ;
10
10
use rustc_hir:: def:: Res ;
@@ -238,31 +238,58 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
238
238
return ;
239
239
}
240
240
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 ( ) ) {
243
274
if_chain ! {
244
- if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
275
+ if let WherePredicate :: BoundPredicate ( bound_predicate) = predicate;
245
276
if bound_predicate. origin != PredicateOrigin :: ImplTrait ;
246
277
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;
249
279
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
+ }
266
293
}
267
294
}
268
295
}
0 commit comments