@@ -1443,23 +1443,101 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
14431443 /// helper method to determine the span to remove when suggesting the
14441444 /// deletion of a lifetime
14451445 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 ;
14581452 }
1453+ }
1454+ if in_band {
1455+ Some ( param. span )
14591456 } 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+ }
14611469 }
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+ }
14631541 }
14641542 }
14651543
@@ -1521,8 +1599,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15211599 span,
15221600 & format ! ( "lifetime parameter `{}` only used once" , name) ,
15231601 ) ;
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) ;
15261611 err. emit ( ) ;
15271612 }
15281613 }
@@ -1555,7 +1640,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15551640 if let Some ( span) = unused_lt_span {
15561641 err. span_suggestion_with_applicability (
15571642 span,
1558- "remove it " ,
1643+ "elide the unused lifetime " ,
15591644 String :: new ( ) ,
15601645 Applicability :: MachineApplicable ,
15611646 ) ;
0 commit comments