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 . impl_trait_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 . impl_trait_bound_span . shrink_to_hi ( ) ,
107105 } ;
108106
109107 let mut associated_tys_sugg = if needs_angle_brackets {
@@ -223,42 +221,64 @@ fn is_same_generics<'tcx>(
223221 } )
224222}
225223
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+
226269fn check ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
227270 if let FnRetTy :: Return ( ty) = decl. output
228- &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
271+ && let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
229272 && let item = cx. tcx . hir ( ) . item ( item_id)
230273 && let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
231274 // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
232275 // we can avoid doing a bunch of stuff unnecessarily.
233276 && opaque_ty. bounds . len ( ) > 1
234277 {
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) ;
260279
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.
262282 // This involves some extra logic when generic arguments are present, since
263283 // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
264284 for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
@@ -267,42 +287,26 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
267287 && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
268288 && let implied_bindings = path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings )
269289 && 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+ } )
295308 {
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) ;
306310 }
307311 }
308312 }
0 commit comments