@@ -163,58 +163,168 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
163
163
} ;
164
164
165
165
let suggest_restriction =
166
- |generics : & hir:: Generics < ' _ > , msg, err : & mut DiagnosticBuilder < ' _ > | {
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.
167
173
let span = generics. where_clause . span_for_predicates_or_empty_place ( ) ;
168
174
if !span. from_expansion ( ) && span. desugaring_kind ( ) . is_none ( ) {
169
- err. span_suggestion (
170
- generics. where_clause . span_for_predicates_or_empty_place ( ) . shrink_to_hi ( ) ,
171
- & format ! ( "consider further restricting {}" , msg) ,
172
- format ! (
173
- "{} {} " ,
174
- if !generics. where_clause. predicates. is_empty( ) {
175
- ","
176
- } else {
177
- " where"
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
+ }
178
236
} ,
179
- trait_ref. without_const( ) . to_predicate( ) ,
180
- ) ,
181
- Applicability :: MachineApplicable ,
182
- ) ;
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
+ }
183
283
}
184
284
} ;
185
285
186
286
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
187
287
// don't suggest `T: Sized + ?Sized`.
188
288
let mut hir_id = body_id;
189
289
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
+ ) ;
190
294
match node {
191
295
hir:: Node :: TraitItem ( hir:: TraitItem {
192
296
generics,
193
297
kind : hir:: TraitItemKind :: Fn ( ..) ,
194
298
..
195
299
} ) if param_ty && self_ty == self . tcx . types . self_param => {
196
300
// Restricting `Self` for a single method.
197
- suggest_restriction ( & generics, "`Self`" , err) ;
301
+ suggest_restriction ( & generics, "`Self`" , err, None ) ;
198
302
return ;
199
303
}
200
304
201
305
hir:: Node :: TraitItem ( hir:: TraitItem {
202
306
generics,
203
- kind : hir:: TraitItemKind :: Fn ( ..) ,
307
+ kind : hir:: TraitItemKind :: Fn ( fn_sig , ..) ,
204
308
..
205
309
} )
206
310
| hir:: Node :: ImplItem ( hir:: ImplItem {
207
311
generics,
208
- kind : hir:: ImplItemKind :: Fn ( ..) ,
312
+ kind : hir:: ImplItemKind :: Fn ( fn_sig , ..) ,
209
313
..
210
314
} )
211
- | hir:: Node :: Item (
212
- hir:: Item { kind : hir:: ItemKind :: Fn ( _, generics, _) , .. }
213
- | hir:: Item { kind : hir:: ItemKind :: Trait ( _, _, generics, _, _) , .. }
315
+ | hir:: Node :: Item ( hir:: Item {
316
+ kind : hir:: ItemKind :: Fn ( fn_sig, generics, _) , ..
317
+ } ) if projection. is_some ( ) => {
318
+ // Missing associated type bound.
319
+ suggest_restriction ( & generics, "the associated type" , err, Some ( fn_sig) ) ;
320
+ return ;
321
+ }
322
+ hir:: Node :: Item (
323
+ hir:: Item { kind : hir:: ItemKind :: Trait ( _, _, generics, _, _) , .. }
214
324
| hir:: Item { kind : hir:: ItemKind :: Impl { generics, .. } , .. } ,
215
325
) if projection. is_some ( ) => {
216
326
// Missing associated type bound.
217
- suggest_restriction ( & generics, "the associated type" , err) ;
327
+ suggest_restriction ( & generics, "the associated type" , err, None ) ;
218
328
return ;
219
329
}
220
330
0 commit comments