rustc_trait_selection/error_reporting/infer/nice_region_error/
static_impl_trait.rs1use rustc_data_structures::fx::FxIndexSet;
4use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
5use rustc_hir::def_id::DefId;
6use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
7use rustc_hir::{
8 self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
9 LifetimeKind, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
10};
11use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
12use rustc_span::def_id::LocalDefId;
13use rustc_span::{Ident, Span};
14use tracing::debug;
15
16use crate::error_reporting::infer::nice_region_error::NiceRegionError;
17use crate::errors::ButNeedsToSatisfy;
18use crate::infer::{RegionResolutionError, SubregionOrigin};
19
20impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
21 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
24 debug!("try_report_static_impl_trait(error={:?})", self.error);
25 let tcx = self.tcx();
26 let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
27 RegionResolutionError::SubSupConflict(
28 _,
29 var_origin,
30 sub_origin,
31 sub_r,
32 sup_origin,
33 sup_r,
34 spans,
35 ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
36 _ => return None,
37 };
38 debug!(
39 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
40 var_origin, sub_origin, sub_r, sup_origin, sup_r
41 );
42 let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
43 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
44 let sp = var_origin.span();
45 let return_sp = sub_origin.span();
46 let param = self.find_param_with_region(*sup_r, *sub_r)?;
47 let simple_ident = param.param.pat.simple_ident();
48 let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
49
50 let (mention_influencer, influencer_point) =
51 if sup_origin.span().overlaps(param.param_ty_span) {
52 (false, sup_origin.span())
64 } else {
65 (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
66 };
67
68 debug!("try_report_static_impl_trait: param_info={:?}", param);
69
70 let mut spans = spans.clone();
71
72 if mention_influencer {
73 spans.push(sup_origin.span());
74 }
75 spans.sort();
77 spans.dedup_by_key(|span| (span.lo(), span.hi()));
78
79 let require_span =
81 if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
82
83 let spans_empty = spans.is_empty();
84 let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
85 let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
86 Some(*bound)
87 } else {
88 None
89 };
90
91 let diag = ButNeedsToSatisfy {
92 sp,
93 influencer_point,
94 spans: spans.clone(),
95 require_span_as_note: require_as_note.then_some(require_span),
99 require_span_as_label: (!require_as_note).then_some(require_span),
101
102 has_lifetime: sup_r.has_name(),
103 lifetime: lifetime_name.clone(),
104 has_param_name: simple_ident.is_some(),
105 param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
106 spans_empty,
107 bound,
108 };
109
110 let mut err = self.tcx().dcx().create_err(diag);
111
112 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.scope);
113
114 let arg = match param.param.pat.simple_ident() {
115 Some(simple_ident) => format!("argument `{simple_ident}`"),
116 None => "the argument".to_string(),
117 };
118 let captures = format!("captures data from {arg}");
119 suggest_new_region_bound(
120 tcx,
121 &mut err,
122 fn_returns,
123 lifetime_name,
124 Some(arg),
125 captures,
126 Some((param.param_ty_span, param.param_ty.to_string())),
127 Some(anon_reg_sup.scope),
128 );
129
130 let reported = err.emit();
131 Some(reported)
132 }
133}
134
135pub fn suggest_new_region_bound(
136 tcx: TyCtxt<'_>,
137 err: &mut Diag<'_>,
138 fn_returns: Vec<&rustc_hir::Ty<'_>>,
139 lifetime_name: String,
140 arg: Option<String>,
141 captures: String,
142 param: Option<(Span, String)>,
143 scope_def_id: Option<LocalDefId>,
144) {
145 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
146 let consider = "consider changing";
148 let declare = "to declare that";
149 let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound");
150 let explicit_static =
151 arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}"));
152 let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
153 let plus_lt = format!(" + {lifetime_name}");
154 for fn_return in fn_returns {
155 if fn_return.span.desugaring_kind().is_some() {
156 continue;
158 }
159 match fn_return.kind {
160 TyKind::OpaqueDef(opaque) => {
162 let did = opaque.def_id.to_def_id();
164 let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
165
166 if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
167 GenericBound::Outlives(Lifetime {
168 kind: LifetimeKind::Static, ident, ..
169 }) => Some(ident.span),
170 _ => None,
171 }) {
172 if let Some(explicit_static) = &explicit_static {
173 err.span_suggestion_verbose(
174 span,
175 format!("{consider} `{ty}`'s {explicit_static}"),
176 &lifetime_name,
177 Applicability::MaybeIncorrect,
178 );
179 }
180 if let Some((param_span, ref param_ty)) = param {
181 err.span_suggestion_verbose(
182 param_span,
183 add_static_bound,
184 param_ty,
185 Applicability::MaybeIncorrect,
186 );
187 }
188 } else if opaque.bounds.iter().any(|arg| {
189 matches!(arg,
190 GenericBound::Outlives(Lifetime { ident, .. })
191 if ident.name.to_string() == lifetime_name )
192 }) {
193 } else {
194 let existing_lt_name = if let Some(id) = scope_def_id
196 && let Some(generics) = tcx.hir_get_generics(id)
197 && let named_lifetimes = generics
198 .params
199 .iter()
200 .filter(|p| {
201 matches!(
202 p.kind,
203 GenericParamKind::Lifetime {
204 kind: hir::LifetimeParamKind::Explicit
205 }
206 )
207 })
208 .map(|p| {
209 if let hir::ParamName::Plain(name) = p.name {
210 Some(name.to_string())
211 } else {
212 None
213 }
214 })
215 .filter(|n| !matches!(n, None))
216 .collect::<Vec<_>>()
217 && named_lifetimes.len() > 0
218 {
219 named_lifetimes[0].clone()
220 } else {
221 None
222 };
223 let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
224 if let Some(id) = scope_def_id
227 && let Some(generics) = tcx.hir_get_generics(id)
228 && let mut spans_suggs =
229 make_elided_region_spans_suggs(name, generics.params.iter())
230 && spans_suggs.len() > 1
231 {
232 let use_lt = if existing_lt_name == None {
233 spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
234 format!("you can introduce a named lifetime parameter `{name}`")
235 } else {
236 format!("you can use the named lifetime parameter `{name}`")
238 };
239 spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
240 err.multipart_suggestion_verbose(
241 format!("{declare} `{ty}` {captures}, {use_lt}",),
242 spans_suggs,
243 Applicability::MaybeIncorrect,
244 );
245 } else {
246 err.span_suggestion_verbose(
247 fn_return.span.shrink_to_hi(),
248 format!("{declare} `{ty}` {captures}, {explicit}",),
249 &plus_lt,
250 Applicability::MaybeIncorrect,
251 );
252 }
253 }
254 }
255 TyKind::TraitObject(_, lt) => {
256 if let LifetimeKind::ImplicitObjectLifetimeDefault = lt.kind {
257 err.span_suggestion_verbose(
258 fn_return.span.shrink_to_hi(),
259 format!("{declare} the trait object {captures}, {explicit}",),
260 &plus_lt,
261 Applicability::MaybeIncorrect,
262 );
263 } else if lt.ident.name.to_string() != lifetime_name {
264 if let Some(explicit_static) = &explicit_static {
269 err.span_suggestion_verbose(
270 lt.ident.span,
271 format!("{consider} the trait object's {explicit_static}"),
272 &lifetime_name,
273 Applicability::MaybeIncorrect,
274 );
275 }
276 if let Some((param_span, param_ty)) = param.clone() {
277 err.span_suggestion_verbose(
278 param_span,
279 add_static_bound,
280 param_ty,
281 Applicability::MaybeIncorrect,
282 );
283 }
284 }
285 }
286 _ => {}
287 }
288 }
289}
290
291fn make_elided_region_spans_suggs<'a>(
292 name: &str,
293 generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
294) -> Vec<(Span, String)> {
295 let mut spans_suggs = Vec::new();
296 let mut bracket_span = None;
297 let mut consecutive_brackets = 0;
298
299 let mut process_consecutive_brackets =
300 |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
301 if let Some(span) = span
302 && bracket_span.is_none_or(|bracket_span| span == bracket_span)
303 {
304 consecutive_brackets += 1;
305 } else if let Some(bracket_span) = bracket_span.take() {
306 let sugg = std::iter::once("<")
307 .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
308 .chain([">"])
309 .collect();
310 spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
311 consecutive_brackets = 0;
312 }
313 bracket_span = span;
314 };
315
316 for p in generic_params {
317 if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
318 match kind {
319 MissingLifetimeKind::Underscore => {
320 process_consecutive_brackets(None, &mut spans_suggs);
321 spans_suggs.push((p.span, name.to_string()))
322 }
323 MissingLifetimeKind::Ampersand => {
324 process_consecutive_brackets(None, &mut spans_suggs);
325 spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
326 }
327 MissingLifetimeKind::Comma => {
328 process_consecutive_brackets(None, &mut spans_suggs);
329 spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
330 }
331 MissingLifetimeKind::Brackets => {
332 process_consecutive_brackets(Some(p.span), &mut spans_suggs);
333 }
334 }
335 }
336 }
337 process_consecutive_brackets(None, &mut spans_suggs);
338
339 spans_suggs
340}
341
342impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
343 pub fn get_impl_ident_and_self_ty_from_trait(
344 tcx: TyCtxt<'tcx>,
345 def_id: DefId,
346 trait_objects: &FxIndexSet<DefId>,
347 ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
348 match tcx.hir_get_if_local(def_id)? {
349 Node::ImplItem(impl_item) => {
350 let impl_did = tcx.hir_get_parent_item(impl_item.hir_id());
351 if let hir::OwnerNode::Item(Item {
352 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
353 ..
354 }) = tcx.hir_owner_node(impl_did)
355 {
356 Some((impl_item.ident, self_ty))
357 } else {
358 None
359 }
360 }
361 Node::TraitItem(trait_item) => {
362 let trait_id = tcx.hir_get_parent_item(trait_item.hir_id());
363 debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
364 let trait_did = trait_id.to_def_id();
368 tcx.local_trait_impls(trait_did).iter().find_map(|&impl_did| {
369 if let Node::Item(Item {
370 kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
371 }) = tcx.hir_node_by_def_id(impl_did)
372 && trait_objects.iter().all(|did| {
373 let mut traits = vec![];
379 let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
380 hir_v.visit_ty_unambig(self_ty);
381 !traits.is_empty()
382 })
383 {
384 Some((trait_item.ident, *self_ty))
385 } else {
386 None
387 }
388 })
389 }
390 _ => None,
391 }
392 }
393}
394
395pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
397
398impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
399 fn visit_ty(&mut self, t: Ty<'tcx>) {
400 match t.kind() {
401 ty::Dynamic(preds, re, _) if re.is_static() => {
402 if let Some(def_id) = preds.principal_def_id() {
403 self.0.insert(def_id);
404 }
405 }
406 _ => t.super_visit_with(self),
407 }
408 }
409}
410
411pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
413
414impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
415 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) {
416 if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind
417 && let Lifetime { kind: LifetimeKind::ImplicitObjectLifetimeDefault, .. } =
418 lifetime_ptr.pointer()
419 {
420 for ptr in poly_trait_refs {
421 if Some(self.1) == ptr.trait_ref.trait_def_id() {
422 self.0.push(ptr.span);
423 }
424 }
425 }
426 walk_ty(self, t);
427 }
428}