@@ -1999,3 +1999,233 @@ impl EarlyLintPass for KeywordIdents {
1999
1999
lint. emit ( )
2000
2000
}
2001
2001
}
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