Skip to content

Commit 00a6ebf

Browse files
committed
Rework non_local_definitions lint to only be a syntactic heuristic
1 parent cb58668 commit 00a6ebf

13 files changed

+36
-643
lines changed

compiler/rustc_lint/messages.ftl

-3
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,6 @@ lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may
592592
lint_non_local_definitions_deprecation = this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
593593
594594
lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks should be written at the same level as their item
595-
.remove_help = remove `{$may_remove_part}` to make the `impl` local
596595
.without_trait = methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl`
597596
.with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl`
598597
.bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type
@@ -617,8 +616,6 @@ lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, `#
617616
remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}`
618617
.non_local = a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute
619618
620-
lint_non_local_definitions_may_move = may need to be moved as well
621-
622619
lint_non_local_definitions_of_trait_not_local = `{$of_trait_str}` is not local
623620
624621
lint_non_local_definitions_self_ty_not_local = `{$self_ty_str}` is not local

compiler/rustc_lint/src/lints.rs

-21
Original file line numberDiff line numberDiff line change
@@ -1375,9 +1375,7 @@ pub(crate) enum NonLocalDefinitionsDiag {
13751375
body_name: String,
13761376
cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
13771377
const_anon: Option<Option<Span>>,
1378-
move_to: Option<(Span, Vec<Span>)>,
13791378
doctest: bool,
1380-
may_remove: Option<(Span, String)>,
13811379
has_trait: bool,
13821380
self_ty_str: String,
13831381
of_trait_str: Option<String>,
@@ -1401,9 +1399,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
14011399
body_name,
14021400
cargo_update,
14031401
const_anon,
1404-
move_to,
14051402
doctest,
1406-
may_remove,
14071403
has_trait,
14081404
self_ty_str,
14091405
of_trait_str,
@@ -1434,27 +1430,10 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
14341430
diag.note(fluent::lint_without_trait);
14351431
}
14361432

1437-
if let Some((move_help, may_move)) = move_to {
1438-
let mut ms = MultiSpan::from_span(move_help);
1439-
for sp in may_move {
1440-
ms.push_span_label(sp, fluent::lint_non_local_definitions_may_move);
1441-
}
1442-
diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help);
1443-
}
14441433
if doctest {
14451434
diag.help(fluent::lint_doctest);
14461435
}
14471436

1448-
if let Some((span, part)) = may_remove {
1449-
diag.arg("may_remove_part", part);
1450-
diag.span_suggestion(
1451-
span,
1452-
fluent::lint_remove_help,
1453-
"",
1454-
Applicability::MaybeIncorrect,
1455-
);
1456-
}
1457-
14581437
if let Some(const_anon) = const_anon {
14591438
diag.note(fluent::lint_exception);
14601439
if let Some(const_anon) = const_anon {

compiler/rustc_lint/src/non_local_def.rs

+24-215
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,11 @@ use rustc_errors::MultiSpan;
22
use rustc_hir::def::DefKind;
33
use rustc_hir::intravisit::{self, Visitor};
44
use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, QPath, TyKind};
5-
use rustc_infer::infer::InferCtxt;
6-
use rustc_infer::traits::{Obligation, ObligationCause};
7-
use rustc_middle::ty::{
8-
self, Binder, EarlyBinder, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
9-
};
5+
use rustc_middle::ty::TyCtxt;
106
use rustc_session::{declare_lint, impl_lint_pass};
117
use rustc_span::def_id::{DefId, LOCAL_CRATE};
128
use rustc_span::symbol::kw;
13-
use rustc_span::{ExpnKind, MacroKind, Span, Symbol, sym};
14-
use rustc_trait_selection::error_reporting::traits::ambiguity::{
15-
CandidateSource, compute_applicable_impls_for_diagnostics,
16-
};
17-
use rustc_trait_selection::infer::TyCtxtInferExt;
9+
use rustc_span::{ExpnKind, MacroKind, Span, Symbol, sym, sym};
1810

1911
use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
2012
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
@@ -142,42 +134,22 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
142134
None
143135
};
144136

145-
// Part 1: Is the Self type local?
146-
let self_ty_has_local_parent =
147-
ty_has_local_parent(&impl_.self_ty.kind, cx, parent, parent_parent);
148-
149-
if self_ty_has_local_parent {
150-
return;
151-
}
152-
153-
// Part 2: Is the Trait local?
154-
let of_trait_has_local_parent = impl_
155-
.of_trait
156-
.map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent))
157-
.unwrap_or(false);
158-
159-
if of_trait_has_local_parent {
160-
return;
137+
// 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
138+
// of the `impl` definition
139+
let mut collector = PathCollector { paths: Vec::new() };
140+
collector.visit_ty(&impl_.self_ty);
141+
if let Some(of_trait) = &impl_.of_trait {
142+
collector.visit_trait_ref(of_trait);
161143
}
162144

163-
// Part 3: Is the impl definition leaking outside it's defining scope?
164-
//
165-
// We always consider inherent impls to be leaking.
166-
let impl_has_enough_non_local_candidates = cx
167-
.tcx
168-
.impl_trait_ref(def_id)
169-
.map(|binder| {
170-
impl_trait_ref_has_enough_non_local_candidates(
171-
cx.tcx,
172-
item.span,
173-
def_id,
174-
binder,
175-
|did| did_has_local_parent(did, cx.tcx, parent, parent_parent),
176-
)
177-
})
178-
.unwrap_or(false);
179-
180-
if impl_has_enough_non_local_candidates {
145+
// 2. We check if any of path reference a "local" parent and if that the case
146+
// we bail out as asked by T-lang, even though this isn't correct from a
147+
// type-system point of view, as inference exists and could still leak the impl.
148+
if collector
149+
.paths
150+
.iter()
151+
.any(|path| path_has_local_parent(path, cx, parent, parent_parent))
152+
{
181153
return;
182154
}
183155

@@ -199,18 +171,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
199171
let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. })
200172
.then_some(span_for_const_anon_suggestion);
201173

202-
let may_remove = match &impl_.self_ty.kind {
203-
TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty)
204-
if ty_has_local_parent(&mut_ty.ty.kind, cx, parent, parent_parent) =>
205-
{
206-
let type_ =
207-
if matches!(impl_.self_ty.kind, TyKind::Ptr(_)) { "*" } else { "&" };
208-
let part = format!("{}{}", type_, mut_ty.mutbl.prefix_str());
209-
Some((impl_.self_ty.span.shrink_to_lo().until(mut_ty.ty.span), part))
210-
}
211-
_ => None,
212-
};
213-
214174
let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span);
215175
let mut ms = MultiSpan::from_span(impl_span);
216176

@@ -221,6 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
221181
self_ty_span,
222182
fluent::lint_non_local_definitions_self_ty_not_local,
223183
);
184+
224185
let of_trait_str = if let Some(of_trait) = &impl_.of_trait {
225186
ms.push_span_label(
226187
path_span_without_args(&of_trait.path),
@@ -231,44 +192,14 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
231192
None
232193
};
233194

234-
let (doctest, move_to) = if is_at_toplevel_doctest() {
235-
(true, None)
236-
} else {
237-
let mut collector = PathCollector { paths: Vec::new() };
238-
collector.visit_ty(&impl_.self_ty);
239-
if let Some(of_trait) = &impl_.of_trait {
240-
collector.visit_trait_ref(of_trait);
241-
}
242-
collector.visit_generics(&impl_.generics);
243-
244-
let mut may_move: Vec<Span> = collector
245-
.paths
246-
.into_iter()
247-
.filter_map(|path| {
248-
if let Some(did) = path.res.opt_def_id()
249-
&& did_has_local_parent(did, cx.tcx, parent, parent_parent)
250-
{
251-
Some(cx.tcx.def_span(did))
252-
} else {
253-
None
254-
}
255-
})
256-
.collect();
257-
may_move.sort();
258-
may_move.dedup();
259-
260-
let move_to = if may_move.is_empty() {
261-
ms.push_span_label(
262-
cx.tcx.def_span(parent),
263-
fluent::lint_non_local_definitions_impl_move_help,
264-
);
265-
None
266-
} else {
267-
Some((cx.tcx.def_span(parent), may_move))
268-
};
195+
let doctest = is_at_toplevel_doctest();
269196

270-
(false, move_to)
271-
};
197+
if !doctest {
198+
ms.push_span_label(
199+
cx.tcx.def_span(parent),
200+
fluent::lint_non_local_definitions_impl_move_help,
201+
);
202+
}
272203

273204
let macro_to_change =
274205
if let ExpnKind::Macro(kind, name) = item.span.ctxt().outer_expn_data().kind {
@@ -287,9 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
287218
const_anon,
288219
self_ty_str,
289220
of_trait_str,
290-
move_to,
291221
doctest,
292-
may_remove,
293222
has_trait: impl_.of_trait.is_some(),
294223
macro_to_change,
295224
})
@@ -316,90 +245,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
316245
}
317246
}
318247

319-
// Detecting if the impl definition is leaking outside of its defining scope.
320-
//
321-
// Rule: for each impl, instantiate all local types with inference vars and
322-
// then assemble candidates for that goal, if there are more than 1 (non-private
323-
// impls), it does not leak.
324-
//
325-
// https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895
326-
fn impl_trait_ref_has_enough_non_local_candidates<'tcx>(
327-
tcx: TyCtxt<'tcx>,
328-
infer_span: Span,
329-
trait_def_id: DefId,
330-
binder: EarlyBinder<'tcx, TraitRef<'tcx>>,
331-
mut did_has_local_parent: impl FnMut(DefId) -> bool,
332-
) -> bool {
333-
let infcx = tcx
334-
.infer_ctxt()
335-
// We use the new trait solver since the obligation we are trying to
336-
// prove here may overflow and those are fatal in the old trait solver.
337-
// Which is unacceptable for a lint.
338-
//
339-
// Thanksfully the part we use here are very similar to the
340-
// new-trait-solver-as-coherence, which is in stabilization.
341-
//
342-
// https://github.com/rust-lang/rust/issues/123573
343-
.with_next_trait_solver(true)
344-
.build();
345-
346-
let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id));
347-
348-
let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer {
349-
infcx: &infcx,
350-
infer_span,
351-
did_has_local_parent: &mut did_has_local_parent,
352-
});
353-
354-
let poly_trait_obligation = Obligation::new(
355-
tcx,
356-
ObligationCause::dummy(),
357-
ty::ParamEnv::empty(),
358-
Binder::dummy(trait_ref),
359-
);
360-
361-
let ambiguities = compute_applicable_impls_for_diagnostics(&infcx, &poly_trait_obligation);
362-
363-
let mut it = ambiguities.iter().filter(|ambi| match ambi {
364-
CandidateSource::DefId(did) => !did_has_local_parent(*did),
365-
CandidateSource::ParamEnv(_) => unreachable!(),
366-
});
367-
368-
let _ = it.next();
369-
it.next().is_some()
370-
}
371-
372-
/// Replace every local type by inference variable.
373-
///
374-
/// ```text
375-
/// <Global<Local> as std::cmp::PartialEq<Global<Local>>>
376-
/// to
377-
/// <Global<_> as std::cmp::PartialEq<Global<_>>>
378-
/// ```
379-
struct ReplaceLocalTypesWithInfer<'a, 'tcx, F: FnMut(DefId) -> bool> {
380-
infcx: &'a InferCtxt<'tcx>,
381-
did_has_local_parent: F,
382-
infer_span: Span,
383-
}
384-
385-
impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder<TyCtxt<'tcx>>
386-
for ReplaceLocalTypesWithInfer<'a, 'tcx, F>
387-
{
388-
fn cx(&self) -> TyCtxt<'tcx> {
389-
self.infcx.tcx
390-
}
391-
392-
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
393-
if let Some(def) = t.ty_adt_def()
394-
&& (self.did_has_local_parent)(def.did())
395-
{
396-
self.infcx.next_ty_var(self.infer_span)
397-
} else {
398-
t.super_fold_with(self)
399-
}
400-
}
401-
}
402-
403248
/// Simple hir::Path collector
404249
struct PathCollector<'tcx> {
405250
paths: Vec<Path<'tcx>>,
@@ -412,42 +257,6 @@ impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
412257
}
413258
}
414259

415-
/// Given a `Ty` we check if the (outermost) type is local.
416-
fn ty_has_local_parent(
417-
ty_kind: &TyKind<'_>,
418-
cx: &LateContext<'_>,
419-
impl_parent: DefId,
420-
impl_parent_parent: Option<DefId>,
421-
) -> bool {
422-
match ty_kind {
423-
TyKind::Path(QPath::Resolved(_, ty_path)) => {
424-
path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
425-
}
426-
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
427-
principle_poly_trait_ref.0.trait_ref.path,
428-
cx,
429-
impl_parent,
430-
impl_parent_parent,
431-
),
432-
TyKind::TraitObject([], _, _)
433-
| TyKind::InferDelegation(_, _)
434-
| TyKind::Slice(_)
435-
| TyKind::Array(_, _)
436-
| TyKind::Ptr(_)
437-
| TyKind::Ref(_, _)
438-
| TyKind::BareFn(_)
439-
| TyKind::Never
440-
| TyKind::Tup(_)
441-
| TyKind::Path(_)
442-
| TyKind::Pat(..)
443-
| TyKind::AnonAdt(_)
444-
| TyKind::OpaqueDef(_, _, _)
445-
| TyKind::Typeof(_)
446-
| TyKind::Infer
447-
| TyKind::Err(_) => false,
448-
}
449-
}
450-
451260
/// Given a path and a parent impl def id, this checks if the if parent resolution
452261
/// def id correspond to the def id of the parent impl definition.
453262
///

tests/ui/lint/non-local-defs/exhaustive.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,13 @@ fn main() {
5555

5656
struct InsideMain;
5757

58+
impl Trait for &InsideMain {}
5859
impl Trait for *mut InsideMain {}
59-
//~^ WARN non-local `impl` definition
6060
impl Trait for *mut [InsideMain] {}
61-
//~^ WARN non-local `impl` definition
6261
impl Trait for [InsideMain; 8] {}
63-
//~^ WARN non-local `impl` definition
6462
impl Trait for (InsideMain,) {}
65-
//~^ WARN non-local `impl` definition
6663
impl Trait for fn(InsideMain) -> () {}
67-
//~^ WARN non-local `impl` definition
6864
impl Trait for fn() -> InsideMain {}
69-
//~^ WARN non-local `impl` definition
7065

7166
fn inside_inside() {
7267
impl Display for InsideMain {

0 commit comments

Comments
 (0)