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