Implement initial IMPL_TRAIT_OVERCAPTURES lint · rust-lang/rust@d57e57c (original) (raw)
``
1
`+
use rustc_data_structures::{fx::FxIndexSet, unord::UnordSet};
`
``
2
`+
use rustc_errors::LintDiagnostic;
`
``
3
`+
use rustc_hir as hir;
`
``
4
`+
use rustc_hir::def::DefKind;
`
``
5
`+
use rustc_hir::def_id::{DefId, LocalDefId};
`
``
6
`+
use rustc_hir::intravisit;
`
``
7
`+
use rustc_middle::ty::{
`
``
8
`+
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
`
``
9
`+
};
`
``
10
`+
use rustc_span::Span;
`
``
11
+
``
12
`+
use crate::fluent_generated as fluent;
`
``
13
`+
use crate::{LateContext, LateLintPass};
`
``
14
+
``
15
`+
declare_lint! {
`
``
16
`+
/// UwU
`
``
17
`+
pub IMPL_TRAIT_OVERCAPTURES,
`
``
18
`+
Warn,
`
``
19
`+
"will capture more lifetimes than possibly intended in edition 2024",
`
``
20
`+
@future_incompatible = FutureIncompatibleInfo {
`
``
21
`+
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
`
``
22
`+
reference: "https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html",
`
``
23
`+
};
`
``
24
`+
}
`
``
25
+
``
26
`+
declare_lint_pass!(
`
``
27
`` +
/// Lint for use of async fn
in the definition of a publicly-reachable
``
``
28
`+
/// trait.
`
``
29
`+
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES]
`
``
30
`+
);
`
``
31
+
``
32
`+
impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
`
``
33
`+
fn check_fn(
`
``
34
`+
&mut self,
`
``
35
`+
cx: &LateContext<'tcx>,
`
``
36
`+
_: intravisit::FnKind<'tcx>,
`
``
37
`+
_: &'tcx hir::FnDecl<'tcx>,
`
``
38
`+
_: &'tcx hir::Body<'tcx>,
`
``
39
`+
_: Span,
`
``
40
`+
parent_def_id: LocalDefId,
`
``
41
`+
) {
`
``
42
`+
match cx.tcx.def_kind(parent_def_id) {
`
``
43
`+
DefKind::AssocFn => {
`
``
44
`+
// RPITITs already capture all lifetimes in scope, so skip them.
`
``
45
`+
if matches!(
`
``
46
`+
cx.tcx.def_kind(cx.tcx.local_parent(parent_def_id)),
`
``
47
`+
DefKind::Trait | DefKind::Impl { of_trait: true }
`
``
48
`+
) {
`
``
49
`+
return;
`
``
50
`+
}
`
``
51
`+
}
`
``
52
`+
DefKind::Fn => {
`
``
53
`+
// All freee functions need to check for overcaptures.
`
``
54
`+
}
`
``
55
`+
DefKind::Closure => return,
`
``
56
`+
kind => {
`
``
57
`+
unreachable!(
`
``
58
`+
"expected function item, found {}",
`
``
59
`+
kind.descr(parent_def_id.to_def_id())
`
``
60
`+
)
`
``
61
`+
}
`
``
62
`+
}
`
``
63
+
``
64
`+
let sig = cx.tcx.fn_sig(parent_def_id).instantiate_identity();
`
``
65
+
``
66
`+
let mut in_scope_parameters = FxIndexSet::default();
`
``
67
`+
let mut current_def_id = Some(parent_def_id.to_def_id());
`
``
68
`+
while let Some(def_id) = current_def_id {
`
``
69
`+
let generics = cx.tcx.generics_of(def_id);
`
``
70
`+
for param in &generics.params {
`
``
71
`+
in_scope_parameters.insert(param.def_id);
`
``
72
`+
}
`
``
73
`+
current_def_id = generics.parent;
`
``
74
`+
}
`
``
75
+
``
76
`+
sig.visit_with(&mut VisitOpaqueTypes {
`
``
77
`+
tcx: cx.tcx,
`
``
78
`+
parent_def_id,
`
``
79
`+
in_scope_parameters,
`
``
80
`+
seen: Default::default(),
`
``
81
`+
});
`
``
82
`+
}
`
``
83
`+
}
`
``
84
+
``
85
`+
struct VisitOpaqueTypes<'tcx> {
`
``
86
`+
tcx: TyCtxt<'tcx>,
`
``
87
`+
parent_def_id: LocalDefId,
`
``
88
`+
in_scope_parameters: FxIndexSet,
`
``
89
`+
seen: FxIndexSet,
`
``
90
`+
}
`
``
91
+
``
92
`+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
`
``
93
`+
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
`
``
94
`+
&mut self,
`
``
95
`+
t: &ty::Binder<'tcx, T>,
`
``
96
`+
) -> Self::Result {
`
``
97
`+
let mut added = vec![];
`
``
98
`+
for arg in t.bound_vars() {
`
``
99
`+
let arg: ty::BoundVariableKind = arg;
`
``
100
`+
match arg {
`
``
101
`+
ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) => {
`
``
102
`+
added.push(def_id);
`
``
103
`+
let unique = self.in_scope_parameters.insert(def_id);
`
``
104
`+
assert!(unique);
`
``
105
`+
}
`
``
106
`+
ty::BoundVariableKind::Ty(_) => {
`
``
107
`` +
todo!("we don't support late-bound type params in impl Trait
")
``
``
108
`+
}
`
``
109
`+
ty::BoundVariableKind::Region(..) => {
`
``
110
`+
unreachable!("all AST-derived bound regions should have a name")
`
``
111
`+
}
`
``
112
`+
ty::BoundVariableKind::Const => {
`
``
113
`+
unreachable!("non-lifetime binder consts are not allowed")
`
``
114
`+
}
`
``
115
`+
}
`
``
116
`+
}
`
``
117
+
``
118
`+
t.super_visit_with(self);
`
``
119
+
``
120
`+
for arg in added.into_iter().rev() {
`
``
121
`+
self.in_scope_parameters.shift_remove(&arg);
`
``
122
`+
}
`
``
123
`+
}
`
``
124
+
``
125
`+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
`
``
126
`+
if !t.has_opaque_types() {
`
``
127
`+
return;
`
``
128
`+
}
`
``
129
+
``
130
`+
if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
`
``
131
`+
&& let Some(opaque_def_id) = opaque_ty.def_id.as_local()
`
``
132
`+
&& self.seen.insert(opaque_def_id)
`
``
133
`+
&& let opaque =
`
``
134
`+
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
`
``
135
`+
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
`
``
136
`+
&& parent_def_id == self.parent_def_id
`
``
137
`+
&& opaque.precise_capturing_args.is_none()
`
``
138
`+
{
`
``
139
`+
let mut captured = UnordSet::default();
`
``
140
`+
let variances = self.tcx.variances_of(opaque_def_id);
`
``
141
`+
let mut current_def_id = Some(opaque_def_id.to_def_id());
`
``
142
`+
while let Some(def_id) = current_def_id {
`
``
143
`+
let generics = self.tcx.generics_of(def_id);
`
``
144
`+
for param in &generics.params {
`
``
145
`+
if variances[param.index as usize] != ty::Invariant {
`
``
146
`+
continue;
`
``
147
`+
}
`
``
148
`+
captured.insert(extract_def_id_from_arg(
`
``
149
`+
self.tcx,
`
``
150
`+
generics,
`
``
151
`+
opaque_ty.args[param.index as usize],
`
``
152
`+
));
`
``
153
`+
}
`
``
154
`+
current_def_id = generics.parent;
`
``
155
`+
}
`
``
156
+
``
157
`+
let uncaptured_spans: Vec<_> = self
`
``
158
`+
.in_scope_parameters
`
``
159
`+
.iter()
`
``
160
`+
.filter(|def_id| !captured.contains(def_id))
`
``
161
`+
.map(|def_id| self.tcx.def_span(def_id))
`
``
162
`+
.collect();
`
``
163
+
``
164
`+
if !uncaptured_spans.is_empty() {
`
``
165
`+
self.tcx.emit_node_lint(
`
``
166
`+
IMPL_TRAIT_OVERCAPTURES,
`
``
167
`+
self.tcx.local_def_id_to_hir_id(opaque_def_id),
`
``
168
`+
ImplTraitOvercapturesLint {
`
``
169
`+
opaque_span: self.tcx.def_span(opaque_def_id),
`
``
170
`+
self_ty: t,
`
``
171
`+
num_captured: uncaptured_spans.len(),
`
``
172
`+
uncaptured_spans,
`
``
173
`+
},
`
``
174
`+
);
`
``
175
`+
}
`
``
176
+
``
177
`+
for clause in
`
``
178
`+
self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args)
`
``
179
`+
{
`
``
180
`+
clause.visit_with(self)
`
``
181
`+
}
`
``
182
`+
}
`
``
183
+
``
184
`+
t.super_visit_with(self);
`
``
185
`+
}
`
``
186
`+
}
`
``
187
+
``
188
`+
struct ImplTraitOvercapturesLint<'tcx> {
`
``
189
`+
opaque_span: Span,
`
``
190
`+
uncaptured_spans: Vec,
`
``
191
`+
self_ty: Ty<'tcx>,
`
``
192
`+
num_captured: usize,
`
``
193
`+
}
`
``
194
+
``
195
`+
impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
`
``
196
`+
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
`
``
197
`+
diag.arg("self_ty", self.self_ty.to_string())
`
``
198
`+
.arg("num_captured", self.num_captured)
`
``
199
`+
.span(self.opaque_span)
`
``
200
`+
.span_note(self.uncaptured_spans, fluent::lint_note)
`
``
201
`+
.note(fluent::lint_note2);
`
``
202
`+
}
`
``
203
+
``
204
`+
fn msg(&self) -> rustc_errors::DiagMessage {
`
``
205
`+
fluent::lint_impl_trait_overcaptures
`
``
206
`+
}
`
``
207
`+
}
`
``
208
+
``
209
`+
fn extract_def_id_from_arg<'tcx>(
`
``
210
`+
tcx: TyCtxt<'tcx>,
`
``
211
`+
generics: &'tcx ty::Generics,
`
``
212
`+
arg: ty::GenericArg<'tcx>,
`
``
213
`+
) -> DefId {
`
``
214
`+
match arg.unpack() {
`
``
215
`+
ty::GenericArgKind::Lifetime(re) => match *re {
`
``
216
`+
ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
`
``
217
`+
ty::ReBound(
`
``
218
`+
_,
`
``
219
`+
ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. },
`
``
220
`+
) => def_id,
`
``
221
`+
_ => unreachable!(),
`
``
222
`+
},
`
``
223
`+
ty::GenericArgKind::Type(ty) => {
`
``
224
`+
let ty::Param(param_ty) = *ty.kind() else {
`
``
225
`+
bug!();
`
``
226
`+
};
`
``
227
`+
generics.type_param(param_ty, tcx).def_id
`
``
228
`+
}
`
``
229
`+
ty::GenericArgKind::Const(ct) => {
`
``
230
`+
let ty::ConstKind::Param(param_ct) = ct.kind() else {
`
``
231
`+
bug!();
`
``
232
`+
};
`
``
233
`+
generics.const_param(param_ct, tcx).def_id
`
``
234
`+
}
`
``
235
`+
}
`
``
236
`+
}
`