@@ -10,35 +10,43 @@ use rustc_span::symbol::Ident;
10
10
use crate :: { LateContext , LateLintPass , LintContext } ;
11
11
12
12
declare_lint ! {
13
- /// The `redundant_sizedness_bound` lint detects `?Sized` bounds applied to type parameters that cannot be unsized.
13
+ /// The `redundant_sizedness_bound` lint detects redundant sizedness bounds applied to type parameters that are already
14
+ /// otherwise implied.
14
15
///
15
16
/// ### Example
16
17
///
17
18
/// ```rust
19
+ /// #![feature(sized_hierarchy)]
20
+ /// use std::marker::MetaSized;
18
21
/// // `T` cannot be unsized because `Clone` requires it to be `Sized`
19
22
/// fn f<T: Clone + ?Sized>(t: &T) {}
23
+ /// // `T` is `Sized` due to `Clone` bound, thereby implying `MetaSized` and making the explicit `MetaSized` bound redundant.
24
+ /// fn g<T: MetaSized + Clone>(t: &T) {}
20
25
/// ```
21
26
///
22
27
/// {{produces}}
23
28
///
24
29
/// ### Explanation
25
30
///
26
31
/// The `?Sized` bound is misleading because it cannot be satisfied by an
27
- /// unsized type. This lint notifies the user of said redundant bound.
32
+ /// unsized type, similarly sizedness bounds that are already implied by other bounds.
33
+ /// This lint notifies the user of said redundant bound.
28
34
pub REDUNDANT_SIZEDNESS_BOUND ,
29
35
Warn ,
30
- "a `?Sized` bound that is unusable due to a `Sized` requirement "
36
+ "a sizedness bound that is redundant due to another bound "
31
37
}
32
38
declare_lint_pass ! ( RedundantSizednessBound => [ REDUNDANT_SIZEDNESS_BOUND ] ) ;
33
39
34
40
struct Bound < ' tcx > {
35
41
/// The [`DefId`] of the type parameter the bound refers to
36
42
param : DefId ,
43
+ /// Identifier of type parameter
37
44
ident : Ident ,
38
-
45
+ /// A reference to the trait bound applied to the parameter
39
46
trait_bound : & ' tcx PolyTraitRef < ' tcx > ,
40
-
47
+ /// The index of the predicate within the generics predicate list
41
48
predicate_pos : usize ,
49
+ /// Position of the bound in the bounds list of a predicate
42
50
bound_pos : usize ,
43
51
}
44
52
@@ -73,12 +81,16 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
73
81
}
74
82
75
83
/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the
76
- /// path taken to find a `Sized` bound if one is found
77
- fn path_to_sized_bound ( cx : & LateContext < ' _ > , trait_bound : & PolyTraitRef < ' _ > ) -> Option < Vec < DefId > > {
78
- fn search ( cx : & LateContext < ' _ > , path : & mut Vec < DefId > ) -> bool {
84
+ /// path taken to find the `target` bound if one is found
85
+ fn path_to_bound (
86
+ cx : & LateContext < ' _ > ,
87
+ trait_bound : & PolyTraitRef < ' _ > ,
88
+ target : DefId ,
89
+ ) -> Option < Vec < DefId > > {
90
+ fn search ( cx : & LateContext < ' _ > , path : & mut Vec < DefId > , target : DefId ) -> bool {
79
91
let trait_def_id = * path. last ( ) . unwrap ( ) ;
80
92
81
- if Some ( trait_def_id) == cx . tcx . lang_items ( ) . sized_trait ( ) {
93
+ if trait_def_id == target {
82
94
return true ;
83
95
}
84
96
@@ -90,7 +102,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
90
102
&& !path. contains ( & trait_predicate. def_id ( ) )
91
103
{
92
104
path. push ( trait_predicate. def_id ( ) ) ;
93
- if search ( cx, path) {
105
+ if search ( cx, path, target ) {
94
106
return true ;
95
107
}
96
108
path. pop ( ) ;
@@ -101,36 +113,53 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
101
113
}
102
114
103
115
let mut path = vec ! [ trait_bound. trait_ref. trait_def_id( ) ?] ;
104
- search ( cx, & mut path) . then_some ( path)
116
+ search ( cx, & mut path, target ) . then_some ( path)
105
117
}
106
118
107
- impl LateLintPass < ' _ > for RedundantSizednessBound {
108
- fn check_generics ( & mut self , cx : & LateContext < ' _ > , generics : & Generics < ' _ > ) {
109
- let Some ( sized_trait) = cx. tcx . lang_items ( ) . sized_trait ( ) else {
110
- return ;
111
- } ;
119
+ // Checks if there exists a bound `redundant_bound` that is already implied by `implicit_bound`
120
+ fn check_redundant_sizedness_bound (
121
+ redundant_bound : DefId ,
122
+ redundant_bound_polarity : BoundPolarity ,
123
+ implicit_bound : DefId ,
124
+ cx : & LateContext < ' _ > ,
125
+ generics : & Generics < ' _ > ,
126
+ ) -> bool {
127
+ let redundant_sized_params: DefIdMap < _ > = type_param_bounds ( generics)
128
+ . filter ( |bound| {
129
+ bound. trait_bound . trait_ref . trait_def_id ( ) == Some ( redundant_bound)
130
+ && std:: mem:: discriminant ( & bound. trait_bound . modifiers . polarity )
131
+ == std:: mem:: discriminant ( & redundant_bound_polarity)
132
+ } )
133
+ . map ( |bound| ( bound. param , bound) )
134
+ . collect ( ) ;
112
135
113
- let maybe_sized_params: DefIdMap < _ > = type_param_bounds ( generics)
114
- . filter ( |bound| {
115
- bound. trait_bound . trait_ref . trait_def_id ( ) == Some ( sized_trait)
116
- && matches ! ( bound. trait_bound. modifiers. polarity, BoundPolarity :: Maybe ( _) )
117
- } )
118
- . map ( |bound| ( bound. param , bound) )
119
- . collect ( ) ;
120
-
121
- for bound in type_param_bounds ( generics) {
122
- if bound. trait_bound . modifiers == TraitBoundModifiers :: NONE
123
- && let Some ( sized_bound) = maybe_sized_params. get ( & bound. param )
124
- && let Some ( path) = path_to_sized_bound ( cx, bound. trait_bound )
125
- {
126
- cx. span_lint ( REDUNDANT_SIZEDNESS_BOUND , sized_bound. trait_bound . span , |diag| {
127
- diag. primary_message (
128
- "`?Sized` bound is ignored because of a `Sized` requirement" ,
129
- ) ;
130
- let ty_param = sized_bound. ident ;
136
+ for bound in type_param_bounds ( generics) {
137
+ if bound. trait_bound . modifiers == TraitBoundModifiers :: NONE
138
+ && let Some ( redundant_sized_bound) = redundant_sized_params. get ( & bound. param )
139
+ && let Some ( path) = path_to_bound ( cx, bound. trait_bound , implicit_bound)
140
+ {
141
+ let redundant_bound_polarity_str = match redundant_bound_polarity {
142
+ BoundPolarity :: Maybe ( _) => "?" ,
143
+ _ => "" ,
144
+ } ;
145
+ cx. span_lint (
146
+ REDUNDANT_SIZEDNESS_BOUND ,
147
+ redundant_sized_bound. trait_bound . span ,
148
+ |diag| {
149
+ let redundant_bound_str = cx. tcx . def_path_str ( redundant_bound) ;
150
+ let implicit_bound_str = cx. tcx . def_path_str ( implicit_bound) ;
151
+
152
+ diag. primary_message ( format ! (
153
+ "`{}{}` bound is redundant because of a `{}` requirement" ,
154
+ redundant_bound_polarity_str, redundant_bound_str, implicit_bound_str,
155
+ ) ) ;
156
+ let ty_param = redundant_sized_bound. ident ;
131
157
diag. span_note (
132
158
bound. trait_bound . span ,
133
- format ! ( "`{ty_param}` cannot be unsized because of the bound" ) ,
159
+ format ! (
160
+ "`{ty_param}` is implied to be `{}` because of the bound" ,
161
+ implicit_bound_str,
162
+ ) ,
134
163
) ;
135
164
136
165
for & [ current_id, next_id] in path. array_windows ( ) {
@@ -141,17 +170,72 @@ impl LateLintPass<'_> for RedundantSizednessBound {
141
170
142
171
diag. span_suggestion_verbose (
143
172
generics. span_for_bound_removal (
144
- sized_bound. predicate_pos ,
145
- sized_bound. bound_pos ,
173
+ redundant_sized_bound. predicate_pos ,
174
+ redundant_sized_bound. bound_pos ,
175
+ ) ,
176
+ format ! (
177
+ "change the bounds that require `{}`, or remove the `{}{}` bound" ,
178
+ implicit_bound_str, redundant_bound_polarity_str, redundant_bound_str,
146
179
) ,
147
- "change the bounds that require `Sized`, or remove the `?Sized` bound" ,
148
180
"" ,
149
181
Applicability :: MaybeIncorrect ,
150
182
) ;
151
- } ) ;
183
+ } ,
184
+ ) ;
152
185
153
- return ;
154
- }
186
+ return true ;
187
+ }
188
+ }
189
+ false
190
+ }
191
+
192
+ impl LateLintPass < ' _ > for RedundantSizednessBound {
193
+ fn check_generics ( & mut self , cx : & LateContext < ' _ > , generics : & Generics < ' _ > ) {
194
+ let Some ( sized_trait) = cx. tcx . lang_items ( ) . sized_trait ( ) else {
195
+ return ;
196
+ } ;
197
+ let Some ( meta_sized_trait) = cx. tcx . lang_items ( ) . meta_sized_trait ( ) else {
198
+ return ;
199
+ } ;
200
+ let Some ( pointee_sized_trait) = cx. tcx . lang_items ( ) . pointee_sized_trait ( ) else {
201
+ return ;
202
+ } ;
203
+
204
+ if check_redundant_sizedness_bound (
205
+ sized_trait,
206
+ BoundPolarity :: Maybe ( Default :: default ( ) ) ,
207
+ sized_trait,
208
+ cx,
209
+ generics,
210
+ ) {
211
+ return ;
212
+ }
213
+ if check_redundant_sizedness_bound (
214
+ meta_sized_trait,
215
+ BoundPolarity :: Positive ,
216
+ sized_trait,
217
+ cx,
218
+ generics,
219
+ ) {
220
+ return ;
221
+ }
222
+ if check_redundant_sizedness_bound (
223
+ pointee_sized_trait,
224
+ BoundPolarity :: Positive ,
225
+ sized_trait,
226
+ cx,
227
+ generics,
228
+ ) {
229
+ return ;
230
+ }
231
+ if check_redundant_sizedness_bound (
232
+ pointee_sized_trait,
233
+ BoundPolarity :: Positive ,
234
+ meta_sized_trait,
235
+ cx,
236
+ generics,
237
+ ) {
238
+ return ;
155
239
}
156
240
}
157
241
}
0 commit comments