@@ -148,6 +148,126 @@ pub trait InferCtxtExt<'tcx> {
148
148
fn suggest_new_overflow_limit ( & self , err : & mut DiagnosticBuilder < ' _ > ) ;
149
149
}
150
150
151
+ fn predicate_constraint ( generics : & hir:: Generics < ' _ > , pred : String ) -> ( Span , String ) {
152
+ (
153
+ generics. where_clause . span_for_predicates_or_empty_place ( ) . shrink_to_hi ( ) ,
154
+ format ! (
155
+ "{} {} " ,
156
+ if !generics. where_clause. predicates. is_empty( ) { "," } else { " where" } ,
157
+ pred,
158
+ ) ,
159
+ )
160
+ }
161
+
162
+ /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
163
+ /// it can also be an `impl Trait` param that needs to be decomposed to a type
164
+ /// param for cleaner code.
165
+ fn suggest_restriction (
166
+ generics : & hir:: Generics < ' _ > ,
167
+ msg : & str ,
168
+ err : & mut DiagnosticBuilder < ' _ > ,
169
+ fn_sig : Option < & hir:: FnSig < ' _ > > ,
170
+ projection : Option < & ty:: ProjectionTy < ' _ > > ,
171
+ trait_ref : & ty:: PolyTraitRef < ' _ > ,
172
+ ) {
173
+ let span = generics. where_clause . span_for_predicates_or_empty_place ( ) ;
174
+ if !span. from_expansion ( ) && span. desugaring_kind ( ) . is_none ( ) {
175
+ // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
176
+ if let Some ( ( name, fn_sig) ) = fn_sig. and_then ( |sig| {
177
+ projection. and_then ( |p| {
178
+ // Shenanigans to get the `Trait` from the `impl Trait`.
179
+ match p. self_ty ( ) . kind {
180
+ ty:: Param ( param) => {
181
+ // `fn foo(t: impl Trait)`
182
+ // ^^^^^ get this string
183
+ param
184
+ . name
185
+ . as_str ( )
186
+ . strip_prefix ( "impl" )
187
+ . map ( |s| ( s. trim_start ( ) . to_string ( ) , sig) )
188
+ }
189
+ _ => None ,
190
+ }
191
+ } )
192
+ } ) {
193
+ // We know we have an `impl Trait` that doesn't satisfy a required projection.
194
+
195
+ // Find all of the ocurrences of `impl Trait` for `Trait` in the function arguments'
196
+ // types. There should be at least one, but there might be *more* than one. In that
197
+ // case we could just ignore it and try to identify which one needs the restriction,
198
+ // but instead we choose to suggest replacing all instances of `impl Trait` with `T`
199
+ // where `T: Trait`.
200
+ let mut ty_spans = vec ! [ ] ;
201
+ let impl_name = format ! ( "impl {}" , name) ;
202
+ for input in fn_sig. decl . inputs {
203
+ if let hir:: TyKind :: Path ( hir:: QPath :: Resolved (
204
+ None ,
205
+ hir:: Path { segments : [ segment] , .. } ,
206
+ ) ) = input. kind
207
+ {
208
+ if segment. ident . as_str ( ) == impl_name. as_str ( ) {
209
+ // `fn foo(t: impl Trait)`
210
+ // ^^^^^^^^^^ get this to suggest
211
+ // `T` instead
212
+
213
+ // There might be more than one `impl Trait`.
214
+ ty_spans. push ( input. span ) ;
215
+ }
216
+ }
217
+ }
218
+
219
+ // The type param `T: Trait` we will suggest to introduce.
220
+ let type_param = format ! ( "{}: {}" , "T" , name) ;
221
+
222
+ // FIXME: modify the `trait_ref` instead of string shenanigans.
223
+ // Turn `<impl Trait as Foo>::Bar: Qux` into `<T as Foo>::Bar: Qux`.
224
+ let pred = trait_ref. without_const ( ) . to_predicate ( ) . to_string ( ) ;
225
+ let pred = pred. replace ( & impl_name, "T" ) ;
226
+ let mut sugg = vec ! [
227
+ match generics
228
+ . params
229
+ . iter( )
230
+ . filter( |p| match p. kind {
231
+ hir:: GenericParamKind :: Type {
232
+ synthetic: Some ( hir:: SyntheticTyParamKind :: ImplTrait ) ,
233
+ ..
234
+ } => false ,
235
+ _ => true ,
236
+ } )
237
+ . last( )
238
+ {
239
+ // `fn foo(t: impl Trait)`
240
+ // ^ suggest `<T: Trait>` here
241
+ None => ( generics. span, format!( "<{}>" , type_param) ) ,
242
+ // `fn foo<A>(t: impl Trait)`
243
+ // ^^^ suggest `<A, T: Trait>` here
244
+ Some ( param) => ( param. span. shrink_to_hi( ) , format!( ", {}" , type_param) ) ,
245
+ } ,
246
+ // `fn foo(t: impl Trait)`
247
+ // ^ suggest `where <T as Trait>::A: Bound`
248
+ predicate_constraint( generics, pred) ,
249
+ ] ;
250
+ sugg. extend ( ty_spans. into_iter ( ) . map ( |s| ( s, "T" . to_string ( ) ) ) ) ;
251
+
252
+ // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.
253
+ err. multipart_suggestion (
254
+ "introduce a type parameter with a trait bound instead of using \
255
+ `impl Trait`",
256
+ sugg,
257
+ Applicability :: MaybeIncorrect ,
258
+ ) ;
259
+ } else {
260
+ // Trivial case: `T` needs an extra bound: `T: Bound`.
261
+ let ( sp, s) = predicate_constraint (
262
+ generics,
263
+ trait_ref. without_const ( ) . to_predicate ( ) . to_string ( ) ,
264
+ ) ;
265
+ let appl = Applicability :: MachineApplicable ;
266
+ err. span_suggestion ( sp, & format ! ( "consider further restricting {}" , msg) , s, appl) ;
267
+ }
268
+ }
269
+ }
270
+
151
271
impl < ' a , ' tcx > InferCtxtExt < ' tcx > for InferCtxt < ' a , ' tcx > {
152
272
fn suggest_restricting_param_bound (
153
273
& self ,
@@ -162,143 +282,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
162
282
_ => return ,
163
283
} ;
164
284
165
- let suggest_restriction =
166
- |generics : & hir:: Generics < ' _ > ,
167
- msg,
168
- err : & mut DiagnosticBuilder < ' _ > ,
169
- fn_sig : Option < & hir:: FnSig < ' _ > > | {
170
- // Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
171
- // it can also be an `impl Trait` param that needs to be decomposed to a type
172
- // param for cleaner code.
173
- let span = generics. where_clause . span_for_predicates_or_empty_place ( ) ;
174
- if !span. from_expansion ( ) && span. desugaring_kind ( ) . is_none ( ) {
175
- // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
176
- if let Some ( ( name, fn_sig) ) = fn_sig. and_then ( |sig| {
177
- projection. and_then ( |p| {
178
- // Shenanigans to get the `Trait` from the `impl Trait`.
179
- match p. self_ty ( ) . kind {
180
- ty:: Param ( param) if param. name . as_str ( ) . starts_with ( "impl " ) => {
181
- let n = param. name . as_str ( ) ;
182
- // `fn foo(t: impl Trait)`
183
- // ^^^^^ get this string
184
- n. split_whitespace ( )
185
- . skip ( 1 )
186
- . next ( )
187
- . map ( |n| ( n. to_string ( ) , sig) )
188
- }
189
- _ => None ,
190
- }
191
- } )
192
- } ) {
193
- // FIXME: Cleanup.
194
- let mut ty_spans = vec ! [ ] ;
195
- let impl_name = format ! ( "impl {}" , name) ;
196
- for i in fn_sig. decl . inputs {
197
- if let hir:: TyKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = i. kind {
198
- match path. segments {
199
- [ segment] if segment. ident . to_string ( ) == impl_name => {
200
- // `fn foo(t: impl Trait)`
201
- // ^^^^^^^^^^ get this to suggest
202
- // `T` instead
203
-
204
- // There might be more than one `impl Trait`.
205
- ty_spans. push ( i. span ) ;
206
- }
207
- _ => { }
208
- }
209
- }
210
- }
211
-
212
- let type_param = format ! ( "{}: {}" , "T" , name) ;
213
- // FIXME: modify the `trait_ref` instead of string shenanigans.
214
- // Turn `<impl Trait as Foo>::Bar: Qux` into `<T as Foo>::Bar: Qux`.
215
- let pred = trait_ref. without_const ( ) . to_predicate ( ) . to_string ( ) ;
216
- let pred = pred. replace ( & impl_name, "T" ) ;
217
- let mut sugg = vec ! [
218
- match generics
219
- . params
220
- . iter( )
221
- . filter( |p| match p. kind {
222
- hir:: GenericParamKind :: Type {
223
- synthetic: Some ( hir:: SyntheticTyParamKind :: ImplTrait ) ,
224
- ..
225
- } => false ,
226
- _ => true ,
227
- } )
228
- . last( )
229
- {
230
- // `fn foo(t: impl Trait)`
231
- // ^ suggest `<T: Trait>` here
232
- None => ( generics. span, format!( "<{}>" , type_param) ) ,
233
- Some ( param) => {
234
- ( param. span. shrink_to_hi( ) , format!( ", {}" , type_param) )
235
- }
236
- } ,
237
- (
238
- // `fn foo(t: impl Trait)`
239
- // ^ suggest `where <T as Trait>::A: Bound`
240
- generics
241
- . where_clause
242
- . span_for_predicates_or_empty_place( )
243
- . shrink_to_hi( ) ,
244
- format!(
245
- "{} {} " ,
246
- if !generics. where_clause. predicates. is_empty( ) {
247
- ","
248
- } else {
249
- " where"
250
- } ,
251
- pred,
252
- ) ,
253
- ) ,
254
- ] ;
255
- sugg. extend ( ty_spans. into_iter ( ) . map ( |s| ( s, "T" . to_string ( ) ) ) ) ;
256
- // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.
257
- err. multipart_suggestion (
258
- "introduce a type parameter with a trait bound instead of using \
259
- `impl Trait`",
260
- sugg,
261
- Applicability :: MaybeIncorrect ,
262
- ) ;
263
- } else {
264
- // Trivial case: `T` needs an extra bound.
265
- err. span_suggestion (
266
- generics
267
- . where_clause
268
- . span_for_predicates_or_empty_place ( )
269
- . shrink_to_hi ( ) ,
270
- & format ! ( "consider further restricting {}" , msg) ,
271
- format ! (
272
- "{} {} " ,
273
- if !generics. where_clause. predicates. is_empty( ) {
274
- ","
275
- } else {
276
- " where"
277
- } ,
278
- trait_ref. without_const( ) . to_predicate( ) ,
279
- ) ,
280
- Applicability :: MachineApplicable ,
281
- ) ;
282
- }
283
- }
284
- } ;
285
-
286
285
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
287
286
// don't suggest `T: Sized + ?Sized`.
288
287
let mut hir_id = body_id;
289
288
while let Some ( node) = self . tcx . hir ( ) . find ( hir_id) {
290
- debug ! (
291
- "suggest_restricting_param_bound {:?} {:?} {:?} {:?}" ,
292
- trait_ref, self_ty. kind, projection, node
293
- ) ;
294
289
match node {
295
290
hir:: Node :: TraitItem ( hir:: TraitItem {
296
291
generics,
297
292
kind : hir:: TraitItemKind :: Fn ( ..) ,
298
293
..
299
294
} ) if param_ty && self_ty == self . tcx . types . self_param => {
300
295
// Restricting `Self` for a single method.
301
- suggest_restriction ( & generics, "`Self`" , err, None ) ;
296
+ suggest_restriction ( & generics, "`Self`" , err, None , projection , trait_ref ) ;
302
297
return ;
303
298
}
304
299
@@ -315,16 +310,30 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
315
310
| hir:: Node :: Item ( hir:: Item {
316
311
kind : hir:: ItemKind :: Fn ( fn_sig, generics, _) , ..
317
312
} ) if projection. is_some ( ) => {
318
- // Missing associated type bound.
319
- suggest_restriction ( & generics, "the associated type" , err, Some ( fn_sig) ) ;
313
+ // Missing restriction on associated type of type parameter (unmet projection).
314
+ suggest_restriction (
315
+ & generics,
316
+ "the associated type" ,
317
+ err,
318
+ Some ( fn_sig) ,
319
+ projection,
320
+ trait_ref,
321
+ ) ;
320
322
return ;
321
323
}
322
324
hir:: Node :: Item (
323
325
hir:: Item { kind : hir:: ItemKind :: Trait ( _, _, generics, _, _) , .. }
324
326
| hir:: Item { kind : hir:: ItemKind :: Impl { generics, .. } , .. } ,
325
327
) if projection. is_some ( ) => {
326
- // Missing associated type bound.
327
- suggest_restriction ( & generics, "the associated type" , err, None ) ;
328
+ // Missing restriction on associated type of type parameter (unmet projection).
329
+ suggest_restriction (
330
+ & generics,
331
+ "the associated type" ,
332
+ err,
333
+ None ,
334
+ projection,
335
+ trait_ref,
336
+ ) ;
328
337
return ;
329
338
}
330
339
0 commit comments