Rollup merge of #131498 - Urgau:transparent-const-anons, r=lcnr
Consider outermost const-anon in `non_local_def` lint This PR change the logic for finding the parent of the `impl` definition in the `non_local_definitions` lint to consider multiple level of const-anon items, instead of only one currently. I also took the opportunity to cleanup the related code. cc ``@traviscross`` Fixes https://github.com/rust-lang/rust/issues/131474
This commit is contained in:
commit
e00f49db17
@ -124,16 +124,6 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
|||||||
// If that's the case this means that this impl block declaration
|
// If that's the case this means that this impl block declaration
|
||||||
// is using local items and so we don't lint on it.
|
// is using local items and so we don't lint on it.
|
||||||
|
|
||||||
// We also ignore anon-const in item by including the anon-const
|
|
||||||
// parent as well.
|
|
||||||
let parent_parent = if parent_def_kind == DefKind::Const
|
|
||||||
&& parent_opt_item_name == Some(kw::Underscore)
|
|
||||||
{
|
|
||||||
Some(cx.tcx.parent(parent))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
|
// 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
|
||||||
// of the `impl` definition
|
// of the `impl` definition
|
||||||
let mut collector = PathCollector { paths: Vec::new() };
|
let mut collector = PathCollector { paths: Vec::new() };
|
||||||
@ -148,13 +138,33 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
|||||||
|p| matches!(p.res, Res::Def(def_kind, _) if def_kind != DefKind::TyParam),
|
|p| matches!(p.res, Res::Def(def_kind, _) if def_kind != DefKind::TyParam),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. We check if any of path reference a "local" parent and if that the case
|
// 1.9. We retrieve the parent def id of the impl item, ...
|
||||||
// we bail out as asked by T-lang, even though this isn't correct from a
|
//
|
||||||
// type-system point of view, as inference exists and could still leak the impl.
|
// ... modulo const-anons items, for enhanced compatibility with the ecosystem
|
||||||
|
// as that pattern is common with `serde`, `bevy`, ...
|
||||||
|
//
|
||||||
|
// For this example we want the `DefId` parent of the outermost const-anon items.
|
||||||
|
// ```
|
||||||
|
// const _: () = { // the parent of this const-anon
|
||||||
|
// const _: () = {
|
||||||
|
// impl Foo {}
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
|
let outermost_impl_parent = peel_parent_while(cx.tcx, parent, |tcx, did| {
|
||||||
|
tcx.def_kind(did) == DefKind::Const
|
||||||
|
&& tcx.opt_item_name(did) == Some(kw::Underscore)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. We check if any of the paths reference a the `impl`-parent.
|
||||||
|
//
|
||||||
|
// If that the case we bail out, as was asked by T-lang, even though this isn't
|
||||||
|
// correct from a type-system point of view, as inference exists and one-impl-rule
|
||||||
|
// make its so that we could still leak the impl.
|
||||||
if collector
|
if collector
|
||||||
.paths
|
.paths
|
||||||
.iter()
|
.iter()
|
||||||
.any(|path| path_has_local_parent(path, cx, parent, parent_parent))
|
.any(|path| path_has_local_parent(path, cx, parent, outermost_impl_parent))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -253,8 +263,8 @@ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a path and a parent impl def id, this checks if the if parent resolution
|
/// Given a path, this checks if the if the parent resolution def id corresponds to
|
||||||
/// def id correspond to the def id of the parent impl definition.
|
/// the def id of the parent impl definition (the direct one and the outermost one).
|
||||||
///
|
///
|
||||||
/// Given this path, we will look at the path (and ignore any generic args):
|
/// Given this path, we will look at the path (and ignore any generic args):
|
||||||
///
|
///
|
||||||
@ -267,32 +277,50 @@ fn path_has_local_parent(
|
|||||||
path: &Path<'_>,
|
path: &Path<'_>,
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
impl_parent: DefId,
|
impl_parent: DefId,
|
||||||
impl_parent_parent: Option<DefId>,
|
outermost_impl_parent: Option<DefId>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
path.res
|
path.res
|
||||||
.opt_def_id()
|
.opt_def_id()
|
||||||
.is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, impl_parent_parent))
|
.is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, outermost_impl_parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a def id and a parent impl def id, this checks if the parent
|
/// Given a def id this checks if the parent def id (modulo modules) correspond to
|
||||||
/// def id (modulo modules) correspond to the def id of the parent impl definition.
|
/// the def id of the parent impl definition (the direct one and the outermost one).
|
||||||
#[inline]
|
#[inline]
|
||||||
fn did_has_local_parent(
|
fn did_has_local_parent(
|
||||||
did: DefId,
|
did: DefId,
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
impl_parent: DefId,
|
impl_parent: DefId,
|
||||||
impl_parent_parent: Option<DefId>,
|
outermost_impl_parent: Option<DefId>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
did.is_local()
|
if !did.is_local() {
|
||||||
&& if let Some(did_parent) = tcx.opt_parent(did) {
|
return false;
|
||||||
did_parent == impl_parent
|
|
||||||
|| Some(did_parent) == impl_parent_parent
|
|
||||||
|| !did_parent.is_crate_root()
|
|
||||||
&& tcx.def_kind(did_parent) == DefKind::Mod
|
|
||||||
&& did_has_local_parent(did_parent, tcx, impl_parent, impl_parent_parent)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Some(parent_did) = tcx.opt_parent(did) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
peel_parent_while(tcx, parent_did, |tcx, did| tcx.def_kind(did) == DefKind::Mod)
|
||||||
|
.map(|parent_did| parent_did == impl_parent || Some(parent_did) == outermost_impl_parent)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a `DefId` checks if it satisfies `f` if it does check with it's parent and continue
|
||||||
|
/// until it doesn't satisfies `f` and return the last `DefId` checked.
|
||||||
|
///
|
||||||
|
/// In other word this method return the first `DefId` that doesn't satisfies `f`.
|
||||||
|
#[inline]
|
||||||
|
fn peel_parent_while(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
mut did: DefId,
|
||||||
|
mut f: impl FnMut(TyCtxt<'_>, DefId) -> bool,
|
||||||
|
) -> Option<DefId> {
|
||||||
|
while !did.is_crate_root() && f(tcx, did) {
|
||||||
|
did = tcx.opt_parent(did).filter(|parent_did| parent_did.is_local())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(did)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return for a given `Path` the span until the last args
|
/// Return for a given `Path` the span until the last args
|
||||||
|
@ -63,6 +63,13 @@ fn foo2() {}
|
|||||||
|
|
||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
impl Test {}
|
||||||
|
//~^ WARN non-local `impl` definition
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Uto9 {}
|
trait Uto9 {}
|
||||||
|
@ -95,7 +95,21 @@ LL | impl Test {
|
|||||||
= note: 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>
|
= note: 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>
|
||||||
|
|
||||||
warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
||||||
--> $DIR/consts.rs:72:9
|
--> $DIR/consts.rs:69:13
|
||||||
|
|
|
||||||
|
LL | const _: () = {
|
||||||
|
| ----------- move the `impl` block outside of this constant `_` and up 3 bodies
|
||||||
|
LL | impl Test {}
|
||||||
|
| ^^^^^----
|
||||||
|
| |
|
||||||
|
| `Test` is not local
|
||||||
|
|
|
||||||
|
= note: 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`
|
||||||
|
= note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint
|
||||||
|
= note: 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>
|
||||||
|
|
||||||
|
warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
||||||
|
--> $DIR/consts.rs:79:9
|
||||||
|
|
|
|
||||||
LL | let _a = || {
|
LL | let _a = || {
|
||||||
| -- move the `impl` block outside of this closure `<unnameable>` and up 2 bodies
|
| -- move the `impl` block outside of this closure `<unnameable>` and up 2 bodies
|
||||||
@ -109,7 +123,7 @@ LL | impl Uto9 for Test {}
|
|||||||
= note: 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>
|
= note: 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>
|
||||||
|
|
||||||
warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
||||||
--> $DIR/consts.rs:79:9
|
--> $DIR/consts.rs:86:9
|
||||||
|
|
|
|
||||||
LL | type A = [u32; {
|
LL | type A = [u32; {
|
||||||
| ____________________-
|
| ____________________-
|
||||||
@ -126,5 +140,5 @@ LL | | }];
|
|||||||
= note: 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`
|
= note: 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`
|
||||||
= note: 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>
|
= note: 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>
|
||||||
|
|
||||||
warning: 8 warnings emitted
|
warning: 9 warnings emitted
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
// This test check that no matter the nesting of const-anons and modules
|
||||||
|
// we consider them as transparent.
|
||||||
|
//
|
||||||
|
// Similar to https://github.com/rust-lang/rust/issues/131474
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
pub mod tmp {
|
||||||
|
pub mod tmp {
|
||||||
|
pub struct Test;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
impl tmp::tmp::Test {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
mod tmp {
|
||||||
|
pub(super) struct InnerTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl tmp::InnerTest {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {}
|
24
tests/ui/lint/non-local-defs/convoluted-locals-131474.rs
Normal file
24
tests/ui/lint/non-local-defs/convoluted-locals-131474.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// This test check that no matter the nesting of const-anons we consider
|
||||||
|
// them as transparent.
|
||||||
|
//
|
||||||
|
// https://github.com/rust-lang/rust/issues/131474
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
pub struct Test;
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
impl Test {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
const _: () = {
|
||||||
|
struct InnerTest;
|
||||||
|
|
||||||
|
impl InnerTest {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {}
|
@ -51,3 +51,10 @@ fn bitflags() {
|
|||||||
impl Flags {}
|
impl Flags {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bitflags_internal() {
|
||||||
|
const _: () = {
|
||||||
|
struct InternalFlags;
|
||||||
|
impl InternalFlags {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user