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