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:: { self , Binder , Ty , TyCtxt , TypeFoldable , TypeFolder } ;
8
+ use rustc_middle:: ty:: { EarlyBinder , TraitRef , TypeSuperFoldable } ;
2
9
use rustc_span:: def_id:: { DefId , LOCAL_CRATE } ;
10
+ use rustc_span:: Span ;
3
11
use 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, CandidateSource ,
15
+ } ;
4
16
5
17
use crate :: lints:: { NonLocalDefinitionsCargoUpdateNote , NonLocalDefinitionsDiag } ;
6
18
use crate :: { LateContext , LateLintPass , LintContext } ;
@@ -35,7 +47,7 @@ declare_lint! {
35
47
/// All nested bodies (functions, enum discriminant, array length, consts) (expect for
36
48
/// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked.
37
49
pub NON_LOCAL_DEFINITIONS ,
38
- Allow ,
50
+ Warn ,
39
51
"checks for non-local definitions" ,
40
52
report_in_external_macro
41
53
}
@@ -66,7 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
66
78
return ;
67
79
}
68
80
69
- let parent = cx. tcx . parent ( item. owner_id . def_id . into ( ) ) ;
81
+ let def_id = item. owner_id . def_id . into ( ) ;
82
+ let parent = cx. tcx . parent ( def_id) ;
70
83
let parent_def_kind = cx. tcx . def_kind ( parent) ;
71
84
let parent_opt_item_name = cx. tcx . opt_item_name ( parent) ;
72
85
@@ -121,6 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
121
134
None
122
135
} ;
123
136
137
+ // Part 1: Is the Self type local?
124
138
let self_ty_has_local_parent = match impl_. self_ty . kind {
125
139
TyKind :: Path ( QPath :: Resolved ( _, ty_path) ) => {
126
140
path_has_local_parent ( ty_path, cx, parent, parent_parent)
@@ -150,41 +164,70 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
150
164
| TyKind :: Err ( _) => false ,
151
165
} ;
152
166
167
+ if self_ty_has_local_parent {
168
+ return ;
169
+ }
170
+
171
+ // Part 2: Is the Trait local?
153
172
let of_trait_has_local_parent = impl_
154
173
. of_trait
155
174
. map ( |of_trait| path_has_local_parent ( of_trait. path , cx, parent, parent_parent) )
156
175
. unwrap_or ( false ) ;
157
176
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) {
161
- let const_anon = if self . body_depth == 1
162
- && parent_def_kind == DefKind :: Const
163
- && parent_opt_item_name != Some ( kw:: Underscore )
164
- && let Some ( parent) = parent. as_local ( )
165
- && let Node :: Item ( item) = cx. tcx . hir_node_by_def_id ( parent)
166
- && let ItemKind :: Const ( ty, _, _) = item. kind
167
- && let TyKind :: Tup ( & [ ] ) = ty. kind
168
- {
169
- Some ( item. ident . span )
170
- } else {
171
- None
172
- } ;
173
-
174
- cx. emit_span_lint (
175
- NON_LOCAL_DEFINITIONS ,
176
- item. span ,
177
- NonLocalDefinitionsDiag :: Impl {
178
- depth : self . body_depth ,
179
- body_kind_descr : cx. tcx . def_kind_descr ( parent_def_kind, parent) ,
180
- body_name : parent_opt_item_name
181
- . map ( |s| s. to_ident_string ( ) )
182
- . unwrap_or_else ( || "<unnameable>" . to_string ( ) ) ,
183
- cargo_update : cargo_update ( ) ,
184
- const_anon,
185
- } ,
186
- )
177
+ if of_trait_has_local_parent {
178
+ return ;
179
+ }
180
+
181
+ // Part 3: Is the impl definition leaking outside it's defining scope?
182
+ //
183
+ // We always consider inherent impls to be leaking.
184
+ let impl_has_enough_non_local_candidates = cx
185
+ . tcx
186
+ . impl_trait_ref ( def_id)
187
+ . map ( |binder| {
188
+ impl_trait_ref_has_enough_non_local_candidates (
189
+ cx. tcx ,
190
+ item. span ,
191
+ def_id,
192
+ binder,
193
+ |did| did_has_local_parent ( did, cx. tcx , parent, parent_parent) ,
194
+ )
195
+ } )
196
+ . unwrap_or ( false ) ;
197
+
198
+ if impl_has_enough_non_local_candidates {
199
+ return ;
187
200
}
201
+
202
+ // Get the span of the parent const item ident (if it's a not a const anon).
203
+ //
204
+ // Used to suggest changing the const item to a const anon.
205
+ let span_for_const_anon_suggestion = if self . body_depth == 1
206
+ && parent_def_kind == DefKind :: Const
207
+ && parent_opt_item_name != Some ( kw:: Underscore )
208
+ && let Some ( parent) = parent. as_local ( )
209
+ && let Node :: Item ( item) = cx. tcx . hir_node_by_def_id ( parent)
210
+ && let ItemKind :: Const ( ty, _, _) = item. kind
211
+ && let TyKind :: Tup ( & [ ] ) = ty. kind
212
+ {
213
+ Some ( item. ident . span )
214
+ } else {
215
+ None
216
+ } ;
217
+
218
+ cx. emit_span_lint (
219
+ NON_LOCAL_DEFINITIONS ,
220
+ item. span ,
221
+ NonLocalDefinitionsDiag :: Impl {
222
+ depth : self . body_depth ,
223
+ body_kind_descr : cx. tcx . def_kind_descr ( parent_def_kind, parent) ,
224
+ body_name : parent_opt_item_name
225
+ . map ( |s| s. to_ident_string ( ) )
226
+ . unwrap_or_else ( || "<unnameable>" . to_string ( ) ) ,
227
+ cargo_update : cargo_update ( ) ,
228
+ const_anon : span_for_const_anon_suggestion,
229
+ } ,
230
+ )
188
231
}
189
232
ItemKind :: Macro ( _macro, MacroKind :: Bang )
190
233
if cx. tcx . has_attr ( item. owner_id . def_id , sym:: macro_export) =>
@@ -207,6 +250,81 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
207
250
}
208
251
}
209
252
253
+ // Detecting if the impl definition is leaking outside of it's defining scope.
254
+ //
255
+ // Rule: for each impl, instantiate all local types with inference vars and
256
+ // then assemble candidates for that goal, if there are more than 1 (non-private
257
+ // impls), it does not leak.
258
+ //
259
+ // https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895
260
+ fn impl_trait_ref_has_enough_non_local_candidates < ' tcx > (
261
+ tcx : TyCtxt < ' tcx > ,
262
+ infer_span : Span ,
263
+ trait_def_id : DefId ,
264
+ binder : EarlyBinder < TraitRef < ' tcx > > ,
265
+ mut did_has_local_parent : impl FnMut ( DefId ) -> bool ,
266
+ ) -> bool {
267
+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
268
+ let trait_ref = binder. instantiate ( tcx, infcx. fresh_args_for_item ( infer_span, trait_def_id) ) ;
269
+
270
+ let trait_ref = trait_ref. fold_with ( & mut ReplaceLocalTypesWithInfer {
271
+ infcx : & infcx,
272
+ infer_span,
273
+ did_has_local_parent : & mut did_has_local_parent,
274
+ } ) ;
275
+
276
+ let poly_trait_obligation = Obligation :: new (
277
+ tcx,
278
+ ObligationCause :: dummy ( ) ,
279
+ ty:: ParamEnv :: empty ( ) ,
280
+ Binder :: dummy ( trait_ref) ,
281
+ ) ;
282
+
283
+ let ambiguities = compute_applicable_impls_for_diagnostics ( & infcx, & poly_trait_obligation) ;
284
+
285
+ let mut it = ambiguities. iter ( ) . filter ( |ambi| match ambi {
286
+ CandidateSource :: DefId ( did) => !did_has_local_parent ( * did) ,
287
+ CandidateSource :: ParamEnv ( _) => unreachable ! ( ) ,
288
+ } ) ;
289
+
290
+ let _ = it. next ( ) ;
291
+ it. next ( ) . is_some ( )
292
+ }
293
+
294
+ /// Replace every local type by inference variable.
295
+ ///
296
+ /// ```text
297
+ /// <Global<Local> as std::cmp::PartialEq<Global<Local>>>
298
+ /// to
299
+ /// <Global<_> as std::cmp::PartialEq<Global<_>>>
300
+ /// ```
301
+ struct ReplaceLocalTypesWithInfer < ' a , ' tcx , F : FnMut ( DefId ) -> bool > {
302
+ infcx : & ' a InferCtxt < ' tcx > ,
303
+ did_has_local_parent : F ,
304
+ infer_span : Span ,
305
+ }
306
+
307
+ impl < ' a , ' tcx , F : FnMut ( DefId ) -> bool > TypeFolder < TyCtxt < ' tcx > >
308
+ for ReplaceLocalTypesWithInfer < ' a , ' tcx , F >
309
+ {
310
+ fn interner ( & self ) -> TyCtxt < ' tcx > {
311
+ self . infcx . tcx
312
+ }
313
+
314
+ fn fold_ty ( & mut self , t : Ty < ' tcx > ) -> Ty < ' tcx > {
315
+ if let Some ( ty_did) = t. ty_def_id ( )
316
+ && ( self . did_has_local_parent ) ( ty_did)
317
+ {
318
+ self . infcx . next_ty_var ( TypeVariableOrigin {
319
+ kind : TypeVariableOriginKind :: TypeInference ,
320
+ span : self . infer_span ,
321
+ } )
322
+ } else {
323
+ t. super_fold_with ( self )
324
+ }
325
+ }
326
+ }
327
+
210
328
/// Given a path and a parent impl def id, this checks if the if parent resolution
211
329
/// def id correspond to the def id of the parent impl definition.
212
330
///
@@ -216,16 +334,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
216
334
/// std::convert::PartialEq<Foo<Bar>>
217
335
/// ^^^^^^^^^^^^^^^^^^^^^^^
218
336
/// ```
337
+ #[ inline]
219
338
fn path_has_local_parent (
220
339
path : & Path < ' _ > ,
221
340
cx : & LateContext < ' _ > ,
222
341
impl_parent : DefId ,
223
342
impl_parent_parent : Option < DefId > ,
224
343
) -> 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
- } )
344
+ path. res
345
+ . opt_def_id ( )
346
+ . is_some_and ( |did| did_has_local_parent ( did, cx. tcx , impl_parent, impl_parent_parent) )
347
+ }
348
+
349
+ /// Given a def id and a parent impl def id, this checks if the parent
350
+ /// def id correspond to the def id of the parent impl definition.
351
+ #[ inline]
352
+ fn did_has_local_parent (
353
+ did : DefId ,
354
+ tcx : TyCtxt < ' _ > ,
355
+ impl_parent : DefId ,
356
+ impl_parent_parent : Option < DefId > ,
357
+ ) -> bool {
358
+ did. is_local ( ) && {
359
+ let res_parent = tcx. parent ( did) ;
360
+ res_parent == impl_parent || Some ( res_parent) == impl_parent_parent
361
+ }
231
362
}
0 commit comments