@@ -4,7 +4,9 @@ use rustc_hir as hir;
44use rustc_hir:: def:: { DefKind , Res } ;
55use rustc_lint_defs:: { builtin:: BARE_TRAIT_OBJECTS , Applicability } ;
66use rustc_span:: Span ;
7- use rustc_trait_selection:: error_reporting:: traits:: suggestions:: NextTypeParamName ;
7+ use rustc_trait_selection:: error_reporting:: traits:: suggestions:: {
8+ NextLifetimeParamName , NextTypeParamName ,
9+ } ;
810
911use super :: HirTyLowerer ;
1012
@@ -17,6 +19,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
1719 & self ,
1820 self_ty : & hir:: Ty < ' _ > ,
1921 in_path : bool ,
22+ borrowed : bool ,
2023 ) {
2124 let tcx = self . tcx ( ) ;
2225
@@ -62,7 +65,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
6265 let mut diag =
6366 rustc_errors:: struct_span_code_err!( self . dcx( ) , self_ty. span, E0782 , "{}" , msg) ;
6467 if self_ty. span . can_be_used_for_suggestions ( )
65- && !self . maybe_suggest_impl_trait ( self_ty, & mut diag)
68+ && !self . maybe_suggest_impl_trait ( self_ty, & mut diag, borrowed )
6669 {
6770 // FIXME: Only emit this suggestion if the trait is object safe.
6871 diag. multipart_suggestion_verbose ( label, sugg, Applicability :: MachineApplicable ) ;
@@ -120,9 +123,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
120123 return ;
121124 } ;
122125 let sugg = self . add_generic_param_suggestion ( generics, self_ty. span , & impl_trait_name) ;
123- if sugg. is_empty ( ) {
124- return ;
125- } ;
126126 diag. multipart_suggestion (
127127 format ! (
128128 "alternatively use a blanket implementation to implement `{of_trait_name}` for \
@@ -152,11 +152,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
152152 }
153153
154154 /// Make sure that we are in the condition to suggest `impl Trait`.
155- fn maybe_suggest_impl_trait ( & self , self_ty : & hir:: Ty < ' _ > , diag : & mut Diag < ' _ > ) -> bool {
155+ fn maybe_suggest_impl_trait (
156+ & self ,
157+ self_ty : & hir:: Ty < ' _ > ,
158+ diag : & mut Diag < ' _ > ,
159+ borrowed : bool ,
160+ ) -> bool {
156161 let tcx = self . tcx ( ) ;
157162 let parent_id = tcx. hir ( ) . get_parent_item ( self_ty. hir_id ) . def_id ;
158163 // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
159164 // and suggest `Trait0<Ty = impl Trait1>`.
165+ // Functions are found in three different contexts.
166+ // 1. Independent functions
167+ // 2. Functions inside trait blocks
168+ // 3. Functions inside impl blocks
160169 let ( sig, generics, owner) = match tcx. hir_node_by_def_id ( parent_id) {
161170 hir:: Node :: Item ( hir:: Item { kind : hir:: ItemKind :: Fn ( sig, generics, _) , .. } ) => {
162171 ( sig, generics, None )
@@ -167,13 +176,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
167176 owner_id,
168177 ..
169178 } ) => ( sig, generics, Some ( tcx. parent ( owner_id. to_def_id ( ) ) ) ) ,
179+ hir:: Node :: ImplItem ( hir:: ImplItem {
180+ kind : hir:: ImplItemKind :: Fn ( sig, _) ,
181+ generics,
182+ owner_id,
183+ ..
184+ } ) => ( sig, generics, Some ( tcx. parent ( owner_id. to_def_id ( ) ) ) ) ,
170185 _ => return false ,
171186 } ;
172187 let Ok ( trait_name) = tcx. sess . source_map ( ) . span_to_snippet ( self_ty. span ) else {
173188 return false ;
174189 } ;
175190 let impl_sugg = vec ! [ ( self_ty. span. shrink_to_lo( ) , "impl " . to_string( ) ) ] ;
176191 let mut is_downgradable = true ;
192+
193+ // Check if trait object is safe for suggesting dynamic dispatch.
177194 let is_object_safe = match self_ty. kind {
178195 hir:: TyKind :: TraitObject ( objects, ..) => {
179196 objects. iter ( ) . all ( |o| match o. trait_ref . path . res {
@@ -189,8 +206,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
189206 }
190207 _ => false ,
191208 } ;
209+
210+ // Suggestions for function return type.
192211 if let hir:: FnRetTy :: Return ( ty) = sig. decl . output
193- && ty. hir_id == self_ty. hir_id
212+ && ty. peel_refs ( ) . hir_id == self_ty. hir_id
194213 {
195214 let pre = if !is_object_safe {
196215 format ! ( "`{trait_name}` is not object safe, " )
@@ -201,14 +220,54 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
201220 "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
202221 single underlying type",
203222 ) ;
204- diag. multipart_suggestion_verbose ( msg, impl_sugg, Applicability :: MachineApplicable ) ;
223+
224+ // Six different cases to consider when suggesting `impl Trait` for a return type:
225+ // 1. `fn fun() -> Trait {}` => Suggest `impl Trait` without mentioning anything about lifetime
226+ // 2. `fn fun<'a>() -> &'a Trait {}` => Suggest `impl Trait` without mentioning anything about lifetime
227+ // 3. `fn fun() -> &'a Trait {}` => Suggest `impl Trait` and an other error (E0261) will suggest to declare the lifetime
228+ // 4. `fn fun() -> &Trait {}` => Suggest to declare and use a fresh lifetime
229+ // 5. `fn fun<'a>() -> &Trait {}` => Suggest to declare and use a fresh lifetime
230+ // 6. `fn fun() -> &mut Trait {}` => Suggest `impl Trait` and mention that returning a mutable reference to a bare trait is impossible
231+ let suggestion = if ty. is_mut_ref ( ) {
232+ // case 6
233+ diag. primary_message ( "cannot return a mutable reference to a bare trait" ) ;
234+ vec ! [ ( ty. span, format!( "impl {trait_name}" ) ) ]
235+ } else if ty. has_anonymous_lifetime ( ) {
236+ // cases 4 and 5
237+ let lifetime = generics. params . next_lifetime_param_name ( None ) ;
238+
239+ let lifetime_decl = if let Some ( span) = generics. span_for_lifetime_suggestion ( ) {
240+ ( span, format ! ( "{lifetime}, " ) )
241+ } else {
242+ ( generics. span , format ! ( "<{lifetime}>" ) )
243+ } ;
244+
245+ let impl_with_lifetime = ( self_ty. span . shrink_to_lo ( ) , format ! ( "{lifetime} impl " ) ) ;
246+ vec ! [ lifetime_decl, impl_with_lifetime]
247+ } else {
248+ // cases 1, 2, and 3
249+ impl_sugg
250+ } ;
251+
252+ diag. multipart_suggestion_verbose ( msg, suggestion, Applicability :: MachineApplicable ) ;
253+
254+ // Suggest `Box<dyn Trait>` for return type
205255 if is_object_safe {
206- diag. multipart_suggestion_verbose (
207- "alternatively, you can return an owned trait object" ,
256+ // If the return type is `&Trait`, we don't want
257+ // the ampersand to be displayed in the `Box<dyn Trait>`
258+ // suggestion.
259+ let suggestion = if borrowed {
260+ vec ! [ ( ty. span, format!( "Box<dyn {trait_name}>" ) ) ]
261+ } else {
208262 vec ! [
209263 ( ty. span. shrink_to_lo( ) , "Box<dyn " . to_string( ) ) ,
210264 ( ty. span. shrink_to_hi( ) , ">" . to_string( ) ) ,
211- ] ,
265+ ]
266+ } ;
267+
268+ diag. multipart_suggestion_verbose (
269+ "alternatively, you can return an owned trait object" ,
270+ suggestion,
212271 Applicability :: MachineApplicable ,
213272 ) ;
214273 } else if is_downgradable {
@@ -217,39 +276,43 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
217276 }
218277 return true ;
219278 }
279+
280+ // Suggestions for function parameters.
220281 for ty in sig. decl . inputs {
221- if ty. hir_id != self_ty. hir_id {
282+ if ty. peel_refs ( ) . hir_id != self_ty. hir_id {
222283 continue ;
223284 }
224285 let sugg = self . add_generic_param_suggestion ( generics, self_ty. span , & trait_name) ;
225- if !sugg. is_empty ( ) {
226- diag. multipart_suggestion_verbose (
227- format ! ( "use a new generic type parameter, constrained by `{trait_name}`" ) ,
228- sugg,
229- Applicability :: MachineApplicable ,
230- ) ;
231- diag. multipart_suggestion_verbose (
232- "you can also use an opaque type, but users won't be able to specify the type \
233- parameter when calling the `fn`, having to rely exclusively on type inference",
234- impl_sugg,
235- Applicability :: MachineApplicable ,
236- ) ;
237- }
286+ diag. multipart_suggestion_verbose (
287+ format ! ( "use a new generic type parameter, constrained by `{trait_name}`" ) ,
288+ sugg,
289+ Applicability :: MachineApplicable ,
290+ ) ;
291+ diag. multipart_suggestion_verbose (
292+ "you can also use an opaque type, but users won't be able to specify the type \
293+ parameter when calling the `fn`, having to rely exclusively on type inference",
294+ impl_sugg,
295+ Applicability :: MachineApplicable ,
296+ ) ;
238297 if !is_object_safe {
239298 diag. note ( format ! ( "`{trait_name}` it is not object safe, so it can't be `dyn`" ) ) ;
240299 if is_downgradable {
241300 // We'll emit the object safety error already, with a structured suggestion.
242301 diag. downgrade_to_delayed_bug ( ) ;
243302 }
244303 } else {
304+ // No ampersand in suggestion if it's borrowed already
305+ let ( dyn_str, paren_dyn_str) =
306+ if borrowed { ( "dyn " , "(dyn " ) } else { ( "&dyn " , "&(dyn " ) } ;
307+
245308 let sugg = if let hir:: TyKind :: TraitObject ( [ _, _, ..] , _, _) = self_ty. kind {
246309 // There are more than one trait bound, we need surrounding parentheses.
247310 vec ! [
248- ( self_ty. span. shrink_to_lo( ) , "&(dyn " . to_string( ) ) ,
311+ ( self_ty. span. shrink_to_lo( ) , paren_dyn_str . to_string( ) ) ,
249312 ( self_ty. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
250313 ]
251314 } else {
252- vec ! [ ( self_ty. span. shrink_to_lo( ) , "&dyn " . to_string( ) ) ]
315+ vec ! [ ( self_ty. span. shrink_to_lo( ) , dyn_str . to_string( ) ) ]
253316 } ;
254317 diag. multipart_suggestion_verbose (
255318 format ! (
0 commit comments