@@ -1454,23 +1454,101 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1454
1454
/// helper method to determine the span to remove when suggesting the
1455
1455
/// deletion of a lifetime
1456
1456
fn lifetime_deletion_span ( & self , name : ast:: Ident , generics : & hir:: Generics ) -> Option < Span > {
1457
- if generics. params . len ( ) == 1 {
1458
- // if sole lifetime, remove the `<>` brackets
1459
- Some ( generics. span )
1460
- } else {
1461
- generics. params . iter ( ) . enumerate ( ) . find_map ( |( i, param) | {
1462
- if param. name . ident ( ) == name {
1463
- // We also want to delete a leading or trailing comma
1464
- // as appropriate
1465
- if i >= generics. params . len ( ) - 1 {
1466
- Some ( generics. params [ i - 1 ] . span . shrink_to_hi ( ) . to ( param. span ) )
1467
- } else {
1468
- Some ( param. span . to ( generics. params [ i + 1 ] . span . shrink_to_lo ( ) ) )
1457
+ generics. params . iter ( ) . enumerate ( ) . find_map ( |( i, param) | {
1458
+ if param. name . ident ( ) == name {
1459
+ let mut in_band = false ;
1460
+ if let hir:: GenericParamKind :: Lifetime { kind } = param. kind {
1461
+ if let hir:: LifetimeParamKind :: InBand = kind {
1462
+ in_band = true ;
1469
1463
}
1464
+ }
1465
+ if in_band {
1466
+ Some ( param. span )
1470
1467
} else {
1471
- None
1468
+ if generics. params . len ( ) == 1 {
1469
+ // if sole lifetime, remove the entire `<>` brackets
1470
+ Some ( generics. span )
1471
+ } else {
1472
+ // if removing within `<>` brackets, we also want to
1473
+ // delete a leading or trailing comma as appropriate
1474
+ if i >= generics. params . len ( ) - 1 {
1475
+ Some ( generics. params [ i - 1 ] . span . shrink_to_hi ( ) . to ( param. span ) )
1476
+ } else {
1477
+ Some ( param. span . to ( generics. params [ i + 1 ] . span . shrink_to_lo ( ) ) )
1478
+ }
1479
+ }
1472
1480
}
1473
- } )
1481
+ } else {
1482
+ None
1483
+ }
1484
+ } )
1485
+ }
1486
+
1487
+ // helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
1488
+ fn suggest_eliding_single_use_lifetime (
1489
+ & self , err : & mut DiagnosticBuilder < ' _ > , def_id : DefId , lifetime : & hir:: Lifetime
1490
+ ) {
1491
+ // FIXME: future work: also suggest `impl Foo<'_>` for `impl<'a> Foo<'a>`
1492
+ let name = lifetime. name . ident ( ) ;
1493
+ let mut remove_decl = None ;
1494
+ if let Some ( parent_def_id) = self . tcx . parent ( def_id) {
1495
+ if let Some ( generics) = self . tcx . hir . get_generics ( parent_def_id) {
1496
+ remove_decl = self . lifetime_deletion_span ( name, generics) ;
1497
+ }
1498
+ }
1499
+
1500
+ let mut remove_use = None ;
1501
+ let mut find_arg_use_span = |inputs : & hir:: HirVec < hir:: Ty > | {
1502
+ for input in inputs {
1503
+ if let hir:: TyKind :: Rptr ( lt, _) = input. node {
1504
+ if lt. name . ident ( ) == name {
1505
+ // include the trailing whitespace between the ampersand and the type name
1506
+ let lt_through_ty_span = lifetime. span . to ( input. span . shrink_to_hi ( ) ) ;
1507
+ remove_use = Some (
1508
+ self . tcx . sess . source_map ( )
1509
+ . span_until_non_whitespace ( lt_through_ty_span)
1510
+ ) ;
1511
+ break ;
1512
+ }
1513
+ }
1514
+ }
1515
+ } ;
1516
+ if let Node :: Lifetime ( hir_lifetime) = self . tcx . hir . get ( lifetime. id ) {
1517
+ if let Some ( parent) = self . tcx . hir . find ( self . tcx . hir . get_parent ( hir_lifetime. id ) ) {
1518
+ match parent {
1519
+ Node :: Item ( item) => {
1520
+ if let hir:: ItemKind :: Fn ( decl, _, _, _) = & item. node {
1521
+ find_arg_use_span ( & decl. inputs ) ;
1522
+ }
1523
+ } ,
1524
+ Node :: ImplItem ( impl_item) => {
1525
+ if let hir:: ImplItemKind :: Method ( sig, _) = & impl_item. node {
1526
+ find_arg_use_span ( & sig. decl . inputs ) ;
1527
+ }
1528
+ }
1529
+ _ => { }
1530
+ }
1531
+ }
1532
+ }
1533
+
1534
+ if let ( Some ( decl_span) , Some ( use_span) ) = ( remove_decl, remove_use) {
1535
+ // if both declaration and use deletion spans start at the same
1536
+ // place ("start at" because the latter includes trailing
1537
+ // whitespace), then this is an in-band lifetime
1538
+ if decl_span. shrink_to_lo ( ) == use_span. shrink_to_lo ( ) {
1539
+ err. span_suggestion_with_applicability (
1540
+ use_span,
1541
+ "elide the single-use lifetime" ,
1542
+ String :: new ( ) ,
1543
+ Applicability :: MachineApplicable ,
1544
+ ) ;
1545
+ } else {
1546
+ err. multipart_suggestion_with_applicability (
1547
+ "elide the single-use lifetime" ,
1548
+ vec ! [ ( decl_span, String :: new( ) ) , ( use_span, String :: new( ) ) ] ,
1549
+ Applicability :: MachineApplicable ,
1550
+ ) ;
1551
+ }
1474
1552
}
1475
1553
}
1476
1554
@@ -1526,14 +1604,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1526
1604
_ => None ,
1527
1605
} {
1528
1606
debug ! ( "id = {:?} span = {:?} name = {:?}" , node_id, span, name) ;
1607
+
1608
+ if name == keywords:: UnderscoreLifetime . ident ( ) {
1609
+ continue ;
1610
+ }
1611
+
1529
1612
let mut err = self . tcx . struct_span_lint_node (
1530
1613
lint:: builtin:: SINGLE_USE_LIFETIMES ,
1531
1614
id,
1532
1615
span,
1533
1616
& format ! ( "lifetime parameter `{}` only used once" , name) ,
1534
1617
) ;
1535
- err. span_label ( span, "this lifetime..." ) ;
1536
- err. span_label ( lifetime. span , "...is used only here" ) ;
1618
+
1619
+ if span == lifetime. span {
1620
+ // spans are the same for in-band lifetime declarations
1621
+ err. span_label ( span, "this lifetime is only used here" ) ;
1622
+ } else {
1623
+ err. span_label ( span, "this lifetime..." ) ;
1624
+ err. span_label ( lifetime. span , "...is used only here" ) ;
1625
+ }
1626
+ self . suggest_eliding_single_use_lifetime ( & mut err, def_id, lifetime) ;
1537
1627
err. emit ( ) ;
1538
1628
}
1539
1629
}
@@ -1566,7 +1656,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
1566
1656
if let Some ( span) = unused_lt_span {
1567
1657
err. span_suggestion_with_applicability (
1568
1658
span,
1569
- "remove it " ,
1659
+ "elide the unused lifetime " ,
1570
1660
String :: new ( ) ,
1571
1661
Applicability :: MachineApplicable ,
1572
1662
) ;
0 commit comments