@@ -17,6 +17,7 @@ use rustc::mir::{
17
17
LocalDecl , LocalKind , Location , Operand , Place , ProjectionElem , Rvalue , Statement ,
18
18
StatementKind , VarBindingForm ,
19
19
} ;
20
+ use rustc:: hir;
20
21
use rustc:: hir:: def_id:: DefId ;
21
22
use rustc:: ty;
22
23
use rustc_data_structures:: fx:: FxHashSet ;
@@ -524,10 +525,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
524
525
err. span_label (
525
526
drop_span,
526
527
format ! (
527
- "...but `{}` is only valid for the duration of the `{}` function, so it \
528
- is dropped here while still borrowed",
529
- name,
530
- self . infcx. tcx. hir. name( fn_node_id) ,
528
+ "but `{}` will be dropped here, when the function `{}` returns" ,
529
+ name, self . infcx. tcx. hir. name( fn_node_id) ,
531
530
)
532
531
) ;
533
532
@@ -1235,67 +1234,137 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1235
1234
let fn_node_id = self . infcx . tcx . hir . as_local_node_id ( did) ?;
1236
1235
let fn_decl = self . infcx . tcx . hir . fn_decl ( fn_node_id) ?;
1237
1236
1238
- // If there is one argument and this error is being reported, that means
1239
- // that the lifetime of the borrow could not be made to match the single
1240
- // argument's lifetime. We can highlight it.
1237
+ // We need to work out which arguments to highlight. We do this by looking
1238
+ // at the return type, where there are three cases:
1241
1239
//
1242
- // If there is more than one argument and this error is being reported, that
1243
- // means there must be a self parameter - as otherwise there would be an error
1244
- // from lifetime elision and not this. So we highlight the self parameter.
1245
- let argument_span = fn_decl. inputs . first ( ) ?. span ;
1246
- let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1247
- if is_closure {
1248
- // Closure arguments are wrapped in a tuple, so we need to get the first
1249
- // from that.
1250
- let argument_ty = if let ty:: TyKind :: Tuple ( elems) = argument_ty. sty {
1251
- let argument_ty = elems. first ( ) ?;
1252
- if let ty:: TyKind :: Ref ( _, _, _) = argument_ty. sty {
1253
- argument_ty
1254
- } else {
1240
+ // 1. If there are named arguments, then we should highlight the return type and
1241
+ // highlight any of the arguments that are also references with that lifetime.
1242
+ // If there are no arguments that have the same lifetime as the return type,
1243
+ // then don't highlight anything.
1244
+ // 2. The return type is a reference with an anonymous lifetime. If this is
1245
+ // the case, then we can take advantage of (and teach) the lifetime elision
1246
+ // rules.
1247
+ //
1248
+ // We know that an error is being reported. So the arguments and return type
1249
+ // must satisfy the elision rules. Therefore, if there is a single argument
1250
+ // then that means the return type and first (and only) argument have the same
1251
+ // lifetime and the borrow isn't meeting that, we can highlight the argument
1252
+ // and return type.
1253
+ //
1254
+ // If there are multiple arguments then the first argument must be self (else
1255
+ // it would not satisfy the elision rules), so we can highlight self and the
1256
+ // return type.
1257
+ // 3. The return type is not a reference. In this case, we don't highlight
1258
+ // anything.
1259
+ let return_ty = sig. output ( ) ;
1260
+ match return_ty. skip_binder ( ) . sty {
1261
+ ty:: TyKind :: Ref ( return_region, _, _) if return_region. has_name ( ) && !is_closure => {
1262
+ // This is case 1 from above, return type is a named reference so we need to
1263
+ // search for relevant arguments.
1264
+ let mut arguments = Vec :: new ( ) ;
1265
+ for ( index, argument) in sig. inputs ( ) . skip_binder ( ) . iter ( ) . enumerate ( ) {
1266
+ if let ty:: TyKind :: Ref ( argument_region, _, _) = argument. sty {
1267
+ if argument_region == return_region {
1268
+ // Need to use the `rustc::ty` types to compare against the
1269
+ // `return_region`. Then use the `rustc::hir` type to get only
1270
+ // the lifetime span.
1271
+ match & fn_decl. inputs [ index] . node {
1272
+ hir:: TyKind :: Rptr ( lifetime, _) => {
1273
+ // With access to the lifetime, we can get
1274
+ // the span of it.
1275
+ arguments. push ( ( * argument, lifetime. span ) ) ;
1276
+ } ,
1277
+ _ => bug ! ( "ty type is a ref but hir type is not" ) ,
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ // We need to have arguments. This shouldn't happen, but it's worth checking.
1284
+ if arguments. is_empty ( ) {
1255
1285
return None ;
1256
1286
}
1257
- } else {
1258
- return None
1259
- } ;
1260
1287
1261
- Some ( AnnotatedBorrowFnSignature :: Closure {
1262
- argument_ty,
1263
- argument_span,
1264
- } )
1265
- } else if let ty:: TyKind :: Ref ( argument_region, _, _) = argument_ty. sty {
1266
- // We only consider the return type for functions.
1267
- let return_span = fn_decl. output . span ( ) ;
1268
-
1269
- let return_ty = sig. output ( ) ;
1270
- let ( return_region, return_ty) = if let ty:: TyKind :: Ref (
1271
- return_region, _, _
1272
- ) = return_ty. skip_binder ( ) . sty {
1273
- ( return_region, * return_ty. skip_binder ( ) )
1274
- } else {
1275
- return None ;
1276
- } ;
1288
+ // We use a mix of the HIR and the Ty types to get information
1289
+ // as the HIR doesn't have full types for closure arguments.
1290
+ let return_ty = * sig. output ( ) . skip_binder ( ) ;
1291
+ let mut return_span = fn_decl. output . span ( ) ;
1292
+ if let hir:: FunctionRetTy :: Return ( ty) = fn_decl. output {
1293
+ if let hir:: TyKind :: Rptr ( lifetime, _) = ty. into_inner ( ) . node {
1294
+ return_span = lifetime. span ;
1295
+ }
1296
+ }
1277
1297
1278
- Some ( AnnotatedBorrowFnSignature :: Function {
1279
- argument_ty,
1280
- argument_span,
1281
- return_ty,
1282
- return_span,
1283
- regions_equal : return_region == argument_region,
1284
- } )
1285
- } else {
1286
- None
1298
+ Some ( AnnotatedBorrowFnSignature :: NamedFunction {
1299
+ arguments,
1300
+ return_ty,
1301
+ return_span,
1302
+ } )
1303
+ } ,
1304
+ ty:: TyKind :: Ref ( _, _, _) if is_closure => {
1305
+ // This is case 2 from above but only for closures, return type is anonymous
1306
+ // reference so we select
1307
+ // the first argument.
1308
+ let argument_span = fn_decl. inputs . first ( ) ?. span ;
1309
+ let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1310
+
1311
+ // Closure arguments are wrapped in a tuple, so we need to get the first
1312
+ // from that.
1313
+ if let ty:: TyKind :: Tuple ( elems) = argument_ty. sty {
1314
+ let argument_ty = elems. first ( ) ?;
1315
+ if let ty:: TyKind :: Ref ( _, _, _) = argument_ty. sty {
1316
+ return Some ( AnnotatedBorrowFnSignature :: Closure {
1317
+ argument_ty,
1318
+ argument_span,
1319
+ } ) ;
1320
+ }
1321
+ }
1322
+
1323
+ None
1324
+ } ,
1325
+ ty:: TyKind :: Ref ( _, _, _) => {
1326
+ // This is also case 2 from above but for functions, return type is still an
1327
+ // anonymous reference so we select the first argument.
1328
+ let argument_span = fn_decl. inputs . first ( ) ?. span ;
1329
+ let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1330
+
1331
+ let return_span = fn_decl. output . span ( ) ;
1332
+ let return_ty = * sig. output ( ) . skip_binder ( ) ;
1333
+
1334
+ // We expect the first argument to be a reference.
1335
+ match argument_ty. sty {
1336
+ ty:: TyKind :: Ref ( _, _, _) => { } ,
1337
+ _ => return None ,
1338
+ }
1339
+
1340
+ Some ( AnnotatedBorrowFnSignature :: AnonymousFunction {
1341
+ argument_ty,
1342
+ argument_span,
1343
+ return_ty,
1344
+ return_span,
1345
+ } )
1346
+ } ,
1347
+ _ => {
1348
+ // This is case 3 from above, return type is not a reference so don't highlight
1349
+ // anything.
1350
+ None
1351
+ } ,
1287
1352
}
1288
1353
}
1289
1354
}
1290
1355
1291
1356
#[ derive( Debug ) ]
1292
1357
enum AnnotatedBorrowFnSignature < ' tcx > {
1293
- Function {
1358
+ NamedFunction {
1359
+ arguments : Vec < ( ty:: Ty < ' tcx > , Span ) > ,
1360
+ return_ty : ty:: Ty < ' tcx > ,
1361
+ return_span : Span ,
1362
+ } ,
1363
+ AnonymousFunction {
1294
1364
argument_ty : ty:: Ty < ' tcx > ,
1295
1365
argument_span : Span ,
1296
1366
return_ty : ty:: Ty < ' tcx > ,
1297
1367
return_span : Span ,
1298
- regions_equal : bool ,
1299
1368
} ,
1300
1369
Closure {
1301
1370
argument_ty : ty:: Ty < ' tcx > ,
@@ -1304,57 +1373,86 @@ enum AnnotatedBorrowFnSignature<'tcx> {
1304
1373
}
1305
1374
1306
1375
impl < ' tcx > AnnotatedBorrowFnSignature < ' tcx > {
1376
+ /// Annotate the provided diagnostic with information about borrow from the fn signature that
1377
+ /// helps explain.
1307
1378
fn emit (
1308
1379
& self ,
1309
1380
diag : & mut DiagnosticBuilder < ' _ >
1310
1381
) -> String {
1311
- let ( argument_ty, argument_span) = match self {
1312
- AnnotatedBorrowFnSignature :: Function {
1313
- argument_ty,
1314
- argument_span,
1315
- ..
1316
- } => ( argument_ty, argument_span) ,
1317
- AnnotatedBorrowFnSignature :: Closure {
1382
+ match self {
1383
+ AnnotatedBorrowFnSignature :: Closure { argument_ty, argument_span } => {
1384
+ diag. span_label (
1385
+ * argument_span,
1386
+ format ! ( "has type `{}`" , self . get_name_for_ty( argument_ty, 0 ) ) ,
1387
+ ) ;
1388
+
1389
+ self . get_region_name_for_ty ( argument_ty, 0 )
1390
+ } ,
1391
+ AnnotatedBorrowFnSignature :: AnonymousFunction {
1318
1392
argument_ty,
1319
1393
argument_span,
1320
- } => ( argument_ty, argument_span) ,
1321
- } ;
1394
+ return_ty,
1395
+ return_span,
1396
+ } => {
1397
+ let argument_ty_name = self . get_name_for_ty ( argument_ty, 0 ) ;
1398
+ diag. span_label (
1399
+ * argument_span,
1400
+ format ! ( "has type `{}`" , argument_ty_name)
1401
+ ) ;
1322
1402
1323
- let ( argument_region_name, argument_ty_name) = (
1324
- self . get_region_name_for_ty ( argument_ty, 0 ) ,
1325
- self . get_name_for_ty ( argument_ty, 0 ) ,
1326
- ) ;
1327
- diag. span_label (
1328
- * argument_span,
1329
- format ! ( "has type `{}`" , argument_ty_name)
1330
- ) ;
1403
+ let return_ty_name = self . get_name_for_ty ( return_ty, 0 ) ;
1404
+ let types_equal = return_ty_name == argument_ty_name;
1405
+ diag. span_label (
1406
+ * return_span,
1407
+ format ! (
1408
+ "{}has type `{}`" ,
1409
+ if types_equal { "also " } else { "" } ,
1410
+ return_ty_name,
1411
+ )
1412
+ ) ;
1331
1413
1332
- // Only emit labels for the return value when we're annotating a function.
1333
- if let AnnotatedBorrowFnSignature :: Function {
1334
- return_ty,
1335
- return_span,
1336
- regions_equal,
1337
- ..
1338
- } = self {
1339
- let counter = if * regions_equal { 0 } else { 1 } ;
1340
- let ( return_region_name, return_ty_name) = (
1341
- self . get_region_name_for_ty ( return_ty, counter) ,
1342
- self . get_name_for_ty ( return_ty, counter)
1343
- ) ;
1414
+ diag. note (
1415
+ "argument and return type have the same lifetime due to lifetime elision rules" ,
1416
+ ) ;
1417
+ diag. note (
1418
+ "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-\
1419
+ lifetime-syntax.html#lifetime-elision>",
1420
+ ) ;
1344
1421
1345
- let types_equal = return_ty_name == argument_ty_name;
1346
- diag. span_label (
1347
- * return_span,
1348
- format ! (
1349
- "{}has type `{}`" ,
1350
- if types_equal { "also " } else { "" } ,
1351
- return_ty_name,
1352
- )
1353
- ) ;
1422
+ self . get_region_name_for_ty ( return_ty, 0 )
1423
+ } ,
1424
+ AnnotatedBorrowFnSignature :: NamedFunction {
1425
+ arguments,
1426
+ return_ty,
1427
+ return_span,
1428
+ } => {
1429
+ // Region of return type and arguments checked to be the same earlier.
1430
+ let region_name = self . get_region_name_for_ty ( return_ty, 0 ) ;
1431
+ for ( _, argument_span) in arguments {
1432
+ diag. span_label (
1433
+ * argument_span,
1434
+ format ! ( "has lifetime `{}`" , region_name)
1435
+ ) ;
1436
+ }
1354
1437
1355
- return_region_name
1356
- } else {
1357
- argument_region_name
1438
+ diag. span_label (
1439
+ * return_span,
1440
+ format ! (
1441
+ "also has lifetime `{}`" ,
1442
+ region_name,
1443
+ )
1444
+ ) ;
1445
+
1446
+ diag. help (
1447
+ & format ! (
1448
+ "use data from the highlighted arguments which match the `{}` lifetime of \
1449
+ the return type",
1450
+ region_name,
1451
+ ) ,
1452
+ ) ;
1453
+
1454
+ region_name
1455
+ } ,
1358
1456
}
1359
1457
}
1360
1458
0 commit comments