Auto merge of #122747 - Urgau:non-local-defs_perfect_impl, r= · rust-lang/rust@c5a16cd (original) (raw)
1
``
`-
use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind};
`
``
1
`+
use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind};
`
``
2
`+
use rustc_hir::{Path, QPath};
`
``
3
`+
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
`
``
4
`+
use rustc_infer::infer::InferCtxt;
`
``
5
`+
use rustc_infer::traits::{Obligation, ObligationCause};
`
``
6
`+
use rustc_middle::query::Key;
`
``
7
`+
use rustc_middle::ty::TypeSuperFoldable;
`
``
8
`+
use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable, TypeFolder};
`
2
9
`use rustc_span::def_id::{DefId, LOCAL_CRATE};
`
``
10
`+
use rustc_span::Span;
`
3
11
`use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};
`
``
12
`+
use rustc_trait_selection::infer::TyCtxtInferExt;
`
``
13
`+
use rustc_trait_selection::traits::error_reporting::ambiguity::{
`
``
14
`+
compute_applicable_impls_for_diagnostics, Ambiguity,
`
``
15
`+
};
`
4
16
``
5
17
`use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
`
6
18
`use crate::{LateContext, LateLintPass, LintContext};
`
`@@ -35,7 +47,7 @@ declare_lint! {
`
35
47
`/// All nested bodies (functions, enum discriminant, array length, consts) (expect for
`
36
48
`` /// const _: Ty = { ... }
in top-level module, which is still undecided) are checked.
``
37
49
`pub NON_LOCAL_DEFINITIONS,
`
38
``
`-
Allow,
`
``
50
`+
Warn,
`
39
51
`"checks for non-local definitions",
`
40
52
` report_in_external_macro
`
41
53
`}
`
`@@ -66,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
`
66
78
`return;
`
67
79
`}
`
68
80
``
69
``
`-
let parent = cx.tcx.parent(item.owner_id.def_id.into());
`
``
81
`+
let def_id = item.owner_id.def_id.into();
`
``
82
+
``
83
`+
let parent = cx.tcx.parent(def_id);
`
70
84
`let parent_def_kind = cx.tcx.def_kind(parent);
`
71
85
`let parent_opt_item_name = cx.tcx.opt_item_name(parent);
`
72
86
``
`@@ -155,9 +169,54 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
`
155
169
`.map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent))
`
156
170
`.unwrap_or(false);
`
157
171
``
158
``
`-
// If none of them have a local parent (LOGICAL NOR) this means that
`
159
``
`-
// this impl definition is a non-local definition and so we lint on it.
`
160
``
`-
if !(self_ty_has_local_parent || of_trait_has_local_parent) {
`
``
172
`+
// Detecting if the impl definition is leaking outside of it's defining scope.
`
``
173
`+
//
`
``
174
`+
// Rule: for each impl, instantiate all local types with inference vars and
`
``
175
`+
// then assemble candidates for that goal, if there are more than 1 (non-private
`
``
176
`+
// impls), it does not leak.
`
``
177
`+
//
`
``
178
`+
// https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895
`
``
179
`+
let impl_is_not_leaky = cx
`
``
180
`+
.tcx
`
``
181
`+
.impl_trait_ref(def_id)
`
``
182
`+
.map(|binder| {
`
``
183
`+
let infcx = cx.tcx.infer_ctxt().build();
`
``
184
`+
let trait_ref = binder
`
``
185
`+
.instantiate(cx.tcx, infcx.fresh_args_for_item(item.span, def_id));
`
``
186
+
``
187
`+
let trait_ref = trait_ref.fold_with(&mut LocalTypeInferenceFolder {
`
``
188
`+
infcx: &infcx,
`
``
189
`+
to_ignore: 1,
`
``
190
`+
impl_parent: parent,
`
``
191
`+
impl_parent_parent: parent_parent,
`
``
192
`+
span: item.span,
`
``
193
`+
});
`
``
194
+
``
195
`+
let poly_trait_obligation = Obligation::new(
`
``
196
`+
cx.tcx,
`
``
197
`+
ObligationCause::dummy(),
`
``
198
`+
ty::ParamEnv::empty(),
`
``
199
`+
Binder::dummy(trait_ref),
`
``
200
`+
);
`
``
201
+
``
202
`+
let ambiguities = compute_applicable_impls_for_diagnostics(
`
``
203
`+
&infcx,
`
``
204
`+
&poly_trait_obligation,
`
``
205
`+
);
`
``
206
+
``
207
`+
let mut it = ambiguities.iter().filter(|ambi| match ambi {
`
``
208
`+
Ambiguity::DefId(did) => {
`
``
209
`+
!did_has_local_parent(*did, cx.tcx, parent, parent_parent)
`
``
210
`+
}
`
``
211
`+
Ambiguity::ParamEnv(_) => false,
`
``
212
`+
});
`
``
213
+
``
214
`+
let _ = it.next();
`
``
215
`+
it.next().is_some()
`
``
216
`+
})
`
``
217
`+
.unwrap_or(false);
`
``
218
+
``
219
`+
if !(self_ty_has_local_parent || of_trait_has_local_parent || impl_is_not_leaky) {
`
161
220
`let const_anon = if self.body_depth == 1
`
162
221
` && parent_def_kind == DefKind::Const
`
163
222
` && parent_opt_item_name != Some(kw::Underscore)
`
`@@ -207,6 +266,47 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
`
207
266
`}
`
208
267
`}
`
209
268
``
``
269
`+
/// Replace every local type by inference variable.
`
``
270
`+
///
`
``
271
/// ```text
``
272
`+
/// <Global as std::cmp::PartialEq<Global>>
`
``
273
`+
/// to
`
``
274
`+
/// <Global<_> as std::cmp::PartialEq<Global<_>>>
`
``
275
/// ```
``
276
`+
struct LocalTypeInferenceFolder<'a, 'tcx> {
`
``
277
`+
infcx: &'a InferCtxt<'tcx>,
`
``
278
`+
to_ignore: u32,
`
``
279
`+
impl_parent: DefId,
`
``
280
`+
impl_parent_parent: Option,
`
``
281
`+
span: Span,
`
``
282
`+
}
`
``
283
+
``
284
`+
impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for LocalTypeInferenceFolder<'a, 'tcx> {
`
``
285
`+
fn interner(&self) -> TyCtxt<'tcx> {
`
``
286
`+
self.infcx.tcx
`
``
287
`+
}
`
``
288
+
``
289
`+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
`
``
290
`+
if let Some(ty_did) = t.ty_def_id()
`
``
291
`+
&& did_has_local_parent(
`
``
292
`+
ty_did,
`
``
293
`+
self.infcx.tcx,
`
``
294
`+
self.impl_parent,
`
``
295
`+
self.impl_parent_parent,
`
``
296
`+
)
`
``
297
`+
&& self.to_ignore == 0
`
``
298
`+
{
`
``
299
`+
self.infcx.next_ty_var(TypeVariableOrigin {
`
``
300
`+
kind: TypeVariableOriginKind::TypeInference,
`
``
301
`+
span: self.span,
`
``
302
`+
})
`
``
303
`+
} else {
`
``
304
`+
self.to_ignore = self.to_ignore.saturating_sub(1);
`
``
305
`+
t.super_fold_with(self)
`
``
306
`+
}
`
``
307
`+
}
`
``
308
`+
}
`
``
309
+
210
310
`/// Given a path and a parent impl def id, this checks if the if parent resolution
`
211
311
`/// def id correspond to the def id of the parent impl definition.
`
212
312
`///
`
`@@ -216,16 +316,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
`
216
316
`/// std::convert::PartialEq<Foo>
`
217
317
`/// ^^^^^^^^^^^^^^^^^^^^^^^
`
218
318
```` /// ```
````
``
319
`+
#[inline]
`
219
320
`fn path_has_local_parent(
`
220
321
`path: &Path<'_>,
`
221
322
`cx: &LateContext<'_>,
`
222
323
`impl_parent: DefId,
`
223
324
`impl_parent_parent: Option,
`
224
325
`) -> bool {
`
225
``
`-
path.res.opt_def_id().is_some_and(|did| {
`
226
``
`-
did.is_local() && {
`
227
``
`-
let res_parent = cx.tcx.parent(did);
`
228
``
`-
res_parent == impl_parent || Some(res_parent) == impl_parent_parent
`
229
``
`-
}
`
230
``
`-
})
`
``
326
`+
path.res
`
``
327
`+
.opt_def_id()
`
``
328
`+
.is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, impl_parent_parent))
`
``
329
`+
}
`
``
330
+
``
331
`+
/// Given a def id and a parent impl def id, this checks if the parent
`
``
332
`+
/// def id correspond to the def id of the parent impl definition.
`
``
333
`+
#[inline]
`
``
334
`+
fn did_has_local_parent(
`
``
335
`+
did: DefId,
`
``
336
`+
tcx: TyCtxt<'_>,
`
``
337
`+
impl_parent: DefId,
`
``
338
`+
impl_parent_parent: Option,
`
``
339
`+
) -> bool {
`
``
340
`+
did.is_local() && {
`
``
341
`+
let res_parent = tcx.parent(did);
`
``
342
`+
res_parent == impl_parent || Some(res_parent) == impl_parent_parent
`
``
343
`+
}
`
231
344
`}
`