1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
2
use clippy_utils:: source:: snippet;
3
3
use rustc_errors:: { Applicability , SuggestionStyle } ;
4
- use rustc_hir:: def_id:: { DefId , LocalDefId } ;
4
+ use rustc_hir:: def_id:: DefId ;
5
5
use rustc_hir:: intravisit:: FnKind ;
6
6
use rustc_hir:: {
7
- Body , FnDecl , FnRetTy , GenericArg , GenericBound , ImplItem , ImplItemKind , ItemKind , TraitBoundModifier , TraitItem ,
8
- TraitItemKind , TyKind ,
7
+ Body , FnDecl , FnRetTy , GenericArg , GenericBound , ImplItem , ImplItemKind , ItemKind , OpaqueTy , TraitBoundModifier ,
8
+ TraitItem , TraitItemKind , TyKind , TypeBinding ,
9
9
} ;
10
10
use rustc_hir_analysis:: hir_ty_to_ty;
11
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
12
use rustc_middle:: ty:: { self , ClauseKind , Generics , Ty , TyCtxt } ;
13
13
use rustc_session:: declare_lint_pass;
14
+ use rustc_span:: def_id:: LocalDefId ;
14
15
use rustc_span:: Span ;
15
16
16
17
declare_clippy_lint ! {
@@ -50,20 +51,17 @@ declare_clippy_lint! {
50
51
}
51
52
declare_lint_pass ! ( ImpliedBoundsInImpls => [ IMPLIED_BOUNDS_IN_IMPLS ] ) ;
52
53
53
- #[ allow( clippy:: too_many_arguments) ]
54
54
fn emit_lint (
55
55
cx : & LateContext < ' _ > ,
56
56
poly_trait : & rustc_hir:: PolyTraitRef < ' _ > ,
57
57
opaque_ty : & rustc_hir:: OpaqueTy < ' _ > ,
58
58
index : usize ,
59
- // The bindings that were implied
59
+ // The bindings that were implied, used for suggestion purposes since removing a bound with associated types
60
+ // means we might need to then move it to a different bound
60
61
implied_bindings : & [ rustc_hir:: TypeBinding < ' _ > ] ,
61
- // The original bindings that `implied_bindings` are implied from
62
- implied_by_bindings : & [ rustc_hir:: TypeBinding < ' _ > ] ,
63
- implied_by_args : & [ GenericArg < ' _ > ] ,
64
- implied_by_span : Span ,
62
+ bound : & ImplTraitBound < ' _ > ,
65
63
) {
66
- let implied_by = snippet ( cx, implied_by_span , ".." ) ;
64
+ let implied_by = snippet ( cx, bound . span , ".." ) ;
67
65
68
66
span_lint_and_then (
69
67
cx,
@@ -93,17 +91,17 @@ fn emit_lint(
93
91
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
94
92
let omitted_assoc_tys: Vec < _ > = implied_bindings
95
93
. iter ( )
96
- . filter ( |binding| !implied_by_bindings . iter ( ) . any ( |b| b. ident == binding. ident ) )
94
+ . filter ( |binding| !bound . bindings . iter ( ) . any ( |b| b. ident == binding. ident ) )
97
95
. collect ( ) ;
98
96
99
97
if !omitted_assoc_tys. is_empty ( ) {
100
98
// `<>` needs to be added if there aren't yet any generic arguments or bindings
101
- let needs_angle_brackets = implied_by_args . is_empty ( ) && implied_by_bindings . is_empty ( ) ;
102
- let insert_span = match ( implied_by_args , implied_by_bindings ) {
99
+ let needs_angle_brackets = bound . args . is_empty ( ) && bound . bindings . is_empty ( ) ;
100
+ let insert_span = match ( bound . args , bound . bindings ) {
103
101
( [ .., arg] , [ .., binding] ) => arg. span ( ) . max ( binding. span ) . shrink_to_hi ( ) ,
104
102
( [ .., arg] , [ ] ) => arg. span ( ) . shrink_to_hi ( ) ,
105
103
( [ ] , [ .., binding] ) => binding. span . shrink_to_hi ( ) ,
106
- ( [ ] , [ ] ) => implied_by_span . shrink_to_hi ( ) ,
104
+ ( [ ] , [ ] ) => bound . span . shrink_to_hi ( ) ,
107
105
} ;
108
106
109
107
let mut associated_tys_sugg = if needs_angle_brackets {
@@ -223,42 +221,93 @@ fn is_same_generics<'tcx>(
223
221
} )
224
222
}
225
223
224
+ struct ImplTraitBound < ' tcx > {
225
+ /// The span of the bound in the `impl Trait` type
226
+ span : Span ,
227
+ /// The predicates defined in the trait referenced by this bound. This also contains the actual
228
+ /// supertrait bounds
229
+ predicates : & ' tcx [ ( ty:: Clause < ' tcx > , Span ) ] ,
230
+ /// The `DefId` of the trait being referenced by this bound
231
+ trait_def_id : DefId ,
232
+ /// The generic arguments on the `impl Trait` bound
233
+ args : & ' tcx [ GenericArg < ' tcx > ] ,
234
+ /// The associated types on this bound
235
+ bindings : & ' tcx [ TypeBinding < ' tcx > ] ,
236
+ }
237
+
238
+ /// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
239
+ ///
240
+ /// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
241
+ /// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
242
+ /// `Eq`.
243
+ fn collect_supertrait_bounds < ' tcx > ( cx : & LateContext < ' tcx > , opaque_ty : & OpaqueTy < ' tcx > ) -> Vec < ImplTraitBound < ' tcx > > {
244
+ opaque_ty
245
+ . bounds
246
+ . iter ( )
247
+ . filter_map ( |bound| {
248
+ if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
249
+ && let [ .., path] = poly_trait. trait_ref . path . segments
250
+ && poly_trait. bound_generic_params . is_empty ( )
251
+ && let Some ( trait_def_id) = path. res . opt_def_id ( )
252
+ && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
253
+ // If the trait has no supertrait, there is no need to collect anything from that bound
254
+ && !predicates. is_empty ( )
255
+ {
256
+ Some ( ImplTraitBound {
257
+ predicates,
258
+ args : path. args . map_or ( [ ] . as_slice ( ) , |p| p. args ) ,
259
+ bindings : path. args . map_or ( [ ] . as_slice ( ) , |p| p. bindings ) ,
260
+ trait_def_id,
261
+ span : bound. span ( ) ,
262
+ } )
263
+ } else {
264
+ None
265
+ }
266
+ } )
267
+ . collect ( )
268
+ }
269
+
270
+ /// Given a bound in an `impl Trait` type, looks for a trait in the set of supertraits (previously
271
+ /// collected in [`collect_supertrait_bounds`]) that matches (same trait and generic arguments).
272
+ fn find_bound_in_supertraits < ' a , ' tcx > (
273
+ cx : & LateContext < ' tcx > ,
274
+ trait_def_id : DefId ,
275
+ args : & ' tcx [ GenericArg < ' tcx > ] ,
276
+ bounds : & ' a [ ImplTraitBound < ' tcx > ] ,
277
+ ) -> Option < & ' a ImplTraitBound < ' tcx > > {
278
+ bounds. iter ( ) . find ( |bound| {
279
+ bound. predicates . iter ( ) . any ( |( clause, _) | {
280
+ if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
281
+ && tr. def_id ( ) == trait_def_id
282
+ {
283
+ is_same_generics (
284
+ cx. tcx ,
285
+ tr. trait_ref . args ,
286
+ bound. args ,
287
+ args,
288
+ bound. trait_def_id ,
289
+ trait_def_id,
290
+ )
291
+ } else {
292
+ false
293
+ }
294
+ } )
295
+ } )
296
+ }
297
+
226
298
fn check ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
227
299
if let FnRetTy :: Return ( ty) = decl. output
228
- &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
300
+ && let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
229
301
&& let item = cx. tcx . hir ( ) . item ( item_id)
230
302
&& let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
231
303
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232
304
// we can avoid doing a bunch of stuff unnecessarily.
233
305
&& opaque_ty. bounds . len ( ) > 1
234
306
{
235
- // Get all the (implied) trait predicates in the bounds.
236
- // For `impl Deref + DerefMut` this will contain [`Deref`].
237
- // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
238
- // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
239
- // Example:
240
- // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
241
- // `DerefMut::Target` needs to match `Deref::Target`.
242
- let implied_bounds: Vec < _ > = opaque_ty
243
- . bounds
244
- . iter ( )
245
- . filter_map ( |bound| {
246
- if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
247
- && let [ .., path] = poly_trait. trait_ref . path . segments
248
- && poly_trait. bound_generic_params . is_empty ( )
249
- && let Some ( trait_def_id) = path. res . opt_def_id ( )
250
- && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
251
- && !predicates. is_empty ( )
252
- // If the trait has no supertrait, there is nothing to add.
253
- {
254
- Some ( ( bound. span ( ) , path, predicates, trait_def_id) )
255
- } else {
256
- None
257
- }
258
- } )
259
- . collect ( ) ;
307
+ let supertraits = collect_supertrait_bounds ( cx, opaque_ty) ;
260
308
261
- // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
309
+ // Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
310
+ // supertraits of each of the bounds.
262
311
// This involves some extra logic when generic arguments are present, since
263
312
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264
313
for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
@@ -267,42 +316,9 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267
316
&& let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
268
317
&& let implied_bindings = path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings )
269
318
&& let Some ( def_id) = poly_trait. trait_ref . path . res . opt_def_id ( )
270
- && let Some ( ( implied_by_span, implied_by_args, implied_by_bindings) ) =
271
- implied_bounds
272
- . iter ( )
273
- . find_map ( |& ( span, implied_by_path, preds, implied_by_def_id) | {
274
- let implied_by_args = implied_by_path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) ;
275
- let implied_by_bindings = implied_by_path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings ) ;
276
-
277
- preds. iter ( ) . find_map ( |( clause, _) | {
278
- if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
279
- && tr. def_id ( ) == def_id
280
- && is_same_generics (
281
- cx. tcx ,
282
- tr. trait_ref . args ,
283
- implied_by_args,
284
- implied_args,
285
- implied_by_def_id,
286
- def_id,
287
- )
288
- {
289
- Some ( ( span, implied_by_args, implied_by_bindings) )
290
- } else {
291
- None
292
- }
293
- } )
294
- } )
319
+ && let Some ( bound) = find_bound_in_supertraits ( cx, def_id, implied_args, & supertraits)
295
320
{
296
- emit_lint (
297
- cx,
298
- poly_trait,
299
- opaque_ty,
300
- index,
301
- implied_bindings,
302
- implied_by_bindings,
303
- implied_by_args,
304
- implied_by_span,
305
- ) ;
321
+ emit_lint ( cx, poly_trait, opaque_ty, index, implied_bindings, bound) ;
306
322
}
307
323
}
308
324
}
0 commit comments