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