11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet;
3+ use rustc_errors:: { Applicability , SuggestionStyle } ;
34use rustc_hir:: def_id:: LocalDefId ;
45use rustc_hir:: intravisit:: FnKind ;
5- use rustc_hir:: { Body , FnDecl , FnRetTy , GenericArg , GenericBound , ItemKind , TraitBoundModifier , TyKind } ;
6+ use rustc_hir:: {
7+ Body , FnDecl , FnRetTy , GenericArg , GenericBound , ImplItem , ImplItemKind , ItemKind , TraitBoundModifier , TraitItem ,
8+ TraitItemKind , TyKind ,
9+ } ;
610use rustc_hir_analysis:: hir_ty_to_ty;
711use rustc_lint:: { LateContext , LateLintPass } ;
8- use rustc_errors:: { Applicability , SuggestionStyle } ;
912use rustc_middle:: ty:: { self , ClauseKind , TyCtxt } ;
1013use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1114use rustc_span:: Span ;
@@ -94,6 +97,86 @@ fn is_same_generics(
9497 } )
9598}
9699
100+ fn check ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
101+ if let FnRetTy :: Return ( ty) = decl. output
102+ &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
103+ && let item = cx. tcx . hir ( ) . item ( item_id)
104+ && let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
105+ // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
106+ // we can avoid doing a bunch of stuff unnecessarily.
107+ && opaque_ty. bounds . len ( ) > 1
108+ {
109+ // Get all the (implied) trait predicates in the bounds.
110+ // For `impl Deref + DerefMut` this will contain [`Deref`].
111+ // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
112+ // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
113+ // Example:
114+ // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
115+ // `DerefMut::Target` needs to match `Deref::Target`.
116+ let implied_bounds: Vec < _ > = opaque_ty. bounds . iter ( ) . filter_map ( |bound| {
117+ if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
118+ && let [ .., path] = poly_trait. trait_ref . path . segments
119+ && poly_trait. bound_generic_params . is_empty ( )
120+ && let Some ( trait_def_id) = path. res . opt_def_id ( )
121+ && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
122+ && !predicates. is_empty ( ) // If the trait has no supertrait, there is nothing to add.
123+ {
124+ Some ( ( bound. span ( ) , path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) , predicates) )
125+ } else {
126+ None
127+ }
128+ } ) . collect ( ) ;
129+
130+ // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
131+ // This involves some extra logic when generic arguments are present, since
132+ // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
133+ for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
134+ if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
135+ && let [ .., path] = poly_trait. trait_ref . path . segments
136+ && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
137+ && let Some ( def_id) = poly_trait. trait_ref . path . res . opt_def_id ( )
138+ && let Some ( implied_by_span) = implied_bounds. iter ( ) . find_map ( |& ( span, implied_by_args, preds) | {
139+ preds. iter ( ) . find_map ( |( clause, _) | {
140+ if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
141+ && tr. def_id ( ) == def_id
142+ && is_same_generics ( cx. tcx , tr. trait_ref . args , implied_by_args, implied_args)
143+ {
144+ Some ( span)
145+ } else {
146+ None
147+ }
148+ } )
149+ } )
150+ {
151+ let implied_by = snippet ( cx, implied_by_span, ".." ) ;
152+ span_lint_and_then (
153+ cx, IMPLIED_BOUNDS_IN_IMPLS ,
154+ poly_trait. span ,
155+ & format ! ( "this bound is already specified as the supertrait of `{implied_by}`" ) ,
156+ |diag| {
157+ // If we suggest removing a bound, we may also need extend the span
158+ // to include the `+` token, so we don't end up with something like `impl + B`
159+
160+ let implied_span_extended = if let Some ( next_bound) = opaque_ty. bounds . get ( index + 1 ) {
161+ poly_trait. span . to ( next_bound. span ( ) . shrink_to_lo ( ) )
162+ } else {
163+ poly_trait. span
164+ } ;
165+
166+ diag. span_suggestion_with_style (
167+ implied_span_extended,
168+ "try removing this bound" ,
169+ "" ,
170+ Applicability :: MachineApplicable ,
171+ SuggestionStyle :: ShowAlways
172+ ) ;
173+ }
174+ ) ;
175+ }
176+ }
177+ }
178+ }
179+
97180impl LateLintPass < ' _ > for ImpliedBoundsInImpls {
98181 fn check_fn (
99182 & mut self ,
@@ -104,82 +187,16 @@ impl LateLintPass<'_> for ImpliedBoundsInImpls {
104187 _: Span ,
105188 _: LocalDefId ,
106189 ) {
107- if let FnRetTy :: Return ( ty) = decl. output
108- &&let TyKind :: OpaqueDef ( item_id, ..) = ty. kind
109- && let item = cx. tcx . hir ( ) . item ( item_id)
110- && let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
111- // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
112- // we can avoid doing a bunch of stuff unnecessarily.
113- && opaque_ty. bounds . len ( ) > 1
114- {
115- // Get all the (implied) trait predicates in the bounds.
116- // For `impl Deref + DerefMut` this will contain [`Deref`].
117- // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
118- // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
119- // Example:
120- // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
121- // `DerefMut::Target` needs to match `Deref::Target`.
122- let implied_bounds: Vec < _ > = opaque_ty. bounds . iter ( ) . filter_map ( |bound| {
123- if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
124- && let [ .., path] = poly_trait. trait_ref . path . segments
125- && poly_trait. bound_generic_params . is_empty ( )
126- && let Some ( trait_def_id) = path. res . opt_def_id ( )
127- && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
128- && !predicates. is_empty ( ) // If the trait has no supertrait, there is nothing to add.
129- {
130- Some ( ( bound. span ( ) , path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) , predicates) )
131- } else {
132- None
133- }
134- } ) . collect ( ) ;
135-
136- // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
137- // This involves some extra logic when generic arguments are present, since
138- // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
139- for ( index, bound) in opaque_ty. bounds . iter ( ) . enumerate ( ) {
140- if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
141- && let [ .., path] = poly_trait. trait_ref . path . segments
142- && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
143- && let Some ( def_id) = poly_trait. trait_ref . path . res . opt_def_id ( )
144- && let Some ( implied_by_span) = implied_bounds. iter ( ) . find_map ( |& ( span, implied_by_args, preds) | {
145- preds. iter ( ) . find_map ( |( clause, _) | {
146- if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
147- && tr. def_id ( ) == def_id
148- && is_same_generics ( cx. tcx , tr. trait_ref . args , implied_by_args, implied_args)
149- {
150- Some ( span)
151- } else {
152- None
153- }
154- } )
155- } )
156- {
157- let implied_by = snippet ( cx, implied_by_span, ".." ) ;
158- span_lint_and_then (
159- cx, IMPLIED_BOUNDS_IN_IMPLS ,
160- poly_trait. span ,
161- & format ! ( "this bound is already specified as the supertrait of `{}`" , implied_by) ,
162- |diag| {
163- // If we suggest removing a bound, we may also need extend the span
164- // to include the `+` token, so we don't end up with something like `impl + B`
165-
166- let implied_span_extended = if let Some ( next_bound) = opaque_ty. bounds . get ( index + 1 ) {
167- poly_trait. span . to ( next_bound. span ( ) . shrink_to_lo ( ) )
168- } else {
169- poly_trait. span
170- } ;
171-
172- diag. span_suggestion_with_style (
173- implied_span_extended,
174- "try removing this bound" ,
175- "" ,
176- Applicability :: MachineApplicable ,
177- SuggestionStyle :: ShowAlways
178- ) ;
179- }
180- ) ;
181- }
182- }
190+ check ( cx, decl) ;
191+ }
192+ fn check_trait_item ( & mut self , cx : & LateContext < ' _ > , item : & TraitItem < ' _ > ) {
193+ if let TraitItemKind :: Fn ( sig, ..) = & item. kind {
194+ check ( cx, sig. decl ) ;
195+ }
196+ }
197+ fn check_impl_item ( & mut self , cx : & LateContext < ' _ > , item : & ImplItem < ' _ > ) {
198+ if let ImplItemKind :: Fn ( sig, ..) = & item. kind {
199+ check ( cx, sig. decl ) ;
183200 }
184201 }
185202}
0 commit comments