rustc_borrowck/diagnostics/
opaque_suggestions.rs
1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use std::ops::ControlFlow;
5
6use either::Either;
7use itertools::Itertools as _;
8use rustc_data_structures::fx::FxIndexSet;
9use rustc_errors::{Diag, Subdiagnostic};
10use rustc_hir as hir;
11use rustc_hir::def_id::DefId;
12use rustc_middle::mir::{self, ConstraintCategory, Location};
13use rustc_middle::ty::{
14 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
15};
16use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
17
18use crate::MirBorrowckCtxt;
19use crate::borrow_set::BorrowData;
20use crate::consumers::RegionInferenceContext;
21use crate::type_check::Locations;
22
23impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
24 pub(crate) fn note_due_to_edition_2024_opaque_capture_rules(
29 &self,
30 borrow: &BorrowData<'tcx>,
31 diag: &mut Diag<'_>,
32 ) {
33 for ty in self.body.local_decls.iter().map(|local| local.ty) {
37 if !ty.has_opaque_types() {
38 continue;
39 }
40
41 let tcx = self.infcx.tcx;
42 let ControlFlow::Break((opaque_def_id, offending_region_idx, location)) = ty
43 .visit_with(&mut FindOpaqueRegion {
44 regioncx: &self.regioncx,
45 tcx,
46 borrow_region: borrow.region,
47 })
48 else {
49 continue;
50 };
51
52 if tcx.rendered_precise_capturing_args(opaque_def_id).is_some() {
55 continue;
56 }
57
58 let mut visitor = CheckExplicitRegionMentionAndCollectGenerics {
64 tcx,
65 generics: tcx.generics_of(opaque_def_id),
66 offending_region_idx,
67 seen_opaques: [opaque_def_id].into_iter().collect(),
68 seen_lifetimes: Default::default(),
69 };
70 if tcx
71 .explicit_item_bounds(opaque_def_id)
72 .skip_binder()
73 .visit_with(&mut visitor)
74 .is_break()
75 {
76 continue;
77 }
78
79 match self.body.stmt_at(location) {
82 Either::Right(mir::Terminator { source_info, .. }) => {
83 diag.span_note(
84 source_info.span,
85 "this call may capture more lifetimes than intended, \
86 because Rust 2024 has adjusted the `impl Trait` lifetime capture rules",
87 );
88 let mut captured_args = visitor.seen_lifetimes;
89 let mut next_generics = Some(visitor.generics);
93 let mut any_synthetic = false;
94 while let Some(generics) = next_generics {
95 for param in &generics.own_params {
96 if param.kind.is_ty_or_const() {
97 captured_args.insert(param.def_id);
98 }
99 if param.kind.is_synthetic() {
100 any_synthetic = true;
101 }
102 }
103 next_generics = generics.parent.map(|def_id| tcx.generics_of(def_id));
104 }
105
106 if let Some(opaque_def_id) = opaque_def_id.as_local()
107 && let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
108 tcx.hir().expect_opaque_ty(opaque_def_id).origin
109 {
110 if let Some(sugg) = impl_trait_overcapture_suggestion(
111 tcx,
112 opaque_def_id,
113 parent,
114 captured_args,
115 ) {
116 sugg.add_to_diag(diag);
117 }
118 } else {
119 diag.span_help(
120 tcx.def_span(opaque_def_id),
121 format!(
122 "if you can modify this crate, add a precise \
123 capturing bound to avoid overcapturing: `+ use<{}>`",
124 if any_synthetic {
125 "/* Args */".to_string()
126 } else {
127 captured_args
128 .into_iter()
129 .map(|def_id| tcx.item_name(def_id))
130 .join(", ")
131 }
132 ),
133 );
134 }
135 return;
136 }
137 Either::Left(_) => {}
138 }
139 }
140 }
141}
142
143struct FindOpaqueRegion<'a, 'tcx> {
145 tcx: TyCtxt<'tcx>,
146 regioncx: &'a RegionInferenceContext<'tcx>,
147 borrow_region: ty::RegionVid,
148}
149
150impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
151 type Result = ControlFlow<(DefId, usize, Location), ()>;
152
153 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
154 if let ty::Alias(ty::Opaque, opaque) = *ty.kind()
157 && let hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl: None } =
158 self.tcx.opaque_ty_origin(opaque.def_id)
159 {
160 let variances = self.tcx.variances_of(opaque.def_id);
161 for (idx, (arg, variance)) in std::iter::zip(opaque.args, variances).enumerate() {
162 if *variance == ty::Bivariant {
164 continue;
165 }
166 let Some(opaque_region) = arg.as_region() else {
168 continue;
169 };
170 if opaque_region.is_bound() {
172 continue;
173 }
174 let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
175
176 if let Some((path, _)) =
178 self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
179 r == opaque_region_vid
180 })
181 {
182 for constraint in path {
183 if let ConstraintCategory::CallArgument(Some(call_ty)) = constraint.category
185 && let ty::FnDef(call_def_id, _) = *call_ty.kind()
186 && call_def_id == parent
188 && let Locations::Single(location) = constraint.locations
189 {
190 return ControlFlow::Break((opaque.def_id, idx, location));
191 }
192 }
193 }
194 }
195 }
196
197 ty.super_visit_with(self)
198 }
199}
200
201struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
202 tcx: TyCtxt<'tcx>,
203 generics: &'tcx ty::Generics,
204 offending_region_idx: usize,
205 seen_opaques: FxIndexSet<DefId>,
206 seen_lifetimes: FxIndexSet<DefId>,
207}
208
209impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
210 type Result = ControlFlow<(), ()>;
211
212 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
213 match *ty.kind() {
214 ty::Alias(ty::Opaque, opaque) => {
215 if self.seen_opaques.insert(opaque.def_id) {
216 for (bound, _) in self
217 .tcx
218 .explicit_item_bounds(opaque.def_id)
219 .iter_instantiated_copied(self.tcx, opaque.args)
220 {
221 bound.visit_with(self)?;
222 }
223 }
224 ControlFlow::Continue(())
225 }
226 _ => ty.super_visit_with(self),
227 }
228 }
229
230 fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
231 match r.kind() {
232 ty::ReEarlyParam(param) => {
233 if param.index as usize == self.offending_region_idx {
234 ControlFlow::Break(())
235 } else {
236 self.seen_lifetimes.insert(self.generics.region_param(param, self.tcx).def_id);
237 ControlFlow::Continue(())
238 }
239 }
240 _ => ControlFlow::Continue(()),
241 }
242 }
243}