1
1
use clippy_utils:: diagnostics:: span_lint;
2
2
use rustc_hir:: def_id:: LocalDefId ;
3
3
use rustc_hir:: intravisit:: FnKind ;
4
- use rustc_hir:: { Body , FnDecl , FnRetTy , GenericArgs , GenericBound , ItemKind , TraitBoundModifier , TyKind } ;
4
+ use rustc_hir:: { Body , FnDecl , FnRetTy , GenericArg , GenericBound , ItemKind , TraitBoundModifier , TyKind } ;
5
+ use rustc_hir_analysis:: hir_ty_to_ty;
5
6
use rustc_lint:: { LateContext , LateLintPass } ;
6
- use rustc_middle:: ty:: ClauseKind ;
7
+ use rustc_middle:: ty:: { self , ClauseKind , TyCtxt } ;
7
8
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
9
use rustc_span:: Span ;
9
10
@@ -17,17 +18,19 @@ declare_clippy_lint! {
17
18
/// Unnecessary complexity.
18
19
///
19
20
/// ### Known problems
20
- /// This lint currently does not work with generic traits (i.e. will not lint even if redundant) .
21
+ /// This lint does not transitively look for implied bounds past the first supertrait .
21
22
///
22
23
/// ### Example
23
24
/// ```rust
25
+ /// # use std::ops::{Deref,DerefMut};
24
26
/// fn f() -> impl Deref<Target = i32> + DerefMut<Target = i32> {
25
27
/// // ^^^^^^^^^^^^^^^^^^^ unnecessary bound, already implied by the `DerefMut` trait bound
26
28
/// Box::new(123)
27
29
/// }
28
30
/// ```
29
31
/// Use instead:
30
32
/// ```rust
33
+ /// # use std::ops::{Deref,DerefMut};
31
34
/// fn f() -> impl DerefMut<Target = i32> {
32
35
/// Box::new(123)
33
36
/// }
@@ -39,6 +42,53 @@ declare_clippy_lint! {
39
42
}
40
43
declare_lint_pass ! ( ImpliedBoundsInImpl => [ IMPLIED_BOUNDS_IN_IMPL ] ) ;
41
44
45
+ /// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
46
+ /// check if the substituted type in the implied-by bound matches with what's subtituted in the
47
+ /// implied type.
48
+ ///
49
+ /// Consider this example function.
50
+ /// ```rust,ignore
51
+ /// trait GenericTrait<T> {}
52
+ /// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
53
+ /// ^ trait_predicate_args: [Self#0, U#2]
54
+ /// impl GenericTrait<i32> for () {}
55
+ /// impl GenericSubTrait<(), i32, ()> for () {}
56
+ /// impl GenericSubTrait<(), [u8; 8], ()> for () {}
57
+ ///
58
+ /// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
59
+ /// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args
60
+ /// (we are interested in `[u8; 8]` specifically, as that
61
+ /// is what `U` in `GenericTrait<U>` is substituted with)
62
+ /// ()
63
+ /// }
64
+ /// ```
65
+ /// Here i32 != [u8; 8], so this will return false.
66
+ fn is_same_generics (
67
+ tcx : TyCtxt < ' _ > ,
68
+ trait_predicate_args : & [ ty:: GenericArg < ' _ > ] ,
69
+ implied_by_args : & [ GenericArg < ' _ > ] ,
70
+ implied_args : & [ GenericArg < ' _ > ] ,
71
+ ) -> bool {
72
+ trait_predicate_args
73
+ . iter ( )
74
+ . enumerate ( )
75
+ . skip ( 1 ) // skip `Self` implicit arg
76
+ . all ( |( arg_index, arg) | {
77
+ if let Some ( ty) = arg. as_type ( )
78
+ && let & ty:: Param ( ty:: ParamTy { index, .. } ) = ty. kind ( )
79
+ // Since `trait_predicate_args` and type params in traits start with `Self=0`
80
+ // and generic argument lists `GenericTrait<i32>` don't have `Self`,
81
+ // we need to subtract 1 from the index.
82
+ && let GenericArg :: Type ( ty_a) = implied_by_args[ index as usize - 1 ]
83
+ && let GenericArg :: Type ( ty_b) = implied_args[ arg_index - 1 ]
84
+ {
85
+ hir_ty_to_ty ( tcx, ty_a) == hir_ty_to_ty ( tcx, ty_b)
86
+ } else {
87
+ false
88
+ }
89
+ } )
90
+ }
91
+
42
92
impl LateLintPass < ' _ > for ImpliedBoundsInImpl {
43
93
fn check_fn (
44
94
& mut self ,
@@ -54,37 +104,52 @@ impl LateLintPass<'_> for ImpliedBoundsInImpl {
54
104
&& let item = cx. tcx . hir ( ) . item ( item_id)
55
105
&& let ItemKind :: OpaqueTy ( opaque_ty) = item. kind
56
106
{
57
- // Get all `DefId`s of (implied) trait predicates in all the bounds.
107
+ // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
108
+ // we can avoid doing a bunch of stuff unnecessarily.
109
+ if opaque_ty. bounds . is_empty ( ) {
110
+ return ;
111
+ }
112
+
113
+ // Get all the (implied) trait predicates in the bounds.
58
114
// For `impl Deref + DerefMut` this will contain [`Deref`].
59
115
// The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
60
116
61
- // N.B. Generic args on trait bounds are currently ignored and (G)ATs are fine to disregard,
62
- // because they must be the same for all of its supertraits. Example:
117
+ // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
118
+ // Example:
63
119
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
64
120
// `DerefMut::Target` needs to match `Deref::Target`
65
- let implied_bounds = opaque_ty. bounds . iter ( ) . flat_map ( |bound| {
121
+ let implied_bounds: Vec < _ > = opaque_ty. bounds . iter ( ) . filter_map ( |bound| {
66
122
if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
67
123
&& let [ .., path] = poly_trait. trait_ref . path . segments
68
124
&& poly_trait. bound_generic_params . is_empty ( )
69
- && path. args . map_or ( true , GenericArgs :: is_empty)
70
125
&& let Some ( trait_def_id) = path. res . opt_def_id ( )
126
+ && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
127
+ && !predicates. is_empty ( ) // If the trait has no supertrait, there is nothing to add.
71
128
{
72
- cx . tcx . implied_predicates_of ( trait_def_id ) . predicates
129
+ Some ( ( path . args . map_or ( [ ] . as_slice ( ) , |a| a . args ) , predicates) )
73
130
} else {
74
- & [ ]
131
+ None
75
132
}
76
- } ) . collect :: < Vec < _ > > ( ) ;
133
+ } ) . collect ( ) ;
77
134
78
135
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
136
+ // This involves some extra logic when generic arguments are present, since
137
+ // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
79
138
for bound in opaque_ty. bounds {
80
139
if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
140
+ && let [ .., path] = poly_trait. trait_ref . path . segments
141
+ && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
81
142
&& let Some ( def_id) = poly_trait. trait_ref . path . res . opt_def_id ( )
82
- && implied_bounds. iter ( ) . any ( |( clause, _) | {
83
- if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( ) {
84
- tr. def_id ( ) == def_id
85
- } else {
86
- false
87
- }
143
+ && implied_bounds. iter ( ) . any ( |( implied_by_args, preds) | {
144
+ preds. iter ( ) . any ( |( clause, _) | {
145
+ if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
146
+ && tr. def_id ( ) == def_id
147
+ {
148
+ is_same_generics ( cx. tcx , tr. trait_ref . args , implied_by_args, implied_args)
149
+ } else {
150
+ false
151
+ }
152
+ } )
88
153
} )
89
154
{
90
155
span_lint ( cx, IMPLIED_BOUNDS_IN_IMPL , poly_trait. span , "this bound is implied by another bound and can be removed" ) ;
0 commit comments