Auto merge of #118391 - compiler-errors:lifetimes-eq, r= · rust-lang/rust@7a2c7d7 (original) (raw)
``
1
`+
#![allow(rustc::diagnostic_outside_of_impl)]
`
``
2
`+
#![allow(rustc::untranslatable_diagnostic)]
`
``
3
+
``
4
`+
use rustc_data_structures::fx::FxHashSet;
`
``
5
`+
use rustc_hir as hir;
`
``
6
`+
use rustc_hir::def::DefKind;
`
``
7
`+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
`
``
8
`+
use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt};
`
``
9
`+
use rustc_macros::LintDiagnostic;
`
``
10
`+
use rustc_middle::ty::{self, TyCtxt};
`
``
11
`+
use rustc_session::lint::builtin::UNUSED_LIFETIMES;
`
``
12
`+
use rustc_span::DUMMY_SP;
`
``
13
`+
use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt};
`
``
14
+
``
15
`+
use crate::{LateContext, LateLintPass};
`
``
16
+
``
17
`+
declare_lint_pass!(RedundantLifetimeArgs => []);
`
``
18
+
``
19
`+
impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs {
`
``
20
`+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
`
``
21
`+
check(cx.tcx, cx.param_env, item.owner_id);
`
``
22
`+
}
`
``
23
+
``
24
`+
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
`
``
25
`+
check(cx.tcx, cx.param_env, item.owner_id);
`
``
26
`+
}
`
``
27
+
``
28
`+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
`
``
29
`+
if cx
`
``
30
`+
.tcx
`
``
31
`+
.hir()
`
``
32
`+
.expect_item(cx.tcx.local_parent(item.owner_id.def_id))
`
``
33
`+
.expect_impl()
`
``
34
`+
.of_trait
`
``
35
`+
.is_some()
`
``
36
`+
{
`
``
37
`+
// Don't check for redundant lifetimes for trait implementations,
`
``
38
`+
// since the signature is required to be compatible with the trait.
`
``
39
`+
return;
`
``
40
`+
}
`
``
41
+
``
42
`+
check(cx.tcx, cx.param_env, item.owner_id);
`
``
43
`+
}
`
``
44
`+
}
`
``
45
+
``
46
`+
fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::OwnerId) {
`
``
47
`+
let def_kind = tcx.def_kind(owner_id);
`
``
48
`+
match def_kind {
`
``
49
`+
DefKind::Struct
`
``
50
`+
| DefKind::Union
`
``
51
`+
| DefKind::Enum
`
``
52
`+
| DefKind::Trait
`
``
53
`+
| DefKind::TraitAlias
`
``
54
`+
| DefKind::AssocTy
`
``
55
`+
| DefKind::Fn
`
``
56
`+
| DefKind::Const
`
``
57
`+
| DefKind::AssocFn
`
``
58
`+
| DefKind::AssocConst
`
``
59
`+
| DefKind::Impl { of_trait: _ } => {
`
``
60
`+
// Proceed
`
``
61
`+
}
`
``
62
`+
DefKind::Mod
`
``
63
`+
| DefKind::Variant
`
``
64
`+
| DefKind::TyAlias
`
``
65
`+
| DefKind::ForeignTy
`
``
66
`+
| DefKind::TyParam
`
``
67
`+
| DefKind::ConstParam
`
``
68
`+
| DefKind::Static(_)
`
``
69
`+
| DefKind::Ctor(_, _)
`
``
70
`+
| DefKind::Macro(_)
`
``
71
`+
| DefKind::ExternCrate
`
``
72
`+
| DefKind::Use
`
``
73
`+
| DefKind::ForeignMod
`
``
74
`+
| DefKind::AnonConst
`
``
75
`+
| DefKind::InlineConst
`
``
76
`+
| DefKind::OpaqueTy
`
``
77
`+
| DefKind::Field
`
``
78
`+
| DefKind::LifetimeParam
`
``
79
`+
| DefKind::GlobalAsm
`
``
80
`+
| DefKind::Closure => return,
`
``
81
`+
}
`
``
82
+
``
83
`+
let infcx = &tcx.infer_ctxt().build();
`
``
84
`+
let ocx = ObligationCtxt::new(infcx);
`
``
85
+
``
86
`+
// Compute the implied outlives bounds for the item. This ensures that we treat
`
``
87
`` +
// a signature with an argument like &'a &'b ()
as implicitly having 'b: 'a
.
``
``
88
`+
let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else {
`
``
89
`+
return;
`
``
90
`+
};
`
``
91
`+
let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types);
`
``
92
`+
let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds);
`
``
93
+
``
94
`+
// The ordering of this lifetime map is a bit subtle.
`
``
95
`+
//
`
``
96
`+
// Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
`
``
97
`` +
// where we can prove that 'candidate = 'victim
.
``
``
98
`+
//
`
``
99
`` +
// 'static
must come first in this list because we can never replace 'static
with
``
``
100
`` +
// something else, but if we find some lifetime 'a
where 'a = 'static
, we want to
``
``
101
`` +
// suggest replacing 'a
with 'static
.
``
``
102
`+
let mut lifetimes = vec![tcx.lifetimes.re_static];
`
``
103
`+
lifetimes.extend(
`
``
104
`+
ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),
`
``
105
`+
);
`
``
106
`+
// If we are in a function, add its late-bound lifetimes too.
`
``
107
`+
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
`
``
108
`+
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
`
``
109
`+
let ty::BoundVariableKind::Region(kind) = var else { continue };
`
``
110
`+
lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
`
``
111
`+
}
`
``
112
`+
}
`
``
113
+
``
114
`+
// Keep track of lifetimes which have already been replaced with other lifetimes.
`
``
115
`` +
// This makes sure that if 'a = 'b = 'c
, we don't say 'c
should be replaced by
``
``
116
`` +
// both 'a
and 'b
.
``
``
117
`+
let mut shadowed = FxHashSet::default();
`
``
118
+
``
119
`+
for (idx, &candidate) in lifetimes.iter().enumerate() {
`
``
120
`+
// Don't suggest removing a lifetime twice.
`
``
121
`+
if shadowed.contains(&candidate) {
`
``
122
`+
continue;
`
``
123
`+
}
`
``
124
+
``
125
`` +
// Can't rename a named lifetime named '_
without ambiguity.
``
``
126
`+
if !candidate.has_name() {
`
``
127
`+
continue;
`
``
128
`+
}
`
``
129
+
``
130
`+
for &victim in &lifetimes[(idx + 1)..] {
`
``
131
`+
// We only care about lifetimes that are "real", i.e. that have a def-id.
`
``
132
`+
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
`
``
133
`+
| ty::ReLateParam(ty::LateParamRegion {
`
``
134
`+
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
`
``
135
`+
..
`
``
136
`+
})) = victim.kind()
`
``
137
`+
else {
`
``
138
`+
continue;
`
``
139
`+
};
`
``
140
+
``
141
`+
// Do not rename lifetimes not local to this item since they'll overlap
`
``
142
`+
// with the lint running on the parent. We still want to consider parent
`
``
143
`+
// lifetimes which make child lifetimes redundant, otherwise we would
`
``
144
`` +
// have truncated the identity_for_item
args above.
``
``
145
`+
if tcx.parent(def_id) != owner_id.to_def_id() {
`
``
146
`+
continue;
`
``
147
`+
}
`
``
148
+
``
149
`+
let infcx = infcx.fork();
`
``
150
+
``
151
`` +
// Require that 'candidate = 'victim
``
``
152
`+
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim);
`
``
153
`+
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate);
`
``
154
+
``
155
`` +
// If there are no lifetime errors, then we have proven that 'candidate = 'victim
!
``
``
156
`+
if infcx.resolve_regions(outlives_env).is_empty() {
`
``
157
`+
shadowed.insert(victim);
`
``
158
`+
tcx.emit_spanned_lint(
`
``
159
`+
UNUSED_LIFETIMES,
`
``
160
`+
tcx.local_def_id_to_hir_id(def_id.expect_local()),
`
``
161
`+
tcx.def_span(def_id),
`
``
162
`+
RedundantLifetimeArgsLint { candidate, victim },
`
``
163
`+
);
`
``
164
`+
}
`
``
165
`+
}
`
``
166
`+
}
`
``
167
`+
}
`
``
168
+
``
169
`+
#[derive(LintDiagnostic)]
`
``
170
`+
#[diag(lint_redundant_lifetime_args)]
`
``
171
`+
#[note]
`
``
172
`+
struct RedundantLifetimeArgsLint<'tcx> {
`
``
173
`+
/// The lifetime we have found to be redundant.
`
``
174
`+
victim: ty::Region<'tcx>,
`
``
175
`+
// The lifetime we can replace the victim with.
`
``
176
`+
candidate: ty::Region<'tcx>,
`
``
177
`+
}
`