17
17
use crate :: astconv:: AstConv ;
18
18
use crate :: check:: intrinsic:: intrinsic_operation_unsafety;
19
19
use crate :: errors;
20
+ use hir:: def:: DefKind ;
20
21
use rustc_data_structures:: captures:: Captures ;
21
22
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
22
23
use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder , ErrorGuaranteed , StashKey } ;
23
24
use rustc_hir as hir;
24
25
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
25
26
use rustc_hir:: intravisit:: { self , Visitor } ;
26
27
use rustc_hir:: { GenericParamKind , Node } ;
27
- use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
28
28
use rustc_infer:: infer:: TyCtxtInferExt ;
29
+ use rustc_infer:: traits:: ObligationCause ;
29
30
use rustc_middle:: hir:: nested_filter;
30
31
use rustc_middle:: ty:: query:: Providers ;
31
32
use rustc_middle:: ty:: util:: { Discr , IntTypeExt } ;
@@ -1195,12 +1196,11 @@ fn infer_return_ty_for_fn_sig<'tcx>(
1195
1196
ty:: ReErased => tcx. lifetimes . re_static ,
1196
1197
_ => r,
1197
1198
} ) ;
1198
- let fn_sig = ty:: Binder :: dummy ( fn_sig) ;
1199
1199
1200
1200
let mut visitor = HirPlaceholderCollector :: default ( ) ;
1201
1201
visitor. visit_ty ( ty) ;
1202
1202
let mut diag = bad_placeholder ( tcx, visitor. 0 , "return type" ) ;
1203
- let ret_ty = fn_sig. skip_binder ( ) . output ( ) ;
1203
+ let ret_ty = fn_sig. output ( ) ;
1204
1204
if ret_ty. is_suggestable ( tcx, false ) {
1205
1205
diag. span_suggestion (
1206
1206
ty. span ,
@@ -1223,26 +1223,26 @@ fn infer_return_ty_for_fn_sig<'tcx>(
1223
1223
Applicability :: MachineApplicable ,
1224
1224
) ;
1225
1225
}
1226
+ } else if let Some ( sugg) = suggest_impl_trait ( tcx, ret_ty, ty. span , hir_id, def_id) {
1227
+ diag. span_suggestion (
1228
+ ty. span ,
1229
+ "replace with an appropriate return type" ,
1230
+ sugg,
1231
+ Applicability :: MachineApplicable ,
1232
+ ) ;
1226
1233
} else if ret_ty. is_closure ( ) {
1227
- // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
1228
- // to prevent the user from getting a papercut while trying to use the unique closure
1229
- // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
1230
1234
diag. help ( "consider using an `Fn`, `FnMut`, or `FnOnce` trait bound" ) ;
1235
+ }
1236
+ // Also note how `Fn` traits work just in case!
1237
+ if ret_ty. is_closure ( ) {
1231
1238
diag. note (
1232
1239
"for more information on `Fn` traits and closure types, see \
1233
1240
https://doc.rust-lang.org/book/ch13-01-closures.html",
1234
1241
) ;
1235
- } else if let Some ( i_ty) = suggest_impl_iterator ( tcx, ret_ty, ty. span , hir_id, def_id) {
1236
- diag. span_suggestion (
1237
- ty. span ,
1238
- "replace with an appropriate return type" ,
1239
- format ! ( "impl Iterator<Item = {}>" , i_ty) ,
1240
- Applicability :: MachineApplicable ,
1241
- ) ;
1242
1242
}
1243
1243
diag. emit ( ) ;
1244
1244
1245
- fn_sig
1245
+ ty :: Binder :: dummy ( fn_sig)
1246
1246
}
1247
1247
None => <dyn AstConv < ' _ > >:: ty_of_fn (
1248
1248
icx,
@@ -1256,47 +1256,94 @@ fn infer_return_ty_for_fn_sig<'tcx>(
1256
1256
}
1257
1257
}
1258
1258
1259
- fn suggest_impl_iterator < ' tcx > (
1259
+ fn suggest_impl_trait < ' tcx > (
1260
1260
tcx : TyCtxt < ' tcx > ,
1261
1261
ret_ty : Ty < ' tcx > ,
1262
1262
span : Span ,
1263
1263
hir_id : hir:: HirId ,
1264
1264
def_id : LocalDefId ,
1265
- ) -> Option < Ty < ' tcx > > {
1266
- let Some ( iter_trait) = tcx. get_diagnostic_item ( sym:: Iterator ) else { return None ; } ;
1267
- let Some ( iterator_item) = tcx. get_diagnostic_item ( sym:: IteratorItem ) else { return None ; } ;
1268
- if !tcx
1269
- . infer_ctxt ( )
1270
- . build ( )
1271
- . type_implements_trait ( iter_trait, [ ret_ty] , tcx. param_env ( def_id) )
1272
- . must_apply_modulo_regions ( )
1273
- {
1274
- return None ;
1275
- }
1276
- let infcx = tcx. infer_ctxt ( ) . build ( ) ;
1277
- let ocx = ObligationCtxt :: new_in_snapshot ( & infcx) ;
1278
- // Find the type of `Iterator::Item`.
1279
- let origin = TypeVariableOrigin { kind : TypeVariableOriginKind :: TypeInference , span } ;
1280
- let ty_var = infcx. next_ty_var ( origin) ;
1281
- let projection = ty:: Binder :: dummy ( ty:: PredicateKind :: Clause ( ty:: Clause :: Projection (
1282
- ty:: ProjectionPredicate {
1283
- projection_ty : tcx. mk_alias_ty ( iterator_item, tcx. mk_substs ( [ ret_ty. into ( ) ] . iter ( ) ) ) ,
1284
- term : ty_var. into ( ) ,
1285
- } ,
1286
- ) ) ) ;
1287
- // Add `<ret_ty as Iterator>::Item = _` obligation.
1288
- ocx. register_obligation ( crate :: traits:: Obligation :: misc (
1289
- tcx,
1290
- span,
1291
- hir_id,
1292
- tcx. param_env ( def_id) ,
1293
- projection,
1294
- ) ) ;
1295
- if ocx. select_where_possible ( ) . is_empty ( )
1296
- && let item_ty = infcx. resolve_vars_if_possible ( ty_var)
1297
- && item_ty. is_suggestable ( tcx, false )
1298
- {
1299
- return Some ( item_ty) ;
1265
+ ) -> Option < String > {
1266
+ let format_as_assoc: fn ( _, _, _, _, _) -> _ =
1267
+ |tcx : TyCtxt < ' tcx > ,
1268
+ _: ty:: SubstsRef < ' tcx > ,
1269
+ trait_def_id : DefId ,
1270
+ assoc_item_def_id : DefId ,
1271
+ item_ty : Ty < ' tcx > | {
1272
+ let trait_name = tcx. item_name ( trait_def_id) ;
1273
+ let assoc_name = tcx. item_name ( assoc_item_def_id) ;
1274
+ Some ( format ! ( "impl {trait_name}<{assoc_name} = {item_ty}>" ) )
1275
+ } ;
1276
+ let format_as_parenthesized: fn ( _, _, _, _, _) -> _ =
1277
+ |tcx : TyCtxt < ' tcx > ,
1278
+ substs : ty:: SubstsRef < ' tcx > ,
1279
+ trait_def_id : DefId ,
1280
+ _: DefId ,
1281
+ item_ty : Ty < ' tcx > | {
1282
+ let trait_name = tcx. item_name ( trait_def_id) ;
1283
+ let args_tuple = substs. type_at ( 1 ) ;
1284
+ let ty:: Tuple ( types) = * args_tuple. kind ( ) else { return None ; } ;
1285
+ if !types. is_suggestable ( tcx, false ) {
1286
+ return None ;
1287
+ }
1288
+ let maybe_ret =
1289
+ if item_ty. is_unit ( ) { String :: new ( ) } else { format ! ( " -> {item_ty}" ) } ;
1290
+ Some ( format ! (
1291
+ "impl {trait_name}({}){maybe_ret}" ,
1292
+ types. iter( ) . map( |ty| ty. to_string( ) ) . collect:: <Vec <_>>( ) . join( ", " )
1293
+ ) )
1294
+ } ;
1295
+
1296
+ for ( trait_def_id, assoc_item_def_id, formatter) in [
1297
+ (
1298
+ tcx. get_diagnostic_item ( sym:: Iterator ) ,
1299
+ tcx. get_diagnostic_item ( sym:: IteratorItem ) ,
1300
+ format_as_assoc,
1301
+ ) ,
1302
+ (
1303
+ tcx. lang_items ( ) . future_trait ( ) ,
1304
+ tcx. get_diagnostic_item ( sym:: FutureOutput ) ,
1305
+ format_as_assoc,
1306
+ ) ,
1307
+ ( tcx. lang_items ( ) . fn_trait ( ) , tcx. lang_items ( ) . fn_once_output ( ) , format_as_parenthesized) ,
1308
+ (
1309
+ tcx. lang_items ( ) . fn_mut_trait ( ) ,
1310
+ tcx. lang_items ( ) . fn_once_output ( ) ,
1311
+ format_as_parenthesized,
1312
+ ) ,
1313
+ (
1314
+ tcx. lang_items ( ) . fn_once_trait ( ) ,
1315
+ tcx. lang_items ( ) . fn_once_output ( ) ,
1316
+ format_as_parenthesized,
1317
+ ) ,
1318
+ ] {
1319
+ let Some ( trait_def_id) = trait_def_id else { continue ; } ;
1320
+ let Some ( assoc_item_def_id) = assoc_item_def_id else { continue ; } ;
1321
+ if tcx. def_kind ( assoc_item_def_id) != DefKind :: AssocTy {
1322
+ continue ;
1323
+ }
1324
+ let param_env = tcx. param_env ( def_id) ;
1325
+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
1326
+ let substs = ty:: InternalSubsts :: for_item ( tcx, trait_def_id, |param, _| {
1327
+ if param. index == 0 { ret_ty. into ( ) } else { infcx. var_for_def ( span, param) }
1328
+ } ) ;
1329
+ if !infcx. type_implements_trait ( trait_def_id, substs, param_env) . must_apply_modulo_regions ( )
1330
+ {
1331
+ continue ;
1332
+ }
1333
+ let ocx = ObligationCtxt :: new_in_snapshot ( & infcx) ;
1334
+ let item_ty = ocx. normalize (
1335
+ & ObligationCause :: misc ( span, hir_id) ,
1336
+ param_env,
1337
+ tcx. mk_projection ( assoc_item_def_id, substs) ,
1338
+ ) ;
1339
+ // FIXME(compiler-errors): We may benefit from resolving regions here.
1340
+ if ocx. select_where_possible ( ) . is_empty ( )
1341
+ && let item_ty = infcx. resolve_vars_if_possible ( item_ty)
1342
+ && item_ty. is_suggestable ( tcx, false )
1343
+ && let Some ( sugg) = formatter ( tcx, infcx. resolve_vars_if_possible ( substs) , trait_def_id, assoc_item_def_id, item_ty)
1344
+ {
1345
+ return Some ( sugg) ;
1346
+ }
1300
1347
}
1301
1348
None
1302
1349
}
0 commit comments