@@ -1999,3 +1999,233 @@ impl EarlyLintPass for KeywordIdents {
19991999 lint. emit ( )
20002000 }
20012001}
2002+
2003+
2004+ pub struct ExplicitOutlivesRequirements ;
2005+
2006+ impl LintPass for ExplicitOutlivesRequirements {
2007+ fn get_lints ( & self ) -> LintArray {
2008+ lint_array ! [ EXPLICIT_OUTLIVES_REQUIREMENTS ]
2009+ }
2010+ }
2011+
2012+ impl ExplicitOutlivesRequirements {
2013+ fn collect_outlives_bound_spans (
2014+ & self ,
2015+ cx : & LateContext ,
2016+ item_def_id : DefId ,
2017+ param_name : & str ,
2018+ bounds : & hir:: GenericBounds ,
2019+ infer_static : bool
2020+ ) -> Vec < ( usize , Span ) > {
2021+ // For lack of a more elegant strategy for comparing the `ty::Predicate`s
2022+ // returned by this query with the params/bounds grabbed from the HIR—and
2023+ // with some regrets—we're going to covert the param/lifetime names to
2024+ // strings
2025+ let inferred_outlives = cx. tcx . inferred_outlives_of ( item_def_id) ;
2026+
2027+ let ty_lt_names = inferred_outlives. iter ( ) . filter_map ( |pred| {
2028+ let binder = match pred {
2029+ ty:: Predicate :: TypeOutlives ( binder) => binder,
2030+ _ => { return None ; }
2031+ } ;
2032+ let ty_outlives_pred = binder. skip_binder ( ) ;
2033+ let ty_name = match ty_outlives_pred. 0 . sty {
2034+ ty:: Param ( param) => param. name . to_string ( ) ,
2035+ _ => { return None ; }
2036+ } ;
2037+ let lt_name = match ty_outlives_pred. 1 {
2038+ ty:: RegionKind :: ReEarlyBound ( region) => {
2039+ region. name . to_string ( )
2040+ } ,
2041+ _ => { return None ; }
2042+ } ;
2043+ Some ( ( ty_name, lt_name) )
2044+ } ) . collect :: < Vec < _ > > ( ) ;
2045+
2046+ let mut bound_spans = Vec :: new ( ) ;
2047+ for ( i, bound) in bounds. iter ( ) . enumerate ( ) {
2048+ if let hir:: GenericBound :: Outlives ( lifetime) = bound {
2049+ let is_static = match lifetime. name {
2050+ hir:: LifetimeName :: Static => true ,
2051+ _ => false
2052+ } ;
2053+ if is_static && !infer_static {
2054+ // infer-outlives for 'static is still feature-gated (tracking issue #44493)
2055+ continue ;
2056+ }
2057+
2058+ let lt_name = & lifetime. name . ident ( ) . to_string ( ) ;
2059+ if ty_lt_names. contains ( & ( param_name. to_owned ( ) , lt_name. to_owned ( ) ) ) {
2060+ bound_spans. push ( ( i, bound. span ( ) ) ) ;
2061+ }
2062+ }
2063+ }
2064+ bound_spans
2065+ }
2066+
2067+ fn consolidate_outlives_bound_spans (
2068+ & self ,
2069+ lo : Span ,
2070+ bounds : & hir:: GenericBounds ,
2071+ bound_spans : Vec < ( usize , Span ) >
2072+ ) -> Vec < Span > {
2073+ if bounds. is_empty ( ) {
2074+ return Vec :: new ( ) ;
2075+ }
2076+ if bound_spans. len ( ) == bounds. len ( ) {
2077+ let ( _, last_bound_span) = bound_spans[ bound_spans. len ( ) -1 ] ;
2078+ // If all bounds are inferable, we want to delete the colon, so
2079+ // start from just after the parameter (span passed as argument)
2080+ vec ! [ lo. to( last_bound_span) ]
2081+ } else {
2082+ let mut merged = Vec :: new ( ) ;
2083+ let mut last_merged_i = None ;
2084+
2085+ let mut from_start = true ;
2086+ for ( i, bound_span) in bound_spans {
2087+ match last_merged_i {
2088+ // If the first bound is inferable, our span should also eat the trailing `+`
2089+ None if i == 0 => {
2090+ merged. push ( bound_span. to ( bounds[ 1 ] . span ( ) . shrink_to_lo ( ) ) ) ;
2091+ last_merged_i = Some ( 0 ) ;
2092+ } ,
2093+ // If consecutive bounds are inferable, merge their spans
2094+ Some ( h) if i == h+1 => {
2095+ if let Some ( tail) = merged. last_mut ( ) {
2096+ // Also eat the trailing `+` if the first
2097+ // more-than-one bound is inferable
2098+ let to_span = if from_start && i < bounds. len ( ) {
2099+ bounds[ i+1 ] . span ( ) . shrink_to_lo ( )
2100+ } else {
2101+ bound_span
2102+ } ;
2103+ * tail = tail. to ( to_span) ;
2104+ last_merged_i = Some ( i) ;
2105+ } else {
2106+ bug ! ( "another bound-span visited earlier" ) ;
2107+ }
2108+ } ,
2109+ _ => {
2110+ // When we find a non-inferable bound, subsequent inferable bounds
2111+ // won't be consecutive from the start (and we'll eat the leading
2112+ // `+` rather than the trailing one)
2113+ from_start = false ;
2114+ merged. push ( bounds[ i-1 ] . span ( ) . shrink_to_hi ( ) . to ( bound_span) ) ;
2115+ last_merged_i = Some ( i) ;
2116+ }
2117+ }
2118+ }
2119+ merged
2120+ }
2121+ }
2122+ }
2123+
2124+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for ExplicitOutlivesRequirements {
2125+ fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx hir:: Item ) {
2126+ let infer_static = cx. tcx . features ( ) . infer_static_outlives_requirements ;
2127+ let def_id = cx. tcx . hir . local_def_id ( item. id ) ;
2128+ if let hir:: ItemKind :: Struct ( _, ref generics) = item. node {
2129+ let mut bound_count = 0 ;
2130+ let mut lint_spans = Vec :: new ( ) ;
2131+
2132+ for param in & generics. params {
2133+ let param_name = match param. kind {
2134+ hir:: GenericParamKind :: Lifetime { .. } => { continue ; } ,
2135+ hir:: GenericParamKind :: Type { .. } => {
2136+ match param. name {
2137+ hir:: ParamName :: Fresh ( _) => { continue ; } ,
2138+ hir:: ParamName :: Plain ( name) => name. to_string ( )
2139+ }
2140+ }
2141+ } ;
2142+ let bound_spans = self . collect_outlives_bound_spans (
2143+ cx, def_id, & param_name, & param. bounds , infer_static
2144+ ) ;
2145+ bound_count += bound_spans. len ( ) ;
2146+ lint_spans. extend (
2147+ self . consolidate_outlives_bound_spans (
2148+ param. span . shrink_to_hi ( ) , & param. bounds , bound_spans
2149+ )
2150+ ) ;
2151+ }
2152+
2153+ let mut where_lint_spans = Vec :: new ( ) ;
2154+ let mut dropped_predicate_count = 0 ;
2155+ let num_predicates = generics. where_clause . predicates . len ( ) ;
2156+ for ( i, where_predicate) in generics. where_clause . predicates . iter ( ) . enumerate ( ) {
2157+ if let hir:: WherePredicate :: BoundPredicate ( predicate) = where_predicate {
2158+ let param_name = match predicate. bounded_ty . node {
2159+ hir:: TyKind :: Path ( ref qpath) => {
2160+ if let hir:: QPath :: Resolved ( None , ty_param_path) = qpath {
2161+ ty_param_path. segments [ 0 ] . ident . to_string ( )
2162+ } else {
2163+ continue ;
2164+ }
2165+ } ,
2166+ _ => { continue ; }
2167+ } ;
2168+ let bound_spans = self . collect_outlives_bound_spans (
2169+ cx, def_id, & param_name, & predicate. bounds , infer_static
2170+ ) ;
2171+ bound_count += bound_spans. len ( ) ;
2172+
2173+ let drop_predicate = bound_spans. len ( ) == predicate. bounds . len ( ) ;
2174+ if drop_predicate {
2175+ dropped_predicate_count += 1 ;
2176+ }
2177+
2178+ // If all the bounds on a predicate were inferable and there are
2179+ // further predicates, we want to eat the trailing comma
2180+ if drop_predicate && i + 1 < num_predicates {
2181+ let next_predicate_span = generics. where_clause . predicates [ i+1 ] . span ( ) ;
2182+ where_lint_spans. push (
2183+ predicate. span . to ( next_predicate_span. shrink_to_lo ( ) )
2184+ ) ;
2185+ } else {
2186+ where_lint_spans. extend (
2187+ self . consolidate_outlives_bound_spans (
2188+ predicate. span . shrink_to_lo ( ) ,
2189+ & predicate. bounds ,
2190+ bound_spans
2191+ )
2192+ ) ;
2193+ }
2194+ }
2195+ }
2196+
2197+ // If all predicates are inferable, drop the entire clause
2198+ // (including the `where`)
2199+ if num_predicates > 0 && dropped_predicate_count == num_predicates {
2200+ let full_where_span = generics. span . shrink_to_hi ( )
2201+ . to ( generics. where_clause . span ( )
2202+ . expect ( "span of (nonempty) where clause should exist" ) ) ;
2203+ lint_spans. push (
2204+ full_where_span
2205+ ) ;
2206+ } else {
2207+ lint_spans. extend ( where_lint_spans) ;
2208+ }
2209+
2210+ if !lint_spans. is_empty ( ) {
2211+ let mut err = cx. struct_span_lint (
2212+ EXPLICIT_OUTLIVES_REQUIREMENTS ,
2213+ lint_spans. clone ( ) ,
2214+ "outlives requirements can be inferred"
2215+ ) ;
2216+ err. multipart_suggestion_with_applicability (
2217+ if bound_count == 1 {
2218+ "remove this bound"
2219+ } else {
2220+ "remove these bounds"
2221+ } ,
2222+ lint_spans. into_iter ( ) . map ( |span| ( span, "" . to_owned ( ) ) ) . collect :: < Vec < _ > > ( ) ,
2223+ Applicability :: MachineApplicable
2224+ ) ;
2225+ err. emit ( ) ;
2226+ }
2227+
2228+ }
2229+ }
2230+
2231+ }
0 commit comments