1use std::assert_matches::debug_assert_matches;
2use std::cell::LazyCell;
3
4use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
5use rustc_data_structures::unord::UnordSet;
6use rustc_errors::{LintDiagnostic, Subdiagnostic};
7use rustc_hir as hir;
8use rustc_hir::def::DefKind;
9use rustc_hir::def_id::{DefId, LocalDefId};
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_infer::infer::outlives::env::OutlivesEnvironment;
12use rustc_macros::LintDiagnostic;
13use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
14use rustc_middle::ty::relate::{
15 Relate, RelateResult, TypeRelation, relate_args_with_variances, structurally_relate_consts,
16 structurally_relate_tys,
17};
18use rustc_middle::ty::{
19 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
20};
21use rustc_middle::{bug, span_bug};
22use rustc_session::lint::fcw;
23use rustc_session::{declare_lint, declare_lint_pass};
24use rustc_span::{Span, Symbol};
25use rustc_trait_selection::errors::{
26 AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion,
27};
28use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
29use rustc_trait_selection::traits::ObligationCtxt;
30
31use crate::{LateContext, LateLintPass, fluent_generated as fluent};
32
33declare_lint! {
34 pub IMPL_TRAIT_OVERCAPTURES,
70 Allow,
71 "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
72 @future_incompatible = FutureIncompatibleInfo {
73 reason: fcw!(EditionSemanticsChange 2024 "rpit-lifetime-capture"),
74 };
75}
76
77declare_lint! {
78 pub IMPL_TRAIT_REDUNDANT_CAPTURES,
100 Allow,
101 "redundant precise-capturing `use<...>` syntax on an `impl Trait`",
102}
103
104declare_lint_pass!(
105 ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES]
108);
109
110impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
111 fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) {
112 match &it.kind {
113 hir::ItemKind::Fn { .. } => check_fn(cx.tcx, it.owner_id.def_id),
114 _ => {}
115 }
116 }
117
118 fn check_impl_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::ImplItem<'tcx>) {
119 match &it.kind {
120 hir::ImplItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
121 _ => {}
122 }
123 }
124
125 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::TraitItem<'tcx>) {
126 match &it.kind {
127 hir::TraitItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
128 _ => {}
129 }
130 }
131}
132
133#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
134enum ParamKind {
135 Early(Symbol, u32),
137 Free(DefId),
139 Late,
141}
142
143fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
144 let sig = tcx.fn_sig(parent_def_id).instantiate_identity();
145
146 let mut in_scope_parameters = FxIndexMap::default();
147 let mut current_def_id = Some(parent_def_id.to_def_id());
149 while let Some(def_id) = current_def_id {
150 let generics = tcx.generics_of(def_id);
151 for param in &generics.own_params {
152 in_scope_parameters.insert(param.def_id, ParamKind::Early(param.name, param.index));
153 }
154 current_def_id = generics.parent;
155 }
156
157 for bound_var in sig.bound_vars() {
158 let ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) = bound_var else {
159 span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig");
160 };
161
162 in_scope_parameters.insert(def_id, ParamKind::Free(def_id));
163 }
164
165 let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig);
166
167 sig.visit_with(&mut VisitOpaqueTypes {
170 tcx,
171 parent_def_id,
172 in_scope_parameters,
173 seen: Default::default(),
174 variances: LazyCell::new(|| {
176 let mut functional_variances = FunctionalVariances {
177 tcx,
178 variances: FxHashMap::default(),
179 ambient_variance: ty::Covariant,
180 generics: tcx.generics_of(parent_def_id),
181 };
182 functional_variances.relate(sig, sig).unwrap();
183 functional_variances.variances
184 }),
185 outlives_env: LazyCell::new(|| {
186 let typing_env = ty::TypingEnv::non_body_analysis(tcx, parent_def_id);
187 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
188 let ocx = ObligationCtxt::new(&infcx);
189 let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default();
190 OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys)
191 }),
192 });
193}
194
195struct VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> {
196 tcx: TyCtxt<'tcx>,
197 parent_def_id: LocalDefId,
198 in_scope_parameters: FxIndexMap<DefId, ParamKind>,
199 variances: LazyCell<FxHashMap<DefId, ty::Variance>, VarFn>,
200 outlives_env: LazyCell<OutlivesEnvironment<'tcx>, OutlivesFn>,
201 seen: FxIndexSet<LocalDefId>,
202}
203
204impl<'tcx, VarFn, OutlivesFn> TypeVisitor<TyCtxt<'tcx>>
205 for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn>
206where
207 VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
208 OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
209{
210 fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
211 let mut added = vec![];
213 for arg in t.bound_vars() {
214 let arg: ty::BoundVariableKind = arg;
215 match arg {
216 ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
217 | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => {
218 added.push(def_id);
219 let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late);
220 assert_eq!(unique, None);
221 }
222 _ => {
223 self.tcx.dcx().span_delayed_bug(
224 self.tcx.def_span(self.parent_def_id),
225 format!("unsupported bound variable kind: {arg:?}"),
226 );
227 }
228 }
229 }
230
231 t.super_visit_with(self);
232
233 for arg in added.into_iter().rev() {
236 self.in_scope_parameters.shift_remove(&arg);
237 }
238 }
239
240 fn visit_ty(&mut self, t: Ty<'tcx>) {
241 if !t.has_aliases() {
242 return;
243 }
244
245 if let ty::Alias(ty::Projection, opaque_ty) = *t.kind()
246 && self.tcx.is_impl_trait_in_trait(opaque_ty.def_id)
247 {
248 self.tcx
250 .type_of(opaque_ty.def_id)
251 .instantiate(self.tcx, opaque_ty.args)
252 .visit_with(self)
253 } else if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
254 && let Some(opaque_def_id) = opaque_ty.def_id.as_local()
255 && self.seen.insert(opaque_def_id)
257 && let opaque =
259 self.tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty()
260 && let hir::OpaqueTyOrigin::FnReturn { parent, .. }
264 | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = opaque.origin
265 && parent == self.parent_def_id
266 {
267 let opaque_span = self.tcx.def_span(opaque_def_id);
268 let new_capture_rules = opaque_span.at_least_rust_2024();
269 if !new_capture_rules
270 && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
271 {
272 let mut captured = FxIndexSet::default();
274 let mut captured_regions = FxIndexSet::default();
275 let variances = self.tcx.variances_of(opaque_def_id);
276 let mut current_def_id = Some(opaque_def_id.to_def_id());
277 while let Some(def_id) = current_def_id {
278 let generics = self.tcx.generics_of(def_id);
279 for param in &generics.own_params {
280 if variances[param.index as usize] != ty::Invariant {
282 continue;
283 }
284
285 let arg = opaque_ty.args[param.index as usize];
286 captured.insert(extract_def_id_from_arg(self.tcx, generics, arg));
289
290 captured_regions.extend(arg.as_region());
291 }
292 current_def_id = generics.parent;
293 }
294
295 let mut uncaptured_args: FxIndexSet<_> = self
297 .in_scope_parameters
298 .iter()
299 .filter(|&(def_id, _)| !captured.contains(def_id))
300 .collect();
301 uncaptured_args.retain(|&(def_id, kind)| {
304 let Some(ty::Bivariant | ty::Contravariant) = self.variances.get(def_id) else {
305 return true;
309 };
310 debug_assert_matches!(self.tcx.def_kind(def_id), DefKind::LifetimeParam);
312 let uncaptured = match *kind {
313 ParamKind::Early(name, index) => ty::Region::new_early_param(
314 self.tcx,
315 ty::EarlyParamRegion { name, index },
316 ),
317 ParamKind::Free(def_id) => ty::Region::new_late_param(
318 self.tcx,
319 self.parent_def_id.to_def_id(),
320 ty::LateParamRegionKind::Named(def_id),
321 ),
322 ParamKind::Late => return true,
324 };
325 !captured_regions.iter().any(|r| {
327 self.outlives_env
328 .free_region_map()
329 .sub_free_regions(self.tcx, *r, uncaptured)
330 })
331 });
332
333 if !uncaptured_args.is_empty() {
336 let suggestion = impl_trait_overcapture_suggestion(
337 self.tcx,
338 opaque_def_id,
339 self.parent_def_id,
340 captured,
341 );
342
343 let uncaptured_spans: Vec<_> = uncaptured_args
344 .into_iter()
345 .map(|(def_id, _)| self.tcx.def_span(def_id))
346 .collect();
347
348 self.tcx.emit_node_span_lint(
349 IMPL_TRAIT_OVERCAPTURES,
350 self.tcx.local_def_id_to_hir_id(opaque_def_id),
351 opaque_span,
352 ImplTraitOvercapturesLint {
353 self_ty: t,
354 num_captured: uncaptured_spans.len(),
355 uncaptured_spans,
356 suggestion,
357 },
358 );
359 }
360 }
361
362 if new_capture_rules
366 && let Some((captured_args, capturing_span)) =
367 opaque.bounds.iter().find_map(|bound| match *bound {
368 hir::GenericBound::Use(a, s) => Some((a, s)),
369 _ => None,
370 })
371 {
372 let mut explicitly_captured = UnordSet::default();
373 for arg in captured_args {
374 match self.tcx.named_bound_var(arg.hir_id()) {
375 Some(
376 ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
377 ) => {
378 if self.tcx.def_kind(self.tcx.local_parent(def_id)) == DefKind::OpaqueTy
379 {
380 let def_id = self
381 .tcx
382 .map_opaque_lifetime_to_parent_lifetime(def_id)
383 .opt_param_def_id(self.tcx, self.parent_def_id.to_def_id())
384 .expect("variable should have been duplicated from parent");
385
386 explicitly_captured.insert(def_id);
387 } else {
388 explicitly_captured.insert(def_id.to_def_id());
389 }
390 }
391 _ => {
392 self.tcx.dcx().span_delayed_bug(
393 self.tcx.hir_span(arg.hir_id()),
394 "no valid for captured arg",
395 );
396 }
397 }
398 }
399
400 if self
401 .in_scope_parameters
402 .iter()
403 .all(|(def_id, _)| explicitly_captured.contains(def_id))
404 {
405 self.tcx.emit_node_span_lint(
406 IMPL_TRAIT_REDUNDANT_CAPTURES,
407 self.tcx.local_def_id_to_hir_id(opaque_def_id),
408 opaque_span,
409 ImplTraitRedundantCapturesLint { capturing_span },
410 );
411 }
412 }
413
414 for clause in
419 self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args)
420 {
421 clause.visit_with(self)
422 }
423 }
424
425 t.super_visit_with(self);
426 }
427}
428
429struct ImplTraitOvercapturesLint<'tcx> {
430 uncaptured_spans: Vec<Span>,
431 self_ty: Ty<'tcx>,
432 num_captured: usize,
433 suggestion: Option<AddPreciseCapturingForOvercapture>,
434}
435
436impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
437 fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
438 diag.primary_message(fluent::lint_impl_trait_overcaptures);
439 diag.arg("self_ty", self.self_ty.to_string())
440 .arg("num_captured", self.num_captured)
441 .span_note(self.uncaptured_spans, fluent::lint_note)
442 .note(fluent::lint_note2);
443 if let Some(suggestion) = self.suggestion {
444 suggestion.add_to_diag(diag);
445 }
446 }
447}
448
449#[derive(LintDiagnostic)]
450#[diag(lint_impl_trait_redundant_captures)]
451struct ImplTraitRedundantCapturesLint {
452 #[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
453 capturing_span: Span,
454}
455
456fn extract_def_id_from_arg<'tcx>(
457 tcx: TyCtxt<'tcx>,
458 generics: &'tcx ty::Generics,
459 arg: ty::GenericArg<'tcx>,
460) -> DefId {
461 match arg.kind() {
462 ty::GenericArgKind::Lifetime(re) => match re.kind() {
463 ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
464 ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
465 | ty::ReLateParam(ty::LateParamRegion {
466 scope: _,
467 kind: ty::LateParamRegionKind::Named(def_id),
468 }) => def_id,
469 _ => unreachable!(),
470 },
471 ty::GenericArgKind::Type(ty) => {
472 let ty::Param(param_ty) = *ty.kind() else {
473 bug!();
474 };
475 generics.type_param(param_ty, tcx).def_id
476 }
477 ty::GenericArgKind::Const(ct) => {
478 let ty::ConstKind::Param(param_ct) = ct.kind() else {
479 bug!();
480 };
481 generics.const_param(param_ct, tcx).def_id
482 }
483 }
484}
485
486struct FunctionalVariances<'tcx> {
493 tcx: TyCtxt<'tcx>,
494 variances: FxHashMap<DefId, ty::Variance>,
495 ambient_variance: ty::Variance,
496 generics: &'tcx ty::Generics,
497}
498
499impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
500 fn cx(&self) -> TyCtxt<'tcx> {
501 self.tcx
502 }
503
504 fn relate_ty_args(
505 &mut self,
506 a_ty: Ty<'tcx>,
507 _: Ty<'tcx>,
508 def_id: DefId,
509 a_args: ty::GenericArgsRef<'tcx>,
510 b_args: ty::GenericArgsRef<'tcx>,
511 _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
512 ) -> RelateResult<'tcx, Ty<'tcx>> {
513 let variances = self.cx().variances_of(def_id);
514 relate_args_with_variances(self, variances, a_args, b_args)?;
515 Ok(a_ty)
516 }
517
518 fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
519 &mut self,
520 variance: ty::Variance,
521 _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
522 a: T,
523 b: T,
524 ) -> RelateResult<'tcx, T> {
525 let old_variance = self.ambient_variance;
526 self.ambient_variance = self.ambient_variance.xform(variance);
527 self.relate(a, b).unwrap();
528 self.ambient_variance = old_variance;
529 Ok(a)
530 }
531
532 fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
533 structurally_relate_tys(self, a, b).unwrap();
534 Ok(a)
535 }
536
537 fn regions(
538 &mut self,
539 a: ty::Region<'tcx>,
540 _: ty::Region<'tcx>,
541 ) -> RelateResult<'tcx, ty::Region<'tcx>> {
542 let def_id = match a.kind() {
543 ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id,
544 ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
545 | ty::ReLateParam(ty::LateParamRegion {
546 scope: _,
547 kind: ty::LateParamRegionKind::Named(def_id),
548 }) => def_id,
549 _ => {
550 return Ok(a);
551 }
552 };
553
554 if let Some(variance) = self.variances.get_mut(&def_id) {
555 *variance = unify(*variance, self.ambient_variance);
556 } else {
557 self.variances.insert(def_id, self.ambient_variance);
558 }
559
560 Ok(a)
561 }
562
563 fn consts(
564 &mut self,
565 a: ty::Const<'tcx>,
566 b: ty::Const<'tcx>,
567 ) -> RelateResult<'tcx, ty::Const<'tcx>> {
568 structurally_relate_consts(self, a, b).unwrap();
569 Ok(a)
570 }
571
572 fn binders<T>(
573 &mut self,
574 a: ty::Binder<'tcx, T>,
575 b: ty::Binder<'tcx, T>,
576 ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
577 where
578 T: Relate<TyCtxt<'tcx>>,
579 {
580 self.relate(a.skip_binder(), b.skip_binder()).unwrap();
581 Ok(a)
582 }
583}
584
585fn unify(a: ty::Variance, b: ty::Variance) -> ty::Variance {
587 match (a, b) {
588 (ty::Bivariant, other) | (other, ty::Bivariant) => other,
590 (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant,
592 (ty::Contravariant, ty::Covariant) | (ty::Covariant, ty::Contravariant) => ty::Invariant,
594 (ty::Contravariant, ty::Contravariant) => ty::Contravariant,
596 (ty::Covariant, ty::Covariant) => ty::Covariant,
597 }
598}