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:: LocalDefId ;
4
+ use rustc_hir:: def_id:: { DefId , LocalDefId } ;
5
5
use rustc_hir:: intravisit:: FnKind ;
6
6
use rustc_hir:: {
7
7
Body , FnDecl , FnRetTy , GenericArg , GenericBound , ImplItem , ImplItemKind , ItemKind , TraitBoundModifier , TraitItem ,
8
8
TraitItemKind , TyKind ,
9
9
} ;
10
10
use rustc_hir_analysis:: hir_ty_to_ty;
11
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
- use rustc_middle:: ty:: { self , ClauseKind , TyCtxt } ;
12
+ use rustc_middle:: ty:: { self , ClauseKind , Generics , Ty , TyCtxt } ;
13
13
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
14
14
use rustc_span:: Span ;
15
15
@@ -45,52 +45,80 @@ declare_clippy_lint! {
45
45
/// ```
46
46
#[ clippy:: version = "1.73.0" ]
47
47
pub IMPLIED_BOUNDS_IN_IMPLS ,
48
- complexity ,
48
+ nursery ,
49
49
"specifying bounds that are implied by other bounds in `impl Trait` type"
50
50
}
51
51
declare_lint_pass ! ( ImpliedBoundsInImpls => [ IMPLIED_BOUNDS_IN_IMPLS ] ) ;
52
52
53
- /// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
54
- /// check if the substituted type in the implied-by bound matches with what's subtituted in the
55
- /// implied type.
53
+ /// Tries to "resolve" a type.
54
+ /// The index passed to this function must start with `Self=0`, i.e. it must be a valid
55
+ /// type parameter index.
56
+ /// If the index is out of bounds, it means that the generic parameter has a default type.
57
+ fn try_resolve_type < ' tcx > (
58
+ tcx : TyCtxt < ' tcx > ,
59
+ args : & ' tcx [ GenericArg < ' tcx > ] ,
60
+ generics : & ' tcx Generics ,
61
+ index : usize ,
62
+ ) -> Option < Ty < ' tcx > > {
63
+ match args. get ( index - 1 ) {
64
+ Some ( GenericArg :: Type ( ty) ) => Some ( hir_ty_to_ty ( tcx, ty) ) ,
65
+ Some ( _) => None ,
66
+ None => Some ( tcx. type_of ( generics. params [ index] . def_id ) . skip_binder ( ) ) ,
67
+ }
68
+ }
69
+
70
+ /// This function tries to, for all generic type parameters in a supertrait predicate `trait ...<U>:
71
+ /// GenericTrait<U>`, check if the substituted type in the implied-by bound matches with what's
72
+ /// subtituted in the implied bound.
56
73
///
57
74
/// Consider this example.
58
75
/// ```rust,ignore
59
76
/// trait GenericTrait<T> {}
60
77
/// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
61
- /// ^ trait_predicate_args: [Self#0, U#2]
78
+ /// ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2]
79
+ /// (the Self#0 is implicit: `<Self as GenericTrait<U>>`)
62
80
/// impl GenericTrait<i32> for () {}
63
81
/// impl GenericSubTrait<(), i32, ()> for () {}
64
- /// impl GenericSubTrait<(), [u8; 8] , ()> for () {}
82
+ /// impl GenericSubTrait<(), i64 , ()> for () {}
65
83
///
66
- /// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
67
- /// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args
68
- /// (we are interested in `[u8; 8]` specifically, as that
69
- /// is what `U` in `GenericTrait<U>` is substituted with)
70
- /// ()
84
+ /// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), i64, ()> {
85
+ /// ^^^ implied_args ^^^^^^^^^^^ implied_by_args
86
+ /// (we are interested in `i64` specifically, as that
87
+ /// is what `U` in `GenericTrait<U>` is substituted with)
71
88
/// }
72
89
/// ```
73
- /// Here i32 != [u8; 8], so this will return false.
74
- fn is_same_generics (
75
- tcx : TyCtxt < ' _ > ,
76
- trait_predicate_args : & [ ty:: GenericArg < ' _ > ] ,
77
- implied_by_args : & [ GenericArg < ' _ > ] ,
78
- implied_args : & [ GenericArg < ' _ > ] ,
90
+ /// Here i32 != i64, so this will return false.
91
+ fn is_same_generics < ' tcx > (
92
+ tcx : TyCtxt < ' tcx > ,
93
+ trait_predicate_args : & ' tcx [ ty:: GenericArg < ' tcx > ] ,
94
+ implied_by_args : & ' tcx [ GenericArg < ' tcx > ] ,
95
+ implied_args : & ' tcx [ GenericArg < ' tcx > ] ,
96
+ implied_by_def_id : DefId ,
97
+ implied_def_id : DefId ,
79
98
) -> bool {
99
+ // Get the generics of the two traits to be able to get default generic parameter.
100
+ let implied_by_generics = tcx. generics_of ( implied_by_def_id) ;
101
+ let implied_generics = tcx. generics_of ( implied_def_id) ;
102
+
80
103
trait_predicate_args
81
104
. iter ( )
82
105
. enumerate ( )
83
106
. skip ( 1 ) // skip `Self` implicit arg
84
107
. all ( |( arg_index, arg) | {
85
- if let Some ( ty) = arg. as_type ( )
86
- && let & ty:: Param ( ty:: ParamTy { index, .. } ) = ty. kind ( )
87
- // Since `trait_predicate_args` and type params in traits start with `Self=0`
88
- // and generic argument lists `GenericTrait<i32>` don't have `Self`,
89
- // we need to subtract 1 from the index.
90
- && let GenericArg :: Type ( ty_a) = implied_by_args[ index as usize - 1 ]
91
- && let GenericArg :: Type ( ty_b) = implied_args[ arg_index - 1 ]
92
- {
93
- hir_ty_to_ty ( tcx, ty_a) == hir_ty_to_ty ( tcx, ty_b)
108
+ if let Some ( ty) = arg. as_type ( ) {
109
+ if let & ty:: Param ( ty:: ParamTy { index, .. } ) = ty. kind ( )
110
+ // `index == 0` means that it's referring to `Self`,
111
+ // in which case we don't try to substitute it
112
+ && index != 0
113
+ && let Some ( ty_a) = try_resolve_type ( tcx, implied_by_args, implied_by_generics, index as usize )
114
+ && let Some ( ty_b) = try_resolve_type ( tcx, implied_args, implied_generics, arg_index)
115
+ {
116
+ ty_a == ty_b
117
+ } else if let Some ( ty_b) = try_resolve_type ( tcx, implied_args, implied_generics, arg_index) {
118
+ ty == ty_b
119
+ } else {
120
+ false
121
+ }
94
122
} else {
95
123
false
96
124
}
@@ -121,7 +149,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
121
149
&& let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
122
150
&& !predicates. is_empty ( ) // If the trait has no supertrait, there is nothing to add.
123
151
{
124
- Some ( ( bound. span ( ) , path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) , predicates) )
152
+ Some ( ( bound. span ( ) , path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) , predicates, trait_def_id ) )
125
153
} else {
126
154
None
127
155
}
@@ -135,18 +163,27 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
135
163
&& let [ .., path] = poly_trait. trait_ref . path . segments
136
164
&& let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
137
165
&& 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
- }
166
+ && let Some ( implied_by_span) = implied_bounds
167
+ . iter ( )
168
+ . find_map ( |& ( span, implied_by_args, preds, implied_by_def_id) | {
169
+ preds. iter ( ) . find_map ( |( clause, _) | {
170
+ if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
171
+ && tr. def_id ( ) == def_id
172
+ && is_same_generics (
173
+ cx. tcx ,
174
+ tr. trait_ref . args ,
175
+ implied_by_args,
176
+ implied_args,
177
+ implied_by_def_id,
178
+ def_id,
179
+ )
180
+ {
181
+ Some ( span)
182
+ } else {
183
+ None
184
+ }
185
+ } )
148
186
} )
149
- } )
150
187
{
151
188
let implied_by = snippet ( cx, implied_by_span, ".." ) ;
152
189
span_lint_and_then (
0 commit comments