@@ -1443,23 +1443,101 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1443
1443
/// helper method to determine the span to remove when suggesting the
1444
1444
/// deletion of a lifetime
1445
1445
fn lifetime_deletion_span ( & self , name : ast:: Ident , generics : & hir:: Generics ) -> Option < Span > {
1446
- if generics. params . len ( ) == 1 {
1447
- // if sole lifetime, remove the `<>` brackets
1448
- Some ( generics. span )
1449
- } else {
1450
- generics. params . iter ( ) . enumerate ( ) . find_map ( |( i, param) | {
1451
- if param. name . ident ( ) == name {
1452
- // We also want to delete a leading or trailing comma
1453
- // as appropriate
1454
- if i >= generics. params . len ( ) - 1 {
1455
- Some ( generics. params [ i - 1 ] . span . shrink_to_hi ( ) . to ( param. span ) )
1456
- } else {
1457
- Some ( param. span . to ( generics. params [ i + 1 ] . span . shrink_to_lo ( ) ) )
1446
+ generics. params . iter ( ) . enumerate ( ) . find_map ( |( i, param) | {
1447
+ if param. name . ident ( ) == name {
1448
+ let mut in_band = false ;
1449
+ if let hir:: GenericParamKind :: Lifetime { kind } = param. kind {
1450
+ if let hir:: LifetimeParamKind :: InBand = kind {
1451
+ in_band = true ;
1458
1452
}
1453
+ }
1454
+ if in_band {
1455
+ Some ( param. span )
1459
1456
} else {
1460
- None
1457
+ if generics. params . len ( ) == 1 {
1458
+ // if sole lifetime, remove the entire `<>` brackets
1459
+ Some ( generics. span )
1460
+ } else {
1461
+ // if removing within `<>` brackets, we also want to
1462
+ // delete a leading or trailing comma as appropriate
1463
+ if i >= generics. params . len ( ) - 1 {
1464
+ Some ( generics. params [ i - 1 ] . span . shrink_to_hi ( ) . to ( param. span ) )
1465
+ } else {
1466
+ Some ( param. span . to ( generics. params [ i + 1 ] . span . shrink_to_lo ( ) ) )
1467
+ }
1468
+ }
1461
1469
}
1462
- } )
1470
+ } else {
1471
+ None
1472
+ }
1473
+ } )
1474
+ }
1475
+
1476
+ // helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
1477
+ fn suggest_eliding_single_use_lifetime (
1478
+ & self , err : & mut DiagnosticBuilder < ' _ > , def_id : DefId , lifetime : & hir:: Lifetime
1479
+ ) {
1480
+ // FIXME: future work: also suggest `impl Foo<'_>` for `impl<'a> Foo<'a>`
1481
+ let name = lifetime. name . ident ( ) ;
1482
+ let mut remove_decl = None ;
1483
+ if let Some ( parent_def_id) = self . tcx . parent ( def_id) {
1484
+ if let Some ( generics) = self . tcx . hir . get_generics ( parent_def_id) {
1485
+ remove_decl = self . lifetime_deletion_span ( name, generics) ;
1486
+ }
1487
+ }
1488
+
1489
+ let mut remove_use = None ;
1490
+ let mut find_arg_use_span = |inputs : & hir:: HirVec < hir:: Ty > | {
1491
+ for input in inputs {
1492
+ if let hir:: TyKind :: Rptr ( lt, _) = input. node {
1493
+ if lt. name . ident ( ) == name {
1494
+ // include the trailing whitespace between the ampersand and the type name
1495
+ let lt_through_ty_span = lifetime. span . to ( input. span . shrink_to_hi ( ) ) ;
1496
+ remove_use = Some (
1497
+ self . tcx . sess . source_map ( )
1498
+ . span_until_non_whitespace ( lt_through_ty_span)
1499
+ ) ;
1500
+ break ;
1501
+ }
1502
+ }
1503
+ }
1504
+ } ;
1505
+ if let Node :: Lifetime ( hir_lifetime) = self . tcx . hir . get ( lifetime. id ) {
1506
+ if let Some ( parent) = self . tcx . hir . find ( self . tcx . hir . get_parent ( hir_lifetime. id ) ) {
1507
+ match parent {
1508
+ Node :: Item ( item) => {
1509
+ if let hir:: ItemKind :: Fn ( decl, _, _, _) = & item. node {
1510
+ find_arg_use_span ( & decl. inputs ) ;
1511
+ }
1512
+ } ,
1513
+ Node :: ImplItem ( impl_item) => {
1514
+ if let hir:: ImplItemKind :: Method ( sig, _) = & impl_item. node {
1515
+ find_arg_use_span ( & sig. decl . inputs ) ;
1516
+ }
1517
+ }
1518
+ _ => { }
1519
+ }
1520
+ }
1521
+ }
1522
+
1523
+ if let ( Some ( decl_span) , Some ( use_span) ) = ( remove_decl, remove_use) {
1524
+ // if both declaration and use deletion spans start at the same
1525
+ // place ("start at" because the latter includes trailing
1526
+ // whitespace), then this is an in-band lifetime
1527
+ if decl_span. shrink_to_lo ( ) == use_span. shrink_to_lo ( ) {
1528
+ err. span_suggestion_with_applicability (
1529
+ use_span,
1530
+ "elide the single-use lifetime" ,
1531
+ String :: new ( ) ,
1532
+ Applicability :: MachineApplicable ,
1533
+ ) ;
1534
+ } else {
1535
+ err. multipart_suggestion_with_applicability (
1536
+ "elide the single-use lifetime" ,
1537
+ vec ! [ ( decl_span, String :: new( ) ) , ( use_span, String :: new( ) ) ] ,
1538
+ Applicability :: MachineApplicable ,
1539
+ ) ;
1540
+ }
1463
1541
}
1464
1542
}
1465
1543
@@ -1521,8 +1599,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1521
1599
span,
1522
1600
& format ! ( "lifetime parameter `{}` only used once" , name) ,
1523
1601
) ;
1524
- err. span_label ( span, "this lifetime..." ) ;
1525
- err. span_label ( lifetime. span , "...is used only here" ) ;
1602
+
1603
+ if span == lifetime. span {
1604
+ // spans are the same for in-band lifetime declarations
1605
+ err. span_label ( span, "this lifetime is only used here" ) ;
1606
+ } else {
1607
+ err. span_label ( span, "this lifetime..." ) ;
1608
+ err. span_label ( lifetime. span , "...is used only here" ) ;
1609
+ }
1610
+ self . suggest_eliding_single_use_lifetime ( & mut err, def_id, lifetime) ;
1526
1611
err. emit ( ) ;
1527
1612
}
1528
1613
}
@@ -1555,7 +1640,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1555
1640
if let Some ( span) = unused_lt_span {
1556
1641
err. span_suggestion_with_applicability (
1557
1642
span,
1558
- "remove it " ,
1643
+ "elide the unused lifetime " ,
1559
1644
String :: new ( ) ,
1560
1645
Applicability :: MachineApplicable ,
1561
1646
) ;
0 commit comments