@@ -163,58 +163,168 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
163163 } ;
164164
165165 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.
167173 let span = generics. where_clause . span_for_predicates_or_empty_place ( ) ;
168174 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+ }
178236 } ,
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+ }
183283 }
184284 } ;
185285
186286 // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
187287 // don't suggest `T: Sized + ?Sized`.
188288 let mut hir_id = body_id;
189289 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+ ) ;
190294 match node {
191295 hir:: Node :: TraitItem ( hir:: TraitItem {
192296 generics,
193297 kind : hir:: TraitItemKind :: Fn ( ..) ,
194298 ..
195299 } ) if param_ty && self_ty == self . tcx . types . self_param => {
196300 // Restricting `Self` for a single method.
197- suggest_restriction ( & generics, "`Self`" , err) ;
301+ suggest_restriction ( & generics, "`Self`" , err, None ) ;
198302 return ;
199303 }
200304
201305 hir:: Node :: TraitItem ( hir:: TraitItem {
202306 generics,
203- kind : hir:: TraitItemKind :: Fn ( ..) ,
307+ kind : hir:: TraitItemKind :: Fn ( fn_sig , ..) ,
204308 ..
205309 } )
206310 | hir:: Node :: ImplItem ( hir:: ImplItem {
207311 generics,
208- kind : hir:: ImplItemKind :: Fn ( ..) ,
312+ kind : hir:: ImplItemKind :: Fn ( fn_sig , ..) ,
209313 ..
210314 } )
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, _, _) , .. }
214324 | hir:: Item { kind : hir:: ItemKind :: Impl { generics, .. } , .. } ,
215325 ) if projection. is_some ( ) => {
216326 // Missing associated type bound.
217- suggest_restriction ( & generics, "the associated type" , err) ;
327+ suggest_restriction ( & generics, "the associated type" , err, None ) ;
218328 return ;
219329 }
220330
0 commit comments