@@ -124,16 +124,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
124124 // If that's the case this means that this impl block declaration
125125 // is using local items and so we don't lint on it.
126126
127- // We also ignore anon-const in item by including the anon-const
128- // parent as well.
129- let parent_parent = if parent_def_kind == DefKind :: Const
130- && parent_opt_item_name == Some ( kw:: Underscore )
131- {
132- Some ( cx. tcx . parent ( parent) )
133- } else {
134- None
135- } ;
136-
137127 // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
138128 // of the `impl` definition
139129 let mut collector = PathCollector { paths : Vec :: new ( ) } ;
@@ -148,13 +138,33 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
148138 |p| matches ! ( p. res, Res :: Def ( def_kind, _) if def_kind != DefKind :: TyParam ) ,
149139 ) ;
150140
151- // 2. We check if any of path reference a "local" parent and if that the case
152- // we bail out as asked by T-lang, even though this isn't correct from a
153- // type-system point of view, as inference exists and could still leak the impl.
141+ // 1.9. We retrieve the parent def id of the impl item, ...
142+ //
143+ // ... modulo const-anons items, for enhanced compatibility with the ecosystem
144+ // as that pattern is common with `serde`, `bevy`, ...
145+ //
146+ // For this example we want the `DefId` parent of the outermost const-anon items.
147+ // ```
148+ // const _: () = { // the parent of this const-anon
149+ // const _: () = {
150+ // impl Foo {}
151+ // };
152+ // };
153+ // ```
154+ let outermost_impl_parent = peel_parent_while ( cx. tcx , parent, |tcx, did| {
155+ tcx. def_kind ( did) == DefKind :: Const
156+ && tcx. opt_item_name ( did) == Some ( kw:: Underscore )
157+ } ) ;
158+
159+ // 2. We check if any of the paths reference a the `impl`-parent.
160+ //
161+ // If that the case we bail out, as was asked by T-lang, even though this isn't
162+ // correct from a type-system point of view, as inference exists and one-impl-rule
163+ // make its so that we could still leak the impl.
154164 if collector
155165 . paths
156166 . iter ( )
157- . any ( |path| path_has_local_parent ( path, cx, parent, parent_parent ) )
167+ . any ( |path| path_has_local_parent ( path, cx, parent, outermost_impl_parent ) )
158168 {
159169 return ;
160170 }
@@ -253,8 +263,8 @@ impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
253263 }
254264}
255265
256- /// Given a path and a parent impl def id , this checks if the if parent resolution
257- /// def id correspond to the def id of the parent impl definition.
266+ /// Given a path, this checks if the if the parent resolution def id corresponds to
267+ /// the def id of the parent impl definition (the direct one and the outermost one) .
258268///
259269/// Given this path, we will look at the path (and ignore any generic args):
260270///
@@ -267,32 +277,50 @@ fn path_has_local_parent(
267277 path : & Path < ' _ > ,
268278 cx : & LateContext < ' _ > ,
269279 impl_parent : DefId ,
270- impl_parent_parent : Option < DefId > ,
280+ outermost_impl_parent : Option < DefId > ,
271281) -> bool {
272282 path. res
273283 . opt_def_id ( )
274- . is_some_and ( |did| did_has_local_parent ( did, cx. tcx , impl_parent, impl_parent_parent ) )
284+ . is_some_and ( |did| did_has_local_parent ( did, cx. tcx , impl_parent, outermost_impl_parent ) )
275285}
276286
277- /// Given a def id and a parent impl def id, this checks if the parent
278- /// def id (modulo modules) correspond to the def id of the parent impl definition .
287+ /// Given a def id this checks if the parent def id (modulo modules) correspond to
288+ /// the def id of the parent impl definition ( the direct one and the outermost one) .
279289#[ inline]
280290fn did_has_local_parent (
281291 did : DefId ,
282292 tcx : TyCtxt < ' _ > ,
283293 impl_parent : DefId ,
284- impl_parent_parent : Option < DefId > ,
294+ outermost_impl_parent : Option < DefId > ,
285295) -> bool {
286- did. is_local ( )
287- && if let Some ( did_parent) = tcx. opt_parent ( did) {
288- did_parent == impl_parent
289- || Some ( did_parent) == impl_parent_parent
290- || !did_parent. is_crate_root ( )
291- && tcx. def_kind ( did_parent) == DefKind :: Mod
292- && did_has_local_parent ( did_parent, tcx, impl_parent, impl_parent_parent)
293- } else {
294- false
295- }
296+ if !did. is_local ( ) {
297+ return false ;
298+ }
299+
300+ let Some ( parent_did) = tcx. opt_parent ( did) else {
301+ return false ;
302+ } ;
303+
304+ peel_parent_while ( tcx, parent_did, |tcx, did| tcx. def_kind ( did) == DefKind :: Mod )
305+ . map ( |parent_did| parent_did == impl_parent || Some ( parent_did) == outermost_impl_parent)
306+ . unwrap_or ( false )
307+ }
308+
309+ /// Given a `DefId` checks if it satisfies `f` if it does check with it's parent and continue
310+ /// until it doesn't satisfies `f` and return the last `DefId` checked.
311+ ///
312+ /// In other word this method return the first `DefId` that doesn't satisfies `f`.
313+ #[ inline]
314+ fn peel_parent_while (
315+ tcx : TyCtxt < ' _ > ,
316+ mut did : DefId ,
317+ mut f : impl FnMut ( TyCtxt < ' _ > , DefId ) -> bool ,
318+ ) -> Option < DefId > {
319+ while !did. is_crate_root ( ) && f ( tcx, did) {
320+ did = tcx. opt_parent ( did) . filter ( |parent_did| parent_did. is_local ( ) ) ?;
321+ }
322+
323+ Some ( did)
296324}
297325
298326/// Return for a given `Path` the span until the last args
0 commit comments