@@ -1129,8 +1129,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1129
1129
}
1130
1130
1131
1131
let hir = self . tcx . hir ( ) ;
1132
- let fn_hir_id = hir. get_parent_node ( obligation. cause . body_id ) ;
1133
- let node = hir. find ( fn_hir_id ) ;
1132
+ let parent_node = hir. get_parent_node ( obligation. cause . body_id ) ;
1133
+ let node = hir. find ( parent_node ) ;
1134
1134
let Some ( hir:: Node :: Item ( hir:: Item {
1135
1135
kind : hir:: ItemKind :: Fn ( sig, _, body_id) ,
1136
1136
..
@@ -1168,17 +1168,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1168
1168
visitor. visit_body ( & body) ;
1169
1169
1170
1170
let typeck_results = self . in_progress_typeck_results . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
1171
- let Some ( liberated_sig) = typeck_results. liberated_fn_sigs ( ) . get ( fn_hir_id) else { return false ; } ;
1172
1171
1173
- let ret_types = visitor
1172
+ let mut ret_types = visitor
1174
1173
. returns
1175
1174
. iter ( )
1176
- . filter_map ( |expr| Some ( ( expr . span , typeck_results. node_type_opt ( expr. hir_id ) ? ) ) )
1177
- . map ( |( expr_span , ty ) | ( expr_span , self . resolve_vars_if_possible ( ty) ) ) ;
1175
+ . filter_map ( |expr| typeck_results. node_type_opt ( expr. hir_id ) )
1176
+ . map ( |ty| self . resolve_vars_if_possible ( ty) ) ;
1178
1177
let ( last_ty, all_returns_have_same_type, only_never_return) = ret_types. clone ( ) . fold (
1179
1178
( None , true , true ) ,
1180
1179
|( last_ty, mut same, only_never_return) : ( std:: option:: Option < Ty < ' _ > > , bool , bool ) ,
1181
- ( _ , ty ) | {
1180
+ ty | {
1182
1181
let ty = self . resolve_vars_if_possible ( ty) ;
1183
1182
same &=
1184
1183
!matches ! ( ty. kind( ) , ty:: Error ( _) )
@@ -1199,60 +1198,39 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1199
1198
( Some ( ty) , same, only_never_return && matches ! ( ty. kind( ) , ty:: Never ) )
1200
1199
} ,
1201
1200
) ;
1202
- let mut spans_and_needs_box = vec ! [ ] ;
1203
-
1204
- match liberated_sig. output ( ) . kind ( ) {
1205
- ty:: Dynamic ( predicates, _) => {
1206
- let cause = ObligationCause :: misc ( ret_ty. span , fn_hir_id) ;
1207
- let param_env = ty:: ParamEnv :: empty ( ) ;
1208
-
1209
- if !only_never_return {
1210
- for ( expr_span, return_ty) in ret_types {
1211
- let self_ty_satisfies_dyn_predicates = |self_ty| {
1212
- predicates. iter ( ) . all ( |predicate| {
1213
- let pred = predicate. with_self_ty ( self . tcx , self_ty) ;
1214
- let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
1215
- self . predicate_may_hold ( & obl)
1201
+ let all_returns_conform_to_trait =
1202
+ if let Some ( ty_ret_ty) = typeck_results. node_type_opt ( ret_ty. hir_id ) {
1203
+ match ty_ret_ty. kind ( ) {
1204
+ ty:: Dynamic ( predicates, _) => {
1205
+ let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
1206
+ let param_env = ty:: ParamEnv :: empty ( ) ;
1207
+ only_never_return
1208
+ || ret_types. all ( |returned_ty| {
1209
+ predicates. iter ( ) . all ( |predicate| {
1210
+ let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
1211
+ let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
1212
+ self . predicate_may_hold ( & obl)
1213
+ } )
1216
1214
} )
1217
- } ;
1218
-
1219
- if let ty:: Adt ( def, substs) = return_ty. kind ( )
1220
- && def. is_box ( )
1221
- && self_ty_satisfies_dyn_predicates ( substs. type_at ( 0 ) )
1222
- {
1223
- spans_and_needs_box. push ( ( expr_span, false ) ) ;
1224
- } else if self_ty_satisfies_dyn_predicates ( return_ty) {
1225
- spans_and_needs_box. push ( ( expr_span, true ) ) ;
1226
- } else {
1227
- return false ;
1228
- }
1229
1215
}
1216
+ _ => false ,
1230
1217
}
1231
- }
1232
- _ => return false ,
1233
- } ;
1218
+ } else {
1219
+ true
1220
+ } ;
1234
1221
1235
1222
let sm = self . tcx . sess . source_map ( ) ;
1236
- if !ret_ty. span . overlaps ( span) {
1223
+ let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true ) = (
1224
+ // Verify that we're dealing with a return `dyn Trait`
1225
+ ret_ty. span . overlaps ( span) ,
1226
+ & ret_ty. kind ,
1227
+ sm. span_to_snippet ( ret_ty. span ) ,
1228
+ // If any of the return types does not conform to the trait, then we can't
1229
+ // suggest `impl Trait` nor trait objects: it is a type mismatch error.
1230
+ all_returns_conform_to_trait,
1231
+ ) else {
1237
1232
return false ;
1238
- }
1239
- let snippet = if let hir:: TyKind :: TraitObject ( ..) = ret_ty. kind {
1240
- if let Ok ( snippet) = sm. span_to_snippet ( ret_ty. span ) {
1241
- snippet
1242
- } else {
1243
- return false ;
1244
- }
1245
- } else {
1246
- // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
1247
- let name = liberated_sig. output ( ) . to_string ( ) ;
1248
- let name =
1249
- name. strip_prefix ( '(' ) . and_then ( |name| name. strip_suffix ( ')' ) ) . unwrap_or ( & name) ;
1250
- if !name. starts_with ( "dyn " ) {
1251
- return false ;
1252
- }
1253
- name. to_owned ( )
1254
1233
} ;
1255
-
1256
1234
err. code ( error_code ! ( E0746 ) ) ;
1257
1235
err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
1258
1236
err. children . clear ( ) ;
@@ -1262,7 +1240,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1262
1240
let trait_obj_msg = "for information on trait objects, see \
1263
1241
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
1264
1242
#using-trait-objects-that-allow-for-values-of-different-types>";
1265
-
1266
1243
let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
1267
1244
let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet } ;
1268
1245
if only_never_return {
@@ -1290,25 +1267,26 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1290
1267
} else {
1291
1268
if is_object_safe {
1292
1269
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
1293
- err. multipart_suggestion (
1294
- "return a boxed trait object instead" ,
1295
- vec ! [
1296
- ( ret_ty. span. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
1297
- ( span. shrink_to_hi( ) , ">" . to_string( ) ) ,
1298
- ] ,
1299
- Applicability :: MaybeIncorrect ,
1300
- ) ;
1301
- for ( span, needs_box) in spans_and_needs_box {
1302
- if needs_box {
1303
- err. multipart_suggestion (
1304
- "... and box this value" ,
1305
- vec ! [
1306
- ( span. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
1307
- ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1308
- ] ,
1309
- Applicability :: MaybeIncorrect ,
1310
- ) ;
1311
- }
1270
+ // Get all the return values and collect their span and suggestion.
1271
+ let mut suggestions: Vec < _ > = visitor
1272
+ . returns
1273
+ . iter ( )
1274
+ . flat_map ( |expr| {
1275
+ [
1276
+ ( expr. span . shrink_to_lo ( ) , "Box::new(" . to_string ( ) ) ,
1277
+ ( expr. span . shrink_to_hi ( ) , ")" . to_string ( ) ) ,
1278
+ ]
1279
+ . into_iter ( )
1280
+ } )
1281
+ . collect ( ) ;
1282
+ if !suggestions. is_empty ( ) {
1283
+ // Add the suggestion for the return type.
1284
+ suggestions. push ( ( ret_ty. span , format ! ( "Box<dyn {}>" , trait_obj) ) ) ;
1285
+ err. multipart_suggestion (
1286
+ "return a boxed trait object instead" ,
1287
+ suggestions,
1288
+ Applicability :: MaybeIncorrect ,
1289
+ ) ;
1312
1290
}
1313
1291
} else {
1314
1292
// This is currently not possible to trigger because E0038 takes precedence, but
@@ -2845,15 +2823,13 @@ fn suggest_trait_object_return_type_alternatives(
2845
2823
Applicability :: MaybeIncorrect ,
2846
2824
) ;
2847
2825
if is_object_safe {
2848
- err. multipart_suggestion (
2826
+ err. span_suggestion (
2827
+ ret_ty,
2849
2828
& format ! (
2850
2829
"use a boxed trait object if all return paths implement trait `{}`" ,
2851
2830
trait_obj,
2852
2831
) ,
2853
- vec ! [
2854
- ( ret_ty. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
2855
- ( ret_ty. shrink_to_hi( ) , ">" . to_string( ) ) ,
2856
- ] ,
2832
+ format ! ( "Box<dyn {}>" , trait_obj) ,
2857
2833
Applicability :: MaybeIncorrect ,
2858
2834
) ;
2859
2835
}
0 commit comments