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