11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet;
33use rustc_errors:: { Applicability , SuggestionStyle } ;
4- use rustc_hir:: def_id:: { DefId , LocalDefId } ;
4+ use rustc_hir:: def_id:: DefId ;
55use rustc_hir:: intravisit:: FnKind ;
66use 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 ,
99} ;
1010use rustc_hir_analysis:: hir_ty_to_ty;
1111use rustc_lint:: { LateContext , LateLintPass } ;
1212use rustc_middle:: ty:: { self , ClauseKind , Generics , Ty , TyCtxt } ;
1313use rustc_session:: declare_lint_pass;
14+ use rustc_span:: def_id:: LocalDefId ;
1415use rustc_span:: Span ;
1516
1617declare_clippy_lint ! {
@@ -50,20 +51,17 @@ declare_clippy_lint! {
5051}
5152declare_lint_pass ! ( ImpliedBoundsInImpls => [ IMPLIED_BOUNDS_IN_IMPLS ] ) ;
5253
53- #[ allow( clippy:: too_many_arguments) ]
5454fn emit_lint (
5555 cx : & LateContext < ' _ > ,
5656 poly_trait : & rustc_hir:: PolyTraitRef < ' _ > ,
5757 opaque_ty : & rustc_hir:: OpaqueTy < ' _ > ,
5858 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
6061 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 < ' _ > ,
6563) {
66- let implied_by = snippet ( cx, implied_by_span , ".." ) ;
64+ let implied_by = snippet ( cx, bound . span , ".." ) ;
6765
6866 span_lint_and_then (
6967 cx,
@@ -93,17 +91,17 @@ fn emit_lint(
9391 // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
9492 let omitted_assoc_tys: Vec < _ > = implied_bindings
9593 . 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 ) )
9795 . collect ( ) ;
9896
9997 if !omitted_assoc_tys. is_empty ( ) {
10098 // `<>` 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 ) {
103101 ( [ .., arg] , [ .., binding] ) => arg. span ( ) . max ( binding. span ) . shrink_to_hi ( ) ,
104102 ( [ .., arg] , [ ] ) => arg. span ( ) . shrink_to_hi ( ) ,
105103 ( [ ] , [ .., binding] ) => binding. span . shrink_to_hi ( ) ,
106- ( [ ] , [ ] ) => implied_by_span . shrink_to_hi ( ) ,
104+ ( [ ] , [ ] ) => bound . span . shrink_to_hi ( ) ,
107105 } ;
108106
109107 let mut associated_tys_sugg = if needs_angle_brackets {
@@ -223,42 +221,93 @@ fn is_same_generics<'tcx>(
223221 } )
224222}
225223
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+
226298fn check ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
227299 if let FnRetTy :: Return ( ty) = decl. output
228- &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
300+ && let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
229301 && let item = cx. tcx . hir ( ) . item ( item_id)
230302 && let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
231303 // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232304 // we can avoid doing a bunch of stuff unnecessarily.
233305 && opaque_ty. bounds . len ( ) > 1
234306 {
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) ;
260308
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.
262311 // This involves some extra logic when generic arguments are present, since
263312 // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264313 for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
@@ -267,42 +316,9 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267316 && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
268317 && let implied_bindings = path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings )
269318 && 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)
295320 {
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) ;
306322 }
307323 }
308324 }
0 commit comments