@@ -124,16 +124,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
124
124
// If that's the case this means that this impl block declaration
125
125
// is using local items and so we don't lint on it.
126
126
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
-
137
127
// 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
138
128
// of the `impl` definition
139
129
let mut collector = PathCollector { paths : Vec :: new ( ) } ;
@@ -148,13 +138,33 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
148
138
|p| matches ! ( p. res, Res :: Def ( def_kind, _) if def_kind != DefKind :: TyParam ) ,
149
139
) ;
150
140
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.
154
164
if collector
155
165
. paths
156
166
. 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 ) )
158
168
{
159
169
return ;
160
170
}
@@ -253,8 +263,8 @@ impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
253
263
}
254
264
}
255
265
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) .
258
268
///
259
269
/// Given this path, we will look at the path (and ignore any generic args):
260
270
///
@@ -267,32 +277,50 @@ fn path_has_local_parent(
267
277
path : & Path < ' _ > ,
268
278
cx : & LateContext < ' _ > ,
269
279
impl_parent : DefId ,
270
- impl_parent_parent : Option < DefId > ,
280
+ outermost_impl_parent : Option < DefId > ,
271
281
) -> bool {
272
282
path. res
273
283
. 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 ) )
275
285
}
276
286
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) .
279
289
#[ inline]
280
290
fn did_has_local_parent (
281
291
did : DefId ,
282
292
tcx : TyCtxt < ' _ > ,
283
293
impl_parent : DefId ,
284
- impl_parent_parent : Option < DefId > ,
294
+ outermost_impl_parent : Option < DefId > ,
285
295
) -> 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)
296
324
}
297
325
298
326
/// Return for a given `Path` the span until the last args
0 commit comments