@@ -4,7 +4,9 @@ use rustc_hir as hir;
4
4
use rustc_hir:: def:: { DefKind , Res } ;
5
5
use rustc_lint_defs:: { builtin:: BARE_TRAIT_OBJECTS , Applicability } ;
6
6
use 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
+ } ;
8
10
9
11
use super :: HirTyLowerer ;
10
12
@@ -17,6 +19,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
17
19
& self ,
18
20
self_ty : & hir:: Ty < ' _ > ,
19
21
in_path : bool ,
22
+ borrowed : bool ,
20
23
) {
21
24
let tcx = self . tcx ( ) ;
22
25
@@ -62,7 +65,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
62
65
let mut diag =
63
66
rustc_errors:: struct_span_code_err!( self . dcx( ) , self_ty. span, E0782 , "{}" , msg) ;
64
67
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 )
66
69
{
67
70
// FIXME: Only emit this suggestion if the trait is object safe.
68
71
diag. multipart_suggestion_verbose ( label, sugg, Applicability :: MachineApplicable ) ;
@@ -120,9 +123,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
120
123
return ;
121
124
} ;
122
125
let sugg = self . add_generic_param_suggestion ( generics, self_ty. span , & impl_trait_name) ;
123
- if sugg. is_empty ( ) {
124
- return ;
125
- } ;
126
126
diag. multipart_suggestion (
127
127
format ! (
128
128
"alternatively use a blanket implementation to implement `{of_trait_name}` for \
@@ -152,11 +152,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
152
152
}
153
153
154
154
/// 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 {
156
161
let tcx = self . tcx ( ) ;
157
162
let parent_id = tcx. hir ( ) . get_parent_item ( self_ty. hir_id ) . def_id ;
158
163
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
159
164
// 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
160
169
let ( sig, generics, owner) = match tcx. hir_node_by_def_id ( parent_id) {
161
170
hir:: Node :: Item ( hir:: Item { kind : hir:: ItemKind :: Fn ( sig, generics, _) , .. } ) => {
162
171
( sig, generics, None )
@@ -167,13 +176,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
167
176
owner_id,
168
177
..
169
178
} ) => ( 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 ( ) ) ) ) ,
170
185
_ => return false ,
171
186
} ;
172
187
let Ok ( trait_name) = tcx. sess . source_map ( ) . span_to_snippet ( self_ty. span ) else {
173
188
return false ;
174
189
} ;
175
190
let impl_sugg = vec ! [ ( self_ty. span. shrink_to_lo( ) , "impl " . to_string( ) ) ] ;
176
191
let mut is_downgradable = true ;
192
+
193
+ // Check if trait object is safe for suggesting dynamic dispatch.
177
194
let is_object_safe = match self_ty. kind {
178
195
hir:: TyKind :: TraitObject ( objects, ..) => {
179
196
objects. iter ( ) . all ( |o| match o. trait_ref . path . res {
@@ -189,8 +206,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
189
206
}
190
207
_ => false ,
191
208
} ;
209
+
210
+ // Suggestions for function return type.
192
211
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
194
213
{
195
214
let pre = if !is_object_safe {
196
215
format ! ( "`{trait_name}` is not object safe, " )
@@ -201,14 +220,54 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
201
220
"{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
202
221
single underlying type",
203
222
) ;
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. is_ref_with_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
205
255
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 {
208
262
vec ! [
209
263
( ty. span. shrink_to_lo( ) , "Box<dyn " . to_string( ) ) ,
210
264
( 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,
212
271
Applicability :: MachineApplicable ,
213
272
) ;
214
273
} else if is_downgradable {
@@ -217,39 +276,43 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
217
276
}
218
277
return true ;
219
278
}
279
+
280
+ // Suggestions for function parameters.
220
281
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 {
222
283
continue ;
223
284
}
224
285
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
+ ) ;
238
297
if !is_object_safe {
239
298
diag. note ( format ! ( "`{trait_name}` it is not object safe, so it can't be `dyn`" ) ) ;
240
299
if is_downgradable {
241
300
// We'll emit the object safety error already, with a structured suggestion.
242
301
diag. downgrade_to_delayed_bug ( ) ;
243
302
}
244
303
} 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
+
245
308
let sugg = if let hir:: TyKind :: TraitObject ( [ _, _, ..] , _, _) = self_ty. kind {
246
309
// There are more than one trait bound, we need surrounding parentheses.
247
310
vec ! [
248
- ( self_ty. span. shrink_to_lo( ) , "&(dyn " . to_string( ) ) ,
311
+ ( self_ty. span. shrink_to_lo( ) , paren_dyn_str . to_string( ) ) ,
249
312
( self_ty. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
250
313
]
251
314
} else {
252
- vec ! [ ( self_ty. span. shrink_to_lo( ) , "&dyn " . to_string( ) ) ]
315
+ vec ! [ ( self_ty. span. shrink_to_lo( ) , dyn_str . to_string( ) ) ]
253
316
} ;
254
317
diag. multipart_suggestion_verbose (
255
318
format ! (
0 commit comments