@@ -162,58 +162,168 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
162162 } ;
163163
164164 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.
166172 let span = generics. where_clause . span_for_predicates_or_empty_place ( ) ;
167173 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+ }
177235 } ,
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+ }
182282 }
183283 } ;
184284
185285 // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
186286 // don't suggest `T: Sized + ?Sized`.
187287 let mut hir_id = body_id;
188288 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+ ) ;
189293 match node {
190294 hir:: Node :: TraitItem ( hir:: TraitItem {
191295 generics,
192296 kind : hir:: TraitItemKind :: Fn ( ..) ,
193297 ..
194298 } ) if param_ty && self_ty == self . tcx . types . self_param => {
195299 // Restricting `Self` for a single method.
196- suggest_restriction ( & generics, "`Self`" , err) ;
300+ suggest_restriction ( & generics, "`Self`" , err, None ) ;
197301 return ;
198302 }
199303
200304 hir:: Node :: TraitItem ( hir:: TraitItem {
201305 generics,
202- kind : hir:: TraitItemKind :: Fn ( ..) ,
306+ kind : hir:: TraitItemKind :: Fn ( fn_sig , ..) ,
203307 ..
204308 } )
205309 | hir:: Node :: ImplItem ( hir:: ImplItem {
206310 generics,
207- kind : hir:: ImplItemKind :: Fn ( ..) ,
311+ kind : hir:: ImplItemKind :: Fn ( fn_sig , ..) ,
208312 ..
209313 } )
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, _, _) , .. }
213323 | hir:: Item { kind : hir:: ItemKind :: Impl { generics, .. } , .. } ,
214324 ) if projection. is_some ( ) => {
215325 // Missing associated type bound.
216- suggest_restriction ( & generics, "the associated type" , err) ;
326+ suggest_restriction ( & generics, "the associated type" , err, None ) ;
217327 return ;
218328 }
219329
0 commit comments