@@ -10,35 +10,43 @@ use rustc_span::symbol::Ident;
1010use crate :: { LateContext , LateLintPass , LintContext } ;
1111
1212declare_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.
1415 ///
1516 /// ### Example
1617 ///
1718 /// ```rust
19+ /// #![feature(sized_hierarchy)]
20+ /// use std::marker::MetaSized;
1821 /// // `T` cannot be unsized because `Clone` requires it to be `Sized`
1922 /// 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) {}
2025 /// ```
2126 ///
2227 /// {{produces}}
2328 ///
2429 /// ### Explanation
2530 ///
2631 /// 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.
2834 pub REDUNDANT_SIZEDNESS_BOUND ,
2935 Warn ,
30- "a `?Sized` bound that is unusable due to a `Sized` requirement "
36+ "a sizedness bound that is redundant due to another bound "
3137}
3238declare_lint_pass ! ( RedundantSizednessBound => [ REDUNDANT_SIZEDNESS_BOUND ] ) ;
3339
3440struct Bound < ' tcx > {
3541 /// The [`DefId`] of the type parameter the bound refers to
3642 param : DefId ,
43+ /// Identifier of type parameter
3744 ident : Ident ,
38-
45+ /// A reference to the trait bound applied to the parameter
3946 trait_bound : & ' tcx PolyTraitRef < ' tcx > ,
40-
47+ /// The index of the predicate within the generics predicate list
4148 predicate_pos : usize ,
49+ /// Position of the bound in the bounds list of a predicate
4250 bound_pos : usize ,
4351}
4452
@@ -73,12 +81,16 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
7381}
7482
7583/// 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 {
7991 let trait_def_id = * path. last ( ) . unwrap ( ) ;
8092
81- if Some ( trait_def_id) == cx . tcx . lang_items ( ) . sized_trait ( ) {
93+ if trait_def_id == target {
8294 return true ;
8395 }
8496
@@ -90,7 +102,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
90102 && !path. contains ( & trait_predicate. def_id ( ) )
91103 {
92104 path. push ( trait_predicate. def_id ( ) ) ;
93- if search ( cx, path) {
105+ if search ( cx, path, target ) {
94106 return true ;
95107 }
96108 path. pop ( ) ;
@@ -101,36 +113,53 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
101113 }
102114
103115 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)
105117}
106118
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 ( ) ;
112135
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 ;
131157 diag. span_note (
132158 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+ ) ,
134163 ) ;
135164
136165 for & [ current_id, next_id] in path. array_windows ( ) {
@@ -141,17 +170,72 @@ impl LateLintPass<'_> for RedundantSizednessBound {
141170
142171 diag. span_suggestion_verbose (
143172 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,
146179 ) ,
147- "change the bounds that require `Sized`, or remove the `?Sized` bound" ,
148180 "" ,
149181 Applicability :: MaybeIncorrect ,
150182 ) ;
151- } ) ;
183+ } ,
184+ ) ;
152185
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 ;
155239 }
156240 }
157241}
0 commit comments