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 } ; 
29use  rustc_span:: def_id:: { DefId ,  LOCAL_CRATE } ; 
10+ use  rustc_span:: Span ; 
311use  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+ } ; 
416
517use  crate :: lints:: { NonLocalDefinitionsCargoUpdateNote ,  NonLocalDefinitionsDiag } ; 
618use  crate :: { LateContext ,  LateLintPass ,  LintContext } ; 
@@ -35,7 +47,7 @@ declare_lint! {
3547     /// All nested bodies (functions, enum discriminant, array length, consts) (expect for 
3648     /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked. 
3749     pub  NON_LOCAL_DEFINITIONS , 
38-     Allow , 
50+     Warn , 
3951    "checks for non-local definitions" , 
4052    report_in_external_macro
4153} 
@@ -66,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
6678            return ; 
6779        } 
6880
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) ; 
7084        let  parent_def_kind = cx. tcx . def_kind ( parent) ; 
7185        let  parent_opt_item_name = cx. tcx . opt_item_name ( parent) ; 
7286
@@ -155,9 +169,54 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
155169                    . map ( |of_trait| path_has_local_parent ( of_trait. path ,  cx,  parent,  parent_parent) ) 
156170                    . unwrap_or ( false ) ; 
157171
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)  { 
161220                    let  const_anon = if  self . body_depth  == 1 
162221                        && parent_def_kind == DefKind :: Const 
163222                        && parent_opt_item_name != Some ( kw:: Underscore ) 
@@ -207,6 +266,47 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
207266    } 
208267} 
209268
269+ /// Replace every local type by inference variable. 
270+ /// 
271+ /// ```text 
272+ /// <Global<Local> as std::cmp::PartialEq<Global<Local>>> 
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 < DefId > , 
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+ 
210310/// Given a path and a parent impl def id, this checks if the if parent resolution 
211311/// def id correspond to the def id of the parent impl definition. 
212312/// 
@@ -216,16 +316,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
216316///    std::convert::PartialEq<Foo<Bar>> 
217317///    ^^^^^^^^^^^^^^^^^^^^^^^ 
218318/// ``` 
319+ #[ inline]  
219320fn  path_has_local_parent ( 
220321    path :  & Path < ' _ > , 
221322    cx :  & LateContext < ' _ > , 
222323    impl_parent :  DefId , 
223324    impl_parent_parent :  Option < DefId > , 
224325)  -> 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 < DefId > , 
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+     } 
231344} 
0 commit comments