@@ -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,130 @@ 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 : false } => {
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 trait implementations,
2038
+ // since the signature is required to be compatible with the trait.
2039
+ return ;
2040
+ }
2041
+ }
2042
+ DefKind :: Impl { of_trait : true }
2043
+ | DefKind :: Mod
2044
+ | DefKind :: Variant
2045
+ | DefKind :: TyAlias
2046
+ | DefKind :: ForeignTy
2047
+ | DefKind :: TyParam
2048
+ | DefKind :: ConstParam
2049
+ | DefKind :: Static { .. }
2050
+ | DefKind :: Ctor ( _, _)
2051
+ | DefKind :: Macro ( _)
2052
+ | DefKind :: ExternCrate
2053
+ | DefKind :: Use
2054
+ | DefKind :: ForeignMod
2055
+ | DefKind :: AnonConst
2056
+ | DefKind :: InlineConst
2057
+ | DefKind :: OpaqueTy
2058
+ | DefKind :: Field
2059
+ | DefKind :: LifetimeParam
2060
+ | DefKind :: GlobalAsm
2061
+ | DefKind :: Closure => return ,
2062
+ }
2063
+
2064
+ // The ordering of this lifetime map is a bit subtle.
2065
+ //
2066
+ // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
2067
+ // where we can prove that `'candidate = 'victim`.
2068
+ //
2069
+ // `'static` must come first in this list because we can never replace `'static` with
2070
+ // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
2071
+ // suggest replacing `'a` with `'static`.
2072
+ let mut lifetimes = vec ! [ tcx. lifetimes. re_static] ;
2073
+ lifetimes. extend (
2074
+ ty:: GenericArgs :: identity_for_item ( tcx, owner_id) . iter ( ) . filter_map ( |arg| arg. as_region ( ) ) ,
2075
+ ) ;
2076
+ // If we are in a function, add its late-bound lifetimes too.
2077
+ if matches ! ( def_kind, DefKind :: Fn | DefKind :: AssocFn ) {
2078
+ for var in tcx. fn_sig ( owner_id) . instantiate_identity ( ) . bound_vars ( ) {
2079
+ let ty:: BoundVariableKind :: Region ( kind) = var else { continue } ;
2080
+ lifetimes. push ( ty:: Region :: new_late_param ( tcx, owner_id. to_def_id ( ) , kind) ) ;
2081
+ }
2082
+ }
2083
+ lifetimes. retain ( |candidate| candidate. has_name ( ) ) ;
2084
+
2085
+ // Keep track of lifetimes which have already been replaced with other lifetimes.
2086
+ // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
2087
+ // both `'a` and `'b`.
2088
+ let mut shadowed = FxHashSet :: default ( ) ;
2089
+
2090
+ for ( idx, & candidate) in lifetimes. iter ( ) . enumerate ( ) {
2091
+ // Don't suggest removing a lifetime twice.
2092
+ if shadowed. contains ( & candidate) {
2093
+ continue ;
2094
+ }
2095
+
2096
+ for & victim in & lifetimes[ ( idx + 1 ) ..] {
2097
+ // We only care about lifetimes that are "real", i.e. that have a def-id.
2098
+ let ( ty:: ReEarlyParam ( ty:: EarlyParamRegion { def_id, .. } )
2099
+ | ty:: ReLateParam ( ty:: LateParamRegion {
2100
+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, _) ,
2101
+ ..
2102
+ } ) ) = victim. kind ( )
2103
+ else {
2104
+ continue ;
2105
+ } ;
2106
+
2107
+ // Do not rename lifetimes not local to this item since they'll overlap
2108
+ // with the lint running on the parent. We still want to consider parent
2109
+ // lifetimes which make child lifetimes redundant, otherwise we would
2110
+ // have truncated the `identity_for_item` args above.
2111
+ if tcx. parent ( def_id) != owner_id. to_def_id ( ) {
2112
+ continue ;
2113
+ }
2114
+
2115
+ // If there are no lifetime errors, then we have proven that `'candidate = 'victim`!
2116
+ if outlives_env. free_region_map ( ) . sub_free_regions ( tcx, candidate, victim)
2117
+ && outlives_env. free_region_map ( ) . sub_free_regions ( tcx, victim, candidate)
2118
+ {
2119
+ shadowed. insert ( victim) ;
2120
+ tcx. emit_spanned_lint (
2121
+ rustc_lint_defs:: builtin:: UNUSED_LIFETIMES ,
2122
+ tcx. local_def_id_to_hir_id ( def_id. expect_local ( ) ) ,
2123
+ tcx. def_span ( def_id) ,
2124
+ RedundantLifetimeArgsLint { candidate, victim } ,
2125
+ ) ;
2126
+ }
2127
+ }
2128
+ }
2129
+ }
2130
+
2131
+ #[ derive( LintDiagnostic ) ]
2132
+ #[ diag( hir_analysis_redundant_lifetime_args) ]
2133
+ #[ note]
2134
+ struct RedundantLifetimeArgsLint < ' tcx > {
2135
+ /// The lifetime we have found to be redundant.
2136
+ victim : ty:: Region < ' tcx > ,
2137
+ // The lifetime we can replace the victim with.
2138
+ candidate : ty:: Region < ' tcx > ,
2139
+ }
2140
+
2013
2141
pub fn provide ( providers : & mut Providers ) {
2014
2142
* providers = Providers { check_mod_type_wf, check_well_formed, ..* providers } ;
2015
2143
}
0 commit comments