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 . impl_trait_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 . impl_trait_bound_span . shrink_to_hi ( ) ,
107
105
} ;
108
106
109
107
let mut associated_tys_sugg = if needs_angle_brackets {
@@ -223,42 +221,64 @@ 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
+ impl_trait_bound_span : Span ,
227
+ /// The predicates defined in the trait referenced by this bound
228
+ predicates : & ' tcx [ ( ty:: Clause < ' tcx > , Span ) ] ,
229
+ /// The `DefId` of the trait being referenced by this bound
230
+ trait_def_id : DefId ,
231
+ /// The generic arguments on the `impl Trait` bound
232
+ args : & ' tcx [ GenericArg < ' tcx > ] ,
233
+ /// The associated types on this bound
234
+ bindings : & ' tcx [ TypeBinding < ' tcx > ] ,
235
+ }
236
+
237
+ /// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
238
+ ///
239
+ /// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
240
+ /// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
241
+ /// `Eq`.
242
+ fn collect_supertrait_bounds < ' tcx > ( cx : & LateContext < ' tcx > , opaque_ty : & OpaqueTy < ' tcx > ) -> Vec < ImplTraitBound < ' tcx > > {
243
+ opaque_ty
244
+ . bounds
245
+ . iter ( )
246
+ . filter_map ( |bound| {
247
+ if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
248
+ && let [ .., path] = poly_trait. trait_ref . path . segments
249
+ && poly_trait. bound_generic_params . is_empty ( )
250
+ && let Some ( trait_def_id) = path. res . opt_def_id ( )
251
+ && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
252
+ // If the trait has no supertrait, there is no need to collect anything from that bound
253
+ && !predicates. is_empty ( )
254
+ {
255
+ Some ( ImplTraitBound {
256
+ predicates,
257
+ args : path. args . map_or ( [ ] . as_slice ( ) , |p| p. args ) ,
258
+ bindings : path. args . map_or ( [ ] . as_slice ( ) , |p| p. bindings ) ,
259
+ trait_def_id,
260
+ impl_trait_bound_span : bound. span ( ) ,
261
+ } )
262
+ } else {
263
+ None
264
+ }
265
+ } )
266
+ . collect ( )
267
+ }
268
+
226
269
fn check ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
227
270
if let FnRetTy :: Return ( ty) = decl. output
228
- &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
271
+ && let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
229
272
&& let item = cx. tcx . hir ( ) . item ( item_id)
230
273
&& let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
231
274
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232
275
// we can avoid doing a bunch of stuff unnecessarily.
233
276
&& opaque_ty. bounds . len ( ) > 1
234
277
{
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 ( ) ;
278
+ let supertraits = collect_supertrait_bounds ( cx, opaque_ty) ;
260
279
261
- // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
280
+ // Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
281
+ // supertraits of each of the bounds.
262
282
// This involves some extra logic when generic arguments are present, since
263
283
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264
284
for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
@@ -267,42 +287,26 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267
287
&& let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
268
288
&& let implied_bindings = path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings )
269
289
&& 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
- } )
290
+ && let Some ( bound) = supertraits. iter ( ) . find ( |bound| {
291
+ bound. predicates . iter ( ) . any ( |( clause, _) | {
292
+ if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
293
+ && tr. def_id ( ) == def_id
294
+ {
295
+ is_same_generics (
296
+ cx. tcx ,
297
+ tr. trait_ref . args ,
298
+ bound. args ,
299
+ implied_args,
300
+ bound. trait_def_id ,
301
+ def_id,
302
+ )
303
+ } else {
304
+ false
305
+ }
306
+ } )
307
+ } )
295
308
{
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
- ) ;
309
+ emit_lint ( cx, poly_trait, opaque_ty, index, implied_bindings, bound) ;
306
310
}
307
311
}
308
312
}
0 commit comments