23
23
24
24
use rustc:: hir:: def:: { Res , DefKind } ;
25
25
use rustc:: hir:: def_id:: { DefId , LOCAL_CRATE } ;
26
- use rustc:: ty:: { self , Ty } ;
26
+ use rustc:: ty:: { self , Ty , TyCtxt } ;
27
27
use rustc:: { lint, util} ;
28
28
use hir:: Node ;
29
29
use util:: nodemap:: HirIdSet ;
@@ -1494,58 +1494,107 @@ impl EarlyLintPass for KeywordIdents {
1494
1494
declare_lint_pass ! ( ExplicitOutlivesRequirements => [ EXPLICIT_OUTLIVES_REQUIREMENTS ] ) ;
1495
1495
1496
1496
impl ExplicitOutlivesRequirements {
1497
- fn collect_outlives_bound_spans (
1498
- & self ,
1499
- cx : & LateContext < ' _ , ' _ > ,
1500
- item_def_id : DefId ,
1501
- param_name : & str ,
1502
- bounds : & hir:: GenericBounds ,
1503
- infer_static : bool
1504
- ) -> Vec < ( usize , Span ) > {
1505
- // For lack of a more elegant strategy for comparing the `ty::Predicate`s
1506
- // returned by this query with the params/bounds grabbed from the HIR—and
1507
- // with some regrets—we're going to covert the param/lifetime names to
1508
- // strings
1509
- let inferred_outlives = cx. tcx . inferred_outlives_of ( item_def_id) ;
1510
-
1511
- let ty_lt_names = inferred_outlives. iter ( ) . filter_map ( |pred| {
1512
- let binder = match pred {
1513
- ty:: Predicate :: TypeOutlives ( binder) => binder,
1514
- _ => { return None ; }
1515
- } ;
1516
- let ty_outlives_pred = binder. skip_binder ( ) ;
1517
- let ty_name = match ty_outlives_pred. 0 . sty {
1518
- ty:: Param ( param) => param. name . to_string ( ) ,
1519
- _ => { return None ; }
1520
- } ;
1521
- let lt_name = match ty_outlives_pred. 1 {
1522
- ty:: RegionKind :: ReEarlyBound ( region) => {
1523
- region. name . to_string ( )
1524
- } ,
1525
- _ => { return None ; }
1526
- } ;
1527
- Some ( ( ty_name, lt_name) )
1528
- } ) . collect :: < Vec < _ > > ( ) ;
1529
-
1530
- let mut bound_spans = Vec :: new ( ) ;
1531
- for ( i, bound) in bounds. iter ( ) . enumerate ( ) {
1532
- if let hir:: GenericBound :: Outlives ( lifetime) = bound {
1533
- let is_static = match lifetime. name {
1534
- hir:: LifetimeName :: Static => true ,
1535
- _ => false
1536
- } ;
1537
- if is_static && !infer_static {
1538
- // infer-outlives for 'static is still feature-gated (tracking issue #44493)
1539
- continue ;
1497
+ fn lifetimes_outliving_lifetime < ' tcx > (
1498
+ inferred_outlives : & ' tcx [ ty:: Predicate < ' tcx > ] ,
1499
+ index : u32 ,
1500
+ ) -> Vec < ty:: Region < ' tcx > > {
1501
+ inferred_outlives. iter ( ) . filter_map ( |pred| {
1502
+ match pred {
1503
+ ty:: Predicate :: RegionOutlives ( outlives) => {
1504
+ let outlives = outlives. skip_binder ( ) ;
1505
+ match outlives. 0 {
1506
+ ty:: ReEarlyBound ( ebr) if ebr. index == index => {
1507
+ Some ( outlives. 1 )
1508
+ }
1509
+ _ => None ,
1510
+ }
1540
1511
}
1512
+ _ => None
1513
+ }
1514
+ } ) . collect ( )
1515
+ }
1541
1516
1542
- let lt_name = & lifetime. name . ident ( ) . to_string ( ) ;
1543
- if ty_lt_names. contains ( & ( param_name. to_owned ( ) , lt_name. to_owned ( ) ) ) {
1544
- bound_spans. push ( ( i, bound. span ( ) ) ) ;
1517
+ fn lifetimes_outliving_type < ' tcx > (
1518
+ inferred_outlives : & ' tcx [ ty:: Predicate < ' tcx > ] ,
1519
+ index : u32 ,
1520
+ ) -> Vec < ty:: Region < ' tcx > > {
1521
+ inferred_outlives. iter ( ) . filter_map ( |pred| {
1522
+ match pred {
1523
+ ty:: Predicate :: TypeOutlives ( outlives) => {
1524
+ let outlives = outlives. skip_binder ( ) ;
1525
+ if outlives. 0 . is_param ( index) {
1526
+ Some ( outlives. 1 )
1527
+ } else {
1528
+ None
1529
+ }
1545
1530
}
1531
+ _ => None
1532
+ }
1533
+ } ) . collect ( )
1534
+ }
1535
+
1536
+ fn collect_outlived_lifetimes < ' tcx > (
1537
+ & self ,
1538
+ param : & ' tcx hir:: GenericParam ,
1539
+ tcx : TyCtxt < ' tcx > ,
1540
+ inferred_outlives : & ' tcx [ ty:: Predicate < ' tcx > ] ,
1541
+ ty_generics : & ' tcx ty:: Generics ,
1542
+ ) -> Vec < ty:: Region < ' tcx > > {
1543
+ let index = ty_generics. param_def_id_to_index [
1544
+ & tcx. hir ( ) . local_def_id_from_hir_id ( param. hir_id ) ] ;
1545
+
1546
+ match param. kind {
1547
+ hir:: GenericParamKind :: Lifetime { .. } => {
1548
+ Self :: lifetimes_outliving_lifetime ( inferred_outlives, index)
1549
+ }
1550
+ hir:: GenericParamKind :: Type { .. } => {
1551
+ Self :: lifetimes_outliving_type ( inferred_outlives, index)
1546
1552
}
1553
+ hir:: GenericParamKind :: Const { .. } => Vec :: new ( ) ,
1547
1554
}
1548
- bound_spans
1555
+ }
1556
+
1557
+
1558
+ fn collect_outlives_bound_spans < ' tcx > (
1559
+ & self ,
1560
+ tcx : TyCtxt < ' tcx > ,
1561
+ bounds : & hir:: GenericBounds ,
1562
+ inferred_outlives : & [ ty:: Region < ' tcx > ] ,
1563
+ infer_static : bool ,
1564
+ ) -> Vec < ( usize , Span ) > {
1565
+ use rustc:: middle:: resolve_lifetime:: Region ;
1566
+
1567
+ bounds
1568
+ . iter ( )
1569
+ . enumerate ( )
1570
+ . filter_map ( |( i, bound) | {
1571
+ if let hir:: GenericBound :: Outlives ( lifetime) = bound {
1572
+ let is_inferred = match tcx. named_region ( lifetime. hir_id ) {
1573
+ Some ( Region :: Static ) if infer_static => {
1574
+ inferred_outlives. iter ( )
1575
+ . any ( |r| if let ty:: ReStatic = r { true } else { false } )
1576
+ }
1577
+ Some ( Region :: EarlyBound ( index, ..) ) => inferred_outlives
1578
+ . iter ( )
1579
+ . any ( |r| {
1580
+ if let ty:: ReEarlyBound ( ebr) = r {
1581
+ ebr. index == index
1582
+ } else {
1583
+ false
1584
+ }
1585
+ } ) ,
1586
+ _ => false ,
1587
+ } ;
1588
+ if is_inferred {
1589
+ Some ( ( i, bound. span ( ) ) )
1590
+ } else {
1591
+ None
1592
+ }
1593
+ } else {
1594
+ None
1595
+ }
1596
+ } )
1597
+ . collect ( )
1549
1598
}
1550
1599
1551
1600
fn consolidate_outlives_bound_spans (
@@ -1569,7 +1618,7 @@ impl ExplicitOutlivesRequirements {
1569
1618
let mut from_start = true ;
1570
1619
for ( i, bound_span) in bound_spans {
1571
1620
match last_merged_i {
1572
- // If the first bound is inferable, our span should also eat the trailing `+`
1621
+ // If the first bound is inferable, our span should also eat the leading `+`
1573
1622
None if i == 0 => {
1574
1623
merged. push ( bound_span. to ( bounds[ 1 ] . span ( ) . shrink_to_lo ( ) ) ) ;
1575
1624
last_merged_i = Some ( 0 ) ;
@@ -1607,26 +1656,48 @@ impl ExplicitOutlivesRequirements {
1607
1656
1608
1657
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for ExplicitOutlivesRequirements {
1609
1658
fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx hir:: Item ) {
1659
+ use rustc:: middle:: resolve_lifetime:: Region ;
1660
+
1610
1661
let infer_static = cx. tcx . features ( ) . infer_static_outlives_requirements ;
1611
1662
let def_id = cx. tcx . hir ( ) . local_def_id_from_hir_id ( item. hir_id ) ;
1612
- if let hir:: ItemKind :: Struct ( _, ref generics) = item. node {
1663
+ if let hir:: ItemKind :: Struct ( _, ref hir_generics)
1664
+ | hir:: ItemKind :: Enum ( _, ref hir_generics)
1665
+ | hir:: ItemKind :: Union ( _, ref hir_generics) = item. node
1666
+ {
1667
+ let inferred_outlives = cx. tcx . inferred_outlives_of ( def_id) ;
1668
+ if inferred_outlives. is_empty ( ) {
1669
+ return ;
1670
+ }
1671
+
1672
+ let ty_generics = cx. tcx . generics_of ( def_id) ;
1673
+
1613
1674
let mut bound_count = 0 ;
1614
1675
let mut lint_spans = Vec :: new ( ) ;
1615
1676
1616
- for param in & generics. params {
1617
- let param_name = match param. kind {
1618
- hir:: GenericParamKind :: Lifetime { .. } => continue ,
1619
- hir:: GenericParamKind :: Type { .. } => {
1620
- match param. name {
1621
- hir:: ParamName :: Fresh ( _) => continue ,
1622
- hir:: ParamName :: Error => continue ,
1623
- hir:: ParamName :: Plain ( name) => name. to_string ( ) ,
1624
- }
1677
+ for param in & hir_generics. params {
1678
+ let has_lifetime_bounds = param. bounds . iter ( ) . any ( |bound| {
1679
+ if let hir:: GenericBound :: Outlives ( _) = bound {
1680
+ true
1681
+ } else {
1682
+ false
1625
1683
}
1626
- hir:: GenericParamKind :: Const { .. } => continue ,
1627
- } ;
1684
+ } ) ;
1685
+ if !has_lifetime_bounds {
1686
+ continue ;
1687
+ }
1688
+
1689
+ let relevant_lifetimes = self . collect_outlived_lifetimes (
1690
+ param,
1691
+ cx. tcx ,
1692
+ inferred_outlives,
1693
+ ty_generics,
1694
+ ) ;
1695
+ if relevant_lifetimes. is_empty ( ) {
1696
+ continue ;
1697
+ }
1698
+
1628
1699
let bound_spans = self . collect_outlives_bound_spans (
1629
- cx, def_id , & param_name , & param. bounds , infer_static
1700
+ cx. tcx , & param. bounds , & relevant_lifetimes , infer_static,
1630
1701
) ;
1631
1702
bound_count += bound_spans. len ( ) ;
1632
1703
lint_spans. extend (
@@ -1638,54 +1709,92 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
1638
1709
1639
1710
let mut where_lint_spans = Vec :: new ( ) ;
1640
1711
let mut dropped_predicate_count = 0 ;
1641
- let num_predicates = generics. where_clause . predicates . len ( ) ;
1642
- for ( i, where_predicate) in generics. where_clause . predicates . iter ( ) . enumerate ( ) {
1643
- if let hir:: WherePredicate :: BoundPredicate ( predicate) = where_predicate {
1644
- let param_name = match predicate. bounded_ty . node {
1645
- hir:: TyKind :: Path ( ref qpath) => {
1646
- if let hir:: QPath :: Resolved ( None , ty_param_path) = qpath {
1647
- ty_param_path. segments [ 0 ] . ident . to_string ( )
1648
- } else {
1649
- continue ;
1650
- }
1651
- } ,
1652
- _ => { continue ; }
1653
- } ;
1654
- let bound_spans = self . collect_outlives_bound_spans (
1655
- cx, def_id, & param_name, & predicate. bounds , infer_static
1656
- ) ;
1657
- bound_count += bound_spans. len ( ) ;
1658
-
1659
- let drop_predicate = bound_spans. len ( ) == predicate. bounds . len ( ) ;
1660
- if drop_predicate {
1661
- dropped_predicate_count += 1 ;
1662
- }
1663
-
1664
- // If all the bounds on a predicate were inferable and there are
1665
- // further predicates, we want to eat the trailing comma
1666
- if drop_predicate && i + 1 < num_predicates {
1667
- let next_predicate_span = generics. where_clause . predicates [ i+1 ] . span ( ) ;
1668
- where_lint_spans. push (
1669
- predicate. span . to ( next_predicate_span. shrink_to_lo ( ) )
1670
- ) ;
1671
- } else {
1672
- where_lint_spans. extend (
1673
- self . consolidate_outlives_bound_spans (
1674
- predicate. span . shrink_to_lo ( ) ,
1712
+ let num_predicates = hir_generics. where_clause . predicates . len ( ) ;
1713
+ for ( i, where_predicate) in hir_generics. where_clause . predicates . iter ( ) . enumerate ( ) {
1714
+ let ( relevant_lifetimes, bounds, span) = match where_predicate {
1715
+ hir:: WherePredicate :: RegionPredicate ( predicate) => {
1716
+ if let Some ( Region :: EarlyBound ( index, ..) )
1717
+ = cx. tcx . named_region ( predicate. lifetime . hir_id )
1718
+ {
1719
+ (
1720
+ Self :: lifetimes_outliving_lifetime ( inferred_outlives, index) ,
1675
1721
& predicate. bounds ,
1676
- bound_spans
1722
+ predicate . span ,
1677
1723
)
1678
- ) ;
1724
+ } else {
1725
+ continue ;
1726
+ }
1679
1727
}
1728
+ hir:: WherePredicate :: BoundPredicate ( predicate) => {
1729
+ // FIXME we can also infer bounds on associated types,
1730
+ // and should check for them here.
1731
+ match predicate. bounded_ty . node {
1732
+ hir:: TyKind :: Path ( hir:: QPath :: Resolved (
1733
+ None ,
1734
+ ref path,
1735
+ ) ) => if let Res :: Def ( DefKind :: TyParam , def_id) = path. res {
1736
+ let index = ty_generics. param_def_id_to_index [ & def_id] ;
1737
+ (
1738
+ Self :: lifetimes_outliving_type ( inferred_outlives, index) ,
1739
+ & predicate. bounds ,
1740
+ predicate. span ,
1741
+ )
1742
+ } else {
1743
+ continue
1744
+ } ,
1745
+ _ => { continue ; }
1746
+ }
1747
+ }
1748
+ _ => continue ,
1749
+ } ;
1750
+ if relevant_lifetimes. is_empty ( ) {
1751
+ continue ;
1752
+ }
1753
+
1754
+ let bound_spans = self . collect_outlives_bound_spans (
1755
+ cx. tcx , bounds, & relevant_lifetimes, infer_static,
1756
+ ) ;
1757
+ bound_count += bound_spans. len ( ) ;
1758
+
1759
+ let drop_predicate = bound_spans. len ( ) == bounds. len ( ) ;
1760
+ if drop_predicate {
1761
+ dropped_predicate_count += 1 ;
1762
+ }
1763
+
1764
+ // If all the bounds on a predicate were inferable and there are
1765
+ // further predicates, we want to eat the trailing comma
1766
+ if drop_predicate && i + 1 < num_predicates {
1767
+ let next_predicate_span = hir_generics. where_clause . predicates [ i+1 ] . span ( ) ;
1768
+ where_lint_spans. push (
1769
+ span. to ( next_predicate_span. shrink_to_lo ( ) )
1770
+ ) ;
1771
+ } else {
1772
+ where_lint_spans. extend (
1773
+ self . consolidate_outlives_bound_spans (
1774
+ span. shrink_to_lo ( ) ,
1775
+ bounds,
1776
+ bound_spans
1777
+ )
1778
+ ) ;
1680
1779
}
1681
1780
}
1682
1781
1683
1782
// If all predicates are inferable, drop the entire clause
1684
1783
// (including the `where`)
1685
1784
if num_predicates > 0 && dropped_predicate_count == num_predicates {
1686
- let full_where_span = generics. span . shrink_to_hi ( )
1687
- . to ( generics. where_clause . span ( )
1688
- . expect ( "span of (nonempty) where clause should exist" ) ) ;
1785
+ let where_span = hir_generics. where_clause . span ( )
1786
+ . expect ( "span of (nonempty) where clause should exist" ) ;
1787
+ // Extend the where clause back to the closing `>` of the
1788
+ // generics, except for tuple struct, which have the `where`
1789
+ // after the fields of the struct.
1790
+ let full_where_span = match item. node {
1791
+ hir:: ItemKind :: Struct ( hir:: VariantData :: Tuple ( ..) , _) => {
1792
+ where_span
1793
+ }
1794
+ _ => {
1795
+ hir_generics. span . shrink_to_hi ( ) . to ( where_span)
1796
+ }
1797
+ } ;
1689
1798
lint_spans. push (
1690
1799
full_where_span
1691
1800
) ;
0 commit comments