22
33use crate :: ty:: TyKind :: * ;
44use crate :: ty:: { InferTy , TyCtxt , TyS } ;
5+ use rustc_data_structures:: fx:: FxHashSet ;
56use rustc_errors:: { Applicability , DiagnosticBuilder } ;
67use rustc_hir as hir;
78use rustc_hir:: def_id:: DefId ;
@@ -105,6 +106,116 @@ pub fn suggest_arbitrary_trait_bound(
105106 true
106107}
107108
109+ fn suggest_removing_unsized_bound (
110+ generics : & hir:: Generics < ' _ > ,
111+ err : & mut DiagnosticBuilder < ' _ > ,
112+ param_name : & str ,
113+ param : & hir:: GenericParam < ' _ > ,
114+ def_id : Option < DefId > ,
115+ ) {
116+ // See if there's a `?Sized` bound that can be removed to suggest that.
117+ // First look at the `where` clause because we can have `where T: ?Sized`, but that
118+ // `?Sized` bound is *also* included in the `GenericParam` as a bound, which breaks
119+ // the spans. Hence the somewhat involved logic that follows.
120+ let mut where_unsized_bounds = FxHashSet :: default ( ) ;
121+ for ( where_pos, predicate) in generics. where_clause . predicates . iter ( ) . enumerate ( ) {
122+ match predicate {
123+ WherePredicate :: BoundPredicate ( WhereBoundPredicate {
124+ bounded_ty :
125+ hir:: Ty {
126+ kind :
127+ hir:: TyKind :: Path ( hir:: QPath :: Resolved (
128+ None ,
129+ hir:: Path {
130+ segments : [ segment] ,
131+ res : hir:: def:: Res :: Def ( hir:: def:: DefKind :: TyParam , _) ,
132+ ..
133+ } ,
134+ ) ) ,
135+ ..
136+ } ,
137+ bounds,
138+ span,
139+ ..
140+ } ) if segment. ident . as_str ( ) == param_name => {
141+ for ( pos, bound) in bounds. iter ( ) . enumerate ( ) {
142+ match bound {
143+ hir:: GenericBound :: Unsized ( _) => { }
144+ hir:: GenericBound :: Trait ( poly, hir:: TraitBoundModifier :: Maybe )
145+ if poly. trait_ref . trait_def_id ( ) == def_id => { }
146+ _ => continue ,
147+ }
148+ let sp = match (
149+ bounds. len ( ) ,
150+ pos,
151+ generics. where_clause . predicates . len ( ) ,
152+ where_pos,
153+ ) {
154+ // where T: ?Sized
155+ // ^^^^^^^^^^^^^^^
156+ ( 1 , _, 1 , _) => generics. where_clause . span ,
157+ // where Foo: Bar, T: ?Sized,
158+ // ^^^^^^^^^^^
159+ ( 1 , _, len, pos) if pos == len - 1 => generics. where_clause . predicates
160+ [ pos - 1 ]
161+ . span ( )
162+ . shrink_to_hi ( )
163+ . to ( * span) ,
164+ // where T: ?Sized, Foo: Bar,
165+ // ^^^^^^^^^^^
166+ ( 1 , _, _, pos) => {
167+ span. until ( generics. where_clause . predicates [ pos + 1 ] . span ( ) )
168+ }
169+ // where T: ?Sized + Bar, Foo: Bar,
170+ // ^^^^^^^^^
171+ ( _, 0 , _, _) => bound. span ( ) . to ( bounds[ 1 ] . span ( ) . shrink_to_lo ( ) ) ,
172+ // where T: Bar + ?Sized, Foo: Bar,
173+ // ^^^^^^^^^
174+ ( _, pos, _, _) => bounds[ pos - 1 ] . span ( ) . shrink_to_hi ( ) . to ( bound. span ( ) ) ,
175+ } ;
176+ where_unsized_bounds. insert ( bound. span ( ) ) ;
177+ err. span_suggestion_verbose (
178+ sp,
179+ "consider removing the `?Sized` bound to make the \
180+ type parameter `Sized`",
181+ String :: new ( ) ,
182+ Applicability :: MaybeIncorrect ,
183+ ) ;
184+ }
185+ }
186+ _ => { }
187+ }
188+ }
189+ for ( pos, bound) in param. bounds . iter ( ) . enumerate ( ) {
190+ match bound {
191+ hir:: GenericBound :: Trait ( poly, hir:: TraitBoundModifier :: Maybe )
192+ if poly. trait_ref . trait_def_id ( ) == def_id
193+ && !where_unsized_bounds. contains ( & bound. span ( ) ) =>
194+ {
195+ let sp = match ( param. bounds . len ( ) , pos) {
196+ // T: ?Sized,
197+ // ^^^^^^^^
198+ ( 1 , _) => param. span . shrink_to_hi ( ) . to ( bound. span ( ) ) ,
199+ // T: ?Sized + Bar,
200+ // ^^^^^^^^^
201+ ( _, 0 ) => bound. span ( ) . to ( param. bounds [ 1 ] . span ( ) . shrink_to_lo ( ) ) ,
202+ // T: Bar + ?Sized,
203+ // ^^^^^^^^^
204+ ( _, pos) => param. bounds [ pos - 1 ] . span ( ) . shrink_to_hi ( ) . to ( bound. span ( ) ) ,
205+ } ;
206+ err. span_suggestion_verbose (
207+ sp,
208+ "consider removing the `?Sized` bound to make the type parameter \
209+ `Sized`",
210+ String :: new ( ) ,
211+ Applicability :: MaybeIncorrect ,
212+ ) ;
213+ }
214+ _ => { }
215+ }
216+ }
217+ }
218+
108219/// Suggest restricting a type param with a new bound.
109220pub fn suggest_constraining_type_param (
110221 tcx : TyCtxt < ' _ > ,
@@ -130,6 +241,7 @@ pub fn suggest_constraining_type_param(
130241 if def_id == tcx. lang_items ( ) . sized_trait ( ) {
131242 // Type parameters are already `Sized` by default.
132243 err. span_label ( param. span , & format ! ( "this type parameter needs to be `{}`" , constraint) ) ;
244+ suggest_removing_unsized_bound ( generics, err, param_name, param, def_id) ;
133245 return true ;
134246 }
135247 let mut suggest_restrict = |span| {
0 commit comments