@@ -8,11 +8,13 @@ use rustc_ast as ast;
8
8
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
9
9
use rustc_errors:: { codes:: * , pluralize, struct_span_code_err, Applicability , ErrorGuaranteed } ;
10
10
use rustc_hir as hir;
11
+ use rustc_hir:: def:: DefKind ;
11
12
use rustc_hir:: def_id:: { DefId , LocalDefId , LocalModDefId } ;
12
13
use rustc_hir:: lang_items:: LangItem ;
13
14
use rustc_hir:: ItemKind ;
14
15
use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
15
16
use rustc_infer:: infer:: { self , InferCtxt , TyCtxtInferExt } ;
17
+ use rustc_macros:: LintDiagnostic ;
16
18
use rustc_middle:: query:: Providers ;
17
19
use rustc_middle:: ty:: print:: with_no_trimmed_paths;
18
20
use rustc_middle:: ty:: trait_def:: TraitSpecializationKind ;
@@ -136,6 +138,8 @@ where
136
138
infcx. implied_bounds_tys_compat ( param_env, body_def_id, & assumed_wf_types, false ) ;
137
139
let outlives_env = OutlivesEnvironment :: with_bounds ( param_env, implied_bounds) ;
138
140
141
+ lint_redundant_lifetimes ( tcx, body_def_id, & outlives_env) ;
142
+
139
143
let errors = infcx. resolve_regions ( & outlives_env) ;
140
144
if errors. is_empty ( ) {
141
145
return Ok ( ( ) ) ;
@@ -2010,6 +2014,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error
2010
2014
res
2011
2015
}
2012
2016
2017
+ fn lint_redundant_lifetimes < ' tcx > (
2018
+ tcx : TyCtxt < ' tcx > ,
2019
+ owner_id : LocalDefId ,
2020
+ outlives_env : & OutlivesEnvironment < ' tcx > ,
2021
+ ) {
2022
+ let def_kind = tcx. def_kind ( owner_id) ;
2023
+ match def_kind {
2024
+ DefKind :: Struct
2025
+ | DefKind :: Union
2026
+ | DefKind :: Enum
2027
+ | DefKind :: Trait
2028
+ | DefKind :: TraitAlias
2029
+ | DefKind :: Fn
2030
+ | DefKind :: Const
2031
+ | DefKind :: Impl { of_trait : _ } => {
2032
+ // Proceed
2033
+ }
2034
+ DefKind :: AssocFn | DefKind :: AssocTy | DefKind :: AssocConst => {
2035
+ let parent_def_id = tcx. local_parent ( owner_id) ;
2036
+ if matches ! ( tcx. def_kind( parent_def_id) , DefKind :: Impl { of_trait: true } ) {
2037
+ // Don't check for redundant lifetimes for associated items of trait
2038
+ // implementations, since the signature is required to be compatible
2039
+ // with the trait, even if the implementation implies some lifetimes
2040
+ // are redundant.
2041
+ return ;
2042
+ }
2043
+ }
2044
+ DefKind :: Mod
2045
+ | DefKind :: Variant
2046
+ | DefKind :: TyAlias
2047
+ | DefKind :: ForeignTy
2048
+ | DefKind :: TyParam
2049
+ | DefKind :: ConstParam
2050
+ | DefKind :: Static { .. }
2051
+ | DefKind :: Ctor ( _, _)
2052
+ | DefKind :: Macro ( _)
2053
+ | DefKind :: ExternCrate
2054
+ | DefKind :: Use
2055
+ | DefKind :: ForeignMod
2056
+ | DefKind :: AnonConst
2057
+ | DefKind :: InlineConst
2058
+ | DefKind :: OpaqueTy
2059
+ | DefKind :: Field
2060
+ | DefKind :: LifetimeParam
2061
+ | DefKind :: GlobalAsm
2062
+ | DefKind :: Closure => return ,
2063
+ }
2064
+
2065
+ // The ordering of this lifetime map is a bit subtle.
2066
+ //
2067
+ // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
2068
+ // where we can prove that `'candidate = 'victim`.
2069
+ //
2070
+ // `'static` must come first in this list because we can never replace `'static` with
2071
+ // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
2072
+ // suggest replacing `'a` with `'static`.
2073
+ let mut lifetimes = vec ! [ tcx. lifetimes. re_static] ;
2074
+ lifetimes. extend (
2075
+ ty:: GenericArgs :: identity_for_item ( tcx, owner_id) . iter ( ) . filter_map ( |arg| arg. as_region ( ) ) ,
2076
+ ) ;
2077
+ // If we are in a function, add its late-bound lifetimes too.
2078
+ if matches ! ( def_kind, DefKind :: Fn | DefKind :: AssocFn ) {
2079
+ for var in tcx. fn_sig ( owner_id) . instantiate_identity ( ) . bound_vars ( ) {
2080
+ let ty:: BoundVariableKind :: Region ( kind) = var else { continue } ;
2081
+ lifetimes. push ( ty:: Region :: new_late_param ( tcx, owner_id. to_def_id ( ) , kind) ) ;
2082
+ }
2083
+ }
2084
+ lifetimes. retain ( |candidate| candidate. has_name ( ) ) ;
2085
+
2086
+ // Keep track of lifetimes which have already been replaced with other lifetimes.
2087
+ // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
2088
+ // both `'a` and `'b`.
2089
+ let mut shadowed = FxHashSet :: default ( ) ;
2090
+
2091
+ for ( idx, & candidate) in lifetimes. iter ( ) . enumerate ( ) {
2092
+ // Don't suggest removing a lifetime twice. We only need to check this
2093
+ // here and not up in the `victim` loop because equality is transitive,
2094
+ // so if A = C and B = C, then A must = B, so it'll be shadowed too in
2095
+ // A's victim loop.
2096
+ if shadowed. contains ( & candidate) {
2097
+ continue ;
2098
+ }
2099
+
2100
+ for & victim in & lifetimes[ ( idx + 1 ) ..] {
2101
+ // We should only have late-bound lifetimes of the `BrNamed` variety,
2102
+ // since we get these signatures straight from `hir_lowering`. And any
2103
+ // other regions (ReError/ReStatic/etc.) shouldn't matter, since we
2104
+ // can't really suggest to remove them.
2105
+ let ( ty:: ReEarlyParam ( ty:: EarlyParamRegion { def_id, .. } )
2106
+ | ty:: ReLateParam ( ty:: LateParamRegion {
2107
+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, _) ,
2108
+ ..
2109
+ } ) ) = victim. kind ( )
2110
+ else {
2111
+ continue ;
2112
+ } ;
2113
+
2114
+ // Do not rename lifetimes not local to this item since they'll overlap
2115
+ // with the lint running on the parent. We still want to consider parent
2116
+ // lifetimes which make child lifetimes redundant, otherwise we would
2117
+ // have truncated the `identity_for_item` args above.
2118
+ if tcx. parent ( def_id) != owner_id. to_def_id ( ) {
2119
+ continue ;
2120
+ }
2121
+
2122
+ // If `candidate <: victim` and `victim <: candidate`, then they're equal.
2123
+ if outlives_env. free_region_map ( ) . sub_free_regions ( tcx, candidate, victim)
2124
+ && outlives_env. free_region_map ( ) . sub_free_regions ( tcx, victim, candidate)
2125
+ {
2126
+ shadowed. insert ( victim) ;
2127
+ tcx. emit_node_span_lint (
2128
+ rustc_lint_defs:: builtin:: REDUNDANT_LIFETIMES ,
2129
+ tcx. local_def_id_to_hir_id ( def_id. expect_local ( ) ) ,
2130
+ tcx. def_span ( def_id) ,
2131
+ RedundantLifetimeArgsLint { candidate, victim } ,
2132
+ ) ;
2133
+ }
2134
+ }
2135
+ }
2136
+ }
2137
+
2138
+ #[ derive( LintDiagnostic ) ]
2139
+ #[ diag( hir_analysis_redundant_lifetime_args) ]
2140
+ #[ note]
2141
+ struct RedundantLifetimeArgsLint < ' tcx > {
2142
+ /// The lifetime we have found to be redundant.
2143
+ victim : ty:: Region < ' tcx > ,
2144
+ // The lifetime we can replace the victim with.
2145
+ candidate : ty:: Region < ' tcx > ,
2146
+ }
2147
+
2013
2148
pub fn provide ( providers : & mut Providers ) {
2014
2149
* providers = Providers { check_mod_type_wf, check_well_formed, ..* providers } ;
2015
2150
}
0 commit comments