]>
Commit | Line | Data |
---|---|---|
8faf50e0 XL |
1 | //! Error Reporting for static impl Traits. |
2 | ||
9c376795 FG |
3 | use crate::errors::{ |
4 | ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, | |
5 | ReqIntroducedLocations, | |
6 | }; | |
9fa01778 XL |
7 | use crate::infer::error_reporting::nice_region_error::NiceRegionError; |
8 | use crate::infer::lexical_region_resolve::RegionResolutionError; | |
3dfed10e XL |
9 | use crate::infer::{SubregionOrigin, TypeTrace}; |
10 | use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; | |
487cf647 | 11 | use rustc_data_structures::fx::FxIndexSet; |
9c376795 | 12 | use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; |
3dfed10e | 13 | use rustc_hir::def_id::DefId; |
5099ac24 | 14 | use rustc_hir::intravisit::{walk_ty, Visitor}; |
9c376795 FG |
15 | use rustc_hir::{ |
16 | self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node, | |
17 | TyKind, | |
18 | }; | |
94222f64 | 19 | use rustc_middle::ty::{ |
064997fb | 20 | self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, |
94222f64 | 21 | }; |
3dfed10e | 22 | use rustc_span::symbol::Ident; |
04454e1e | 23 | use rustc_span::Span; |
8faf50e0 | 24 | |
9c376795 | 25 | use rustc_span::def_id::LocalDefId; |
29967ef6 XL |
26 | use std::ops::ControlFlow; |
27 | ||
dc9dc135 | 28 | impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { |
3dfed10e XL |
29 | /// Print the error message for lifetime errors when the return type is a static `impl Trait`, |
30 | /// `dyn Trait` or if a method call on a trait object introduces a static requirement. | |
5e7ed085 | 31 | pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> { |
f035d41b | 32 | debug!("try_report_static_impl_trait(error={:?})", self.error); |
3dfed10e | 33 | let tcx = self.tcx(); |
a2a8927a | 34 | let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { |
3dfed10e XL |
35 | RegionResolutionError::SubSupConflict( |
36 | _, | |
37 | var_origin, | |
38 | sub_origin, | |
39 | sub_r, | |
40 | sup_origin, | |
41 | sup_r, | |
a2a8927a | 42 | spans, |
5099ac24 | 43 | ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans), |
3dfed10e XL |
44 | RegionResolutionError::ConcreteFailure( |
45 | SubregionOrigin::Subtype(box TypeTrace { cause, .. }), | |
46 | sub_r, | |
47 | sup_r, | |
5099ac24 | 48 | ) if sub_r.is_static() => { |
3dfed10e | 49 | // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. |
a2a8927a | 50 | if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { |
1b1a35ee XL |
51 | // This may have a closure and it would cause ICE |
52 | // through `find_param_with_region` (#78262). | |
5099ac24 | 53 | let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; |
1b1a35ee XL |
54 | let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); |
55 | if fn_returns.is_empty() { | |
56 | return None; | |
57 | } | |
58 | ||
5099ac24 | 59 | let param = self.find_param_with_region(*sup_r, *sub_r)?; |
9c376795 FG |
60 | let simple_ident = param.param.pat.simple_ident(); |
61 | ||
62 | let (has_impl_path, impl_path) = match ctxt.assoc_item.container { | |
63 | AssocItemContainer::TraitContainer => { | |
64 | let id = ctxt.assoc_item.container_id(tcx); | |
65 | (true, tcx.def_path_str(id)) | |
66 | } | |
67 | AssocItemContainer::ImplContainer => (false, String::new()), | |
3dfed10e | 68 | }; |
9c376795 FG |
69 | |
70 | let mut err = self.tcx().sess.create_err(ButCallingIntroduces { | |
71 | param_ty_span: param.param_ty_span, | |
72 | cause_span: cause.span, | |
73 | has_param_name: simple_ident.is_some(), | |
74 | param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), | |
75 | has_lifetime: sup_r.has_name(), | |
76 | lifetime: sup_r.to_string(), | |
77 | assoc_item: ctxt.assoc_item.name, | |
78 | has_impl_path, | |
79 | impl_path, | |
80 | }); | |
3dfed10e | 81 | if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { |
5e7ed085 FG |
82 | let reported = err.emit(); |
83 | return Some(reported); | |
3dfed10e | 84 | } else { |
9c376795 | 85 | err.cancel() |
3dfed10e XL |
86 | } |
87 | } | |
f035d41b XL |
88 | return None; |
89 | } | |
3dfed10e XL |
90 | _ => return None, |
91 | }; | |
92 | debug!( | |
93 | "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", | |
94 | var_origin, sub_origin, sub_r, sup_origin, sup_r | |
95 | ); | |
5099ac24 | 96 | let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; |
3dfed10e XL |
97 | debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); |
98 | let sp = var_origin.span(); | |
99 | let return_sp = sub_origin.span(); | |
5099ac24 | 100 | let param = self.find_param_with_region(*sup_r, *sub_r)?; |
9c376795 | 101 | let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; |
3dfed10e | 102 | |
a2a8927a XL |
103 | let (mention_influencer, influencer_point) = |
104 | if sup_origin.span().overlaps(param.param_ty_span) { | |
105 | // Account for `async fn` like in `async-await/issues/issue-62097.rs`. | |
106 | // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same | |
107 | // place (but with different `ctxt`, hence `overlaps` instead of `==` above). | |
3dfed10e | 108 | // |
a2a8927a | 109 | // This avoids the following: |
3dfed10e | 110 | // |
a2a8927a XL |
111 | // LL | pub async fn run_dummy_fn(&self) { |
112 | // | ^^^^^ | |
113 | // | | | |
114 | // | this data with an anonymous lifetime `'_`... | |
115 | // | ...is captured here... | |
116 | (false, sup_origin.span()) | |
3dfed10e | 117 | } else { |
a2a8927a XL |
118 | (!sup_origin.span().overlaps(return_sp), param.param_ty_span) |
119 | }; | |
a2a8927a XL |
120 | |
121 | debug!("try_report_static_impl_trait: param_info={:?}", param); | |
122 | ||
123 | let mut spans = spans.clone(); | |
124 | ||
125 | if mention_influencer { | |
126 | spans.push(sup_origin.span()); | |
127 | } | |
128 | // We dedup the spans *ignoring* expansion context. | |
129 | spans.sort(); | |
130 | spans.dedup_by_key(|span| (span.lo(), span.hi())); | |
131 | ||
132 | // We try to make the output have fewer overlapping spans if possible. | |
a2a8927a XL |
133 | let require_span = |
134 | if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; | |
135 | ||
9c376795 FG |
136 | let spans_empty = spans.is_empty(); |
137 | let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp); | |
138 | let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { | |
139 | Some(*bound) | |
a2a8927a | 140 | } else { |
9c376795 FG |
141 | None |
142 | }; | |
143 | ||
144 | let mut subdiag = None; | |
a2a8927a | 145 | |
a2a8927a XL |
146 | if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { |
147 | if let ObligationCauseCode::ReturnValue(hir_id) | |
148 | | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() | |
149 | { | |
150 | let parent_id = tcx.hir().get_parent_item(*hir_id); | |
2b03887a | 151 | if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { |
a2a8927a | 152 | let mut span: MultiSpan = fn_decl.output.span().into(); |
9c376795 | 153 | let mut spans = Vec::new(); |
a2a8927a XL |
154 | let mut add_label = true; |
155 | if let hir::FnRetTy::Return(ty) = fn_decl.output { | |
156 | let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); | |
157 | v.visit_ty(ty); | |
158 | if !v.0.is_empty() { | |
159 | span = v.0.clone().into(); | |
9c376795 | 160 | spans = v.0; |
a2a8927a XL |
161 | add_label = false; |
162 | } | |
163 | } | |
9c376795 FG |
164 | let fn_decl_span = fn_decl.output.span(); |
165 | ||
166 | subdiag = Some(ReqIntroducedLocations { | |
a2a8927a | 167 | span, |
9c376795 FG |
168 | spans, |
169 | fn_decl_span, | |
170 | cause_span: cause.span, | |
171 | add_label, | |
172 | }); | |
3dfed10e XL |
173 | } |
174 | } | |
3dfed10e XL |
175 | } |
176 | ||
9c376795 FG |
177 | let diag = ButNeedsToSatisfy { |
178 | sp, | |
179 | influencer_point, | |
180 | spans: spans.clone(), | |
181 | // If any of the "captured here" labels appears on the same line or after | |
182 | // `require_span`, we put it on a note to ensure the text flows by appearing | |
183 | // always at the end. | |
184 | require_span_as_note: require_as_note.then_some(require_span), | |
185 | // We don't need a note, it's already at the end, it can be shown as a `span_label`. | |
186 | require_span_as_label: (!require_as_note).then_some(require_span), | |
187 | req_introduces_loc: subdiag, | |
188 | ||
189 | has_lifetime: sup_r.has_name(), | |
190 | lifetime: sup_r.to_string(), | |
191 | spans_empty, | |
192 | bound, | |
193 | }; | |
194 | ||
195 | let mut err = self.tcx().sess.create_err(diag); | |
196 | ||
3dfed10e | 197 | let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); |
0bf4aa26 | 198 | |
3dfed10e | 199 | let mut override_error_code = None; |
5e7ed085 FG |
200 | if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin |
201 | && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() | |
202 | // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a | |
203 | // `'static` lifetime when called as a method on a binding: `bar.qux()`. | |
204 | && self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) | |
205 | { | |
206 | override_error_code = Some(ctxt.assoc_item.name); | |
3dfed10e | 207 | } |
5e7ed085 FG |
208 | |
209 | if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin | |
210 | && let code = match cause.code() { | |
a2a8927a XL |
211 | ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), |
212 | _ => cause.code(), | |
5e7ed085 | 213 | } |
f2b60f7d | 214 | && let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code) |
5e7ed085 FG |
215 | { |
216 | // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` | |
217 | // lifetime as above, but called using a fully-qualified path to the method: | |
218 | // `Foo::qux(bar)`. | |
487cf647 | 219 | let mut v = TraitObjectVisitor(FxIndexSet::default()); |
5e7ed085 FG |
220 | v.visit_ty(param.param_ty); |
221 | if let Some((ident, self_ty)) = | |
9c376795 | 222 | NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0) |
5e7ed085 | 223 | && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) |
3c0e092e | 224 | { |
5e7ed085 | 225 | override_error_code = Some(ident.name); |
3dfed10e XL |
226 | } |
227 | } | |
228 | if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { | |
229 | // Provide a more targeted error code and description. | |
9c376795 FG |
230 | let retarget_subdiag = MoreTargeted { ident }; |
231 | retarget_subdiag.add_to_diagnostic(&mut err); | |
3dfed10e XL |
232 | } |
233 | ||
3dfed10e XL |
234 | let arg = match param.param.pat.simple_ident() { |
235 | Some(simple_ident) => format!("argument `{}`", simple_ident), | |
236 | None => "the argument".to_string(), | |
237 | }; | |
3dfed10e | 238 | let captures = format!("captures data from {}", arg); |
c295e0f8 XL |
239 | suggest_new_region_bound( |
240 | tcx, | |
241 | &mut err, | |
242 | fn_returns, | |
243 | lifetime_name, | |
244 | Some(arg), | |
245 | captures, | |
246 | Some((param.param_ty_span, param.param_ty.to_string())), | |
9c376795 | 247 | Some(anon_reg_sup.def_id), |
c295e0f8 XL |
248 | ); |
249 | ||
5e7ed085 FG |
250 | let reported = err.emit(); |
251 | Some(reported) | |
c295e0f8 XL |
252 | } |
253 | } | |
3dfed10e | 254 | |
c295e0f8 | 255 | pub fn suggest_new_region_bound( |
a2a8927a | 256 | tcx: TyCtxt<'_>, |
5e7ed085 | 257 | err: &mut Diagnostic, |
c295e0f8 XL |
258 | fn_returns: Vec<&rustc_hir::Ty<'_>>, |
259 | lifetime_name: String, | |
260 | arg: Option<String>, | |
261 | captures: String, | |
262 | param: Option<(Span, String)>, | |
9c376795 | 263 | scope_def_id: Option<LocalDefId>, |
c295e0f8 XL |
264 | ) { |
265 | debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); | |
266 | // FIXME: account for the need of parens in `&(dyn Trait + '_)` | |
2b03887a FG |
267 | let consider = "consider changing"; |
268 | let declare = "to declare that"; | |
c295e0f8 XL |
269 | let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); |
270 | let explicit_static = | |
271 | arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg)); | |
272 | let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; | |
273 | let plus_lt = format!(" + {}", lifetime_name); | |
274 | for fn_return in fn_returns { | |
275 | if fn_return.span.desugaring_kind().is_some() { | |
276 | // Skip `async` desugaring `impl Future`. | |
277 | continue; | |
278 | } | |
279 | match fn_return.kind { | |
f2b60f7d | 280 | TyKind::OpaqueDef(item_id, _, _) => { |
c295e0f8 | 281 | let item = tcx.hir().item(item_id); |
3c0e092e | 282 | let ItemKind::OpaqueTy(opaque) = &item.kind else { |
c295e0f8 XL |
283 | return; |
284 | }; | |
285 | ||
2b03887a FG |
286 | // Get the identity type for this RPIT |
287 | let did = item_id.owner_id.to_def_id(); | |
288 | let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did)); | |
289 | ||
9c376795 FG |
290 | if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { |
291 | GenericBound::Outlives(Lifetime { | |
292 | res: LifetimeName::Static, ident, .. | |
293 | }) => Some(ident.span), | |
294 | _ => None, | |
295 | }) { | |
c295e0f8 | 296 | if let Some(explicit_static) = &explicit_static { |
3dfed10e XL |
297 | err.span_suggestion_verbose( |
298 | span, | |
2b03887a | 299 | &format!("{consider} `{ty}`'s {explicit_static}"), |
923072b8 | 300 | &lifetime_name, |
3dfed10e XL |
301 | Applicability::MaybeIncorrect, |
302 | ); | |
c295e0f8 | 303 | } |
9c376795 | 304 | if let Some((param_span, ref param_ty)) = param { |
3dfed10e | 305 | err.span_suggestion_verbose( |
c295e0f8 | 306 | param_span, |
3dfed10e | 307 | add_static_bound, |
c295e0f8 | 308 | param_ty, |
3dfed10e XL |
309 | Applicability::MaybeIncorrect, |
310 | ); | |
f9f354fc | 311 | } |
9c376795 FG |
312 | } else if opaque.bounds.iter().any(|arg| match arg { |
313 | GenericBound::Outlives(Lifetime { ident, .. }) | |
314 | if ident.name.to_string() == lifetime_name => | |
315 | { | |
316 | true | |
317 | } | |
318 | _ => false, | |
319 | }) { | |
c295e0f8 | 320 | } else { |
9c376795 FG |
321 | // get a lifetime name of existing named lifetimes if any |
322 | let existing_lt_name = if let Some(id) = scope_def_id | |
323 | && let Some(generics) = tcx.hir().get_generics(id) | |
324 | && let named_lifetimes = generics | |
325 | .params | |
326 | .iter() | |
327 | .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) | |
328 | .map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}}) | |
329 | .filter(|n| ! matches!(n, None)) | |
330 | .collect::<Vec<_>>() | |
331 | && named_lifetimes.len() > 0 { | |
332 | named_lifetimes[0].clone() | |
333 | } else { | |
334 | None | |
335 | }; | |
336 | let name = if let Some(name) = &existing_lt_name { | |
337 | format!("{}", name) | |
338 | } else { | |
339 | format!("'a") | |
340 | }; | |
341 | // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. | |
342 | // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any | |
343 | if let Some(id) = scope_def_id | |
344 | && let Some(generics) = tcx.hir().get_generics(id) | |
345 | && let mut spans_suggs = generics | |
346 | .params | |
347 | .iter() | |
348 | .filter(|p| p.is_elided_lifetime()) | |
349 | .map(|p| | |
350 | if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_) | |
351 | (p.span.shrink_to_hi(),format!("{name} ")) | |
352 | } else { // Underscore (elided with '_) | |
353 | (p.span, format!("{name}")) | |
354 | } | |
355 | ) | |
356 | .collect::<Vec<_>>() | |
357 | && spans_suggs.len() > 1 | |
358 | { | |
359 | let use_lt = | |
360 | if existing_lt_name == None { | |
361 | spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>"))); | |
362 | format!("you can introduce a named lifetime parameter `{name}`") | |
363 | } else { | |
364 | // make use the existing named lifetime | |
365 | format!("you can use the named lifetime parameter `{name}`") | |
366 | }; | |
367 | spans_suggs | |
368 | .push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); | |
369 | err.multipart_suggestion_verbose( | |
370 | &format!( | |
371 | "{declare} `{ty}` {captures}, {use_lt}", | |
372 | ), | |
373 | spans_suggs, | |
374 | Applicability::MaybeIncorrect, | |
375 | ); | |
376 | } else { | |
377 | err.span_suggestion_verbose( | |
378 | fn_return.span.shrink_to_hi(), | |
379 | &format!("{declare} `{ty}` {captures}, {explicit}",), | |
380 | &plus_lt, | |
381 | Applicability::MaybeIncorrect, | |
382 | ); | |
383 | } | |
f035d41b | 384 | } |
c295e0f8 | 385 | } |
487cf647 FG |
386 | TyKind::TraitObject(_, lt, _) => { |
387 | if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { | |
c295e0f8 XL |
388 | err.span_suggestion_verbose( |
389 | fn_return.span.shrink_to_hi(), | |
390 | &format!( | |
2b03887a | 391 | "{declare} the trait object {captures}, {explicit}", |
c295e0f8 XL |
392 | declare = declare, |
393 | captures = captures, | |
394 | explicit = explicit, | |
395 | ), | |
923072b8 | 396 | &plus_lt, |
c295e0f8 XL |
397 | Applicability::MaybeIncorrect, |
398 | ); | |
487cf647 | 399 | } else if lt.ident.name.to_string() != lifetime_name { |
c295e0f8 XL |
400 | // With this check we avoid suggesting redundant bounds. This |
401 | // would happen if there are nested impl/dyn traits and only | |
402 | // one of them has the bound we'd suggest already there, like | |
403 | // in `impl Foo<X = dyn Bar> + '_`. | |
404 | if let Some(explicit_static) = &explicit_static { | |
3dfed10e | 405 | err.span_suggestion_verbose( |
487cf647 | 406 | lt.ident.span, |
2b03887a | 407 | &format!("{} the trait object's {}", consider, explicit_static), |
923072b8 | 408 | &lifetime_name, |
3dfed10e XL |
409 | Applicability::MaybeIncorrect, |
410 | ); | |
c295e0f8 XL |
411 | } |
412 | if let Some((param_span, param_ty)) = param.clone() { | |
3dfed10e | 413 | err.span_suggestion_verbose( |
c295e0f8 | 414 | param_span, |
3dfed10e | 415 | add_static_bound, |
c295e0f8 | 416 | param_ty, |
3dfed10e XL |
417 | Applicability::MaybeIncorrect, |
418 | ); | |
419 | } | |
c295e0f8 | 420 | } |
487cf647 | 421 | } |
c295e0f8 | 422 | _ => {} |
3dfed10e | 423 | } |
3dfed10e | 424 | } |
c295e0f8 | 425 | } |
f9f354fc | 426 | |
c295e0f8 | 427 | impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { |
9c376795 FG |
428 | pub fn get_impl_ident_and_self_ty_from_trait( |
429 | tcx: TyCtxt<'tcx>, | |
3dfed10e | 430 | def_id: DefId, |
487cf647 | 431 | trait_objects: &FxIndexSet<DefId>, |
3dfed10e | 432 | ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { |
9c376795 FG |
433 | match tcx.hir().get_if_local(def_id)? { |
434 | Node::ImplItem(impl_item) => { | |
435 | let impl_did = tcx.hir().get_parent_item(impl_item.hir_id()); | |
436 | if let hir::OwnerNode::Item(Item { | |
437 | kind: ItemKind::Impl(hir::Impl { self_ty, .. }), | |
438 | .. | |
439 | }) = tcx.hir().owner(impl_did) | |
2b03887a | 440 | { |
9c376795 FG |
441 | Some((impl_item.ident, self_ty)) |
442 | } else { | |
443 | None | |
3dfed10e XL |
444 | } |
445 | } | |
9c376795 FG |
446 | Node::TraitItem(trait_item) => { |
447 | let trait_id = tcx.hir().get_parent_item(trait_item.hir_id()); | |
448 | debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait); | |
449 | // The method being called is defined in the `trait`, but the `'static` | |
450 | // obligation comes from the `impl`. Find that `impl` so that we can point | |
451 | // at it in the suggestion. | |
452 | let trait_did = trait_id.to_def_id(); | |
453 | tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| { | |
454 | if let Node::Item(Item { | |
455 | kind: ItemKind::Impl(hir::Impl { self_ty, .. }), | |
456 | .. | |
457 | }) = tcx.hir().find_by_def_id(impl_did)? | |
458 | && trait_objects.iter().all(|did| { | |
459 | // FIXME: we should check `self_ty` against the receiver | |
460 | // type in the `UnifyReceiver` context, but for now, use | |
461 | // this imperfect proxy. This will fail if there are | |
462 | // multiple `impl`s for the same trait like | |
463 | // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. | |
464 | // In that case, only the first one will get suggestions. | |
465 | let mut traits = vec![]; | |
466 | let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); | |
467 | hir_v.visit_ty(self_ty); | |
468 | !traits.is_empty() | |
469 | }) | |
470 | { | |
471 | Some((trait_item.ident, *self_ty)) | |
472 | } else { | |
473 | None | |
3dfed10e | 474 | } |
9c376795 | 475 | }) |
3dfed10e XL |
476 | } |
477 | _ => None, | |
478 | } | |
479 | } | |
480 | ||
481 | /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default | |
482 | /// `'static` obligation. Suggest relaxing that implicit bound. | |
483 | fn find_impl_on_dyn_trait( | |
484 | &self, | |
5e7ed085 | 485 | err: &mut Diagnostic, |
3dfed10e XL |
486 | ty: Ty<'_>, |
487 | ctxt: &UnifyReceiverContext<'tcx>, | |
488 | ) -> bool { | |
489 | let tcx = self.tcx(); | |
490 | ||
491 | // Find the method being called. | |
5e7ed085 | 492 | let Ok(Some(instance)) = ty::Instance::resolve( |
3dfed10e XL |
493 | tcx, |
494 | ctxt.param_env, | |
495 | ctxt.assoc_item.def_id, | |
2b03887a | 496 | self.cx.resolve_vars_if_possible(ctxt.substs), |
5e7ed085 FG |
497 | ) else { |
498 | return false; | |
3dfed10e XL |
499 | }; |
500 | ||
487cf647 | 501 | let mut v = TraitObjectVisitor(FxIndexSet::default()); |
3dfed10e XL |
502 | v.visit_ty(ty); |
503 | ||
504 | // Get the `Ident` of the method being called and the corresponding `impl` (to point at | |
505 | // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called). | |
9c376795 | 506 | let Some((ident, self_ty)) = NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) else { |
5e7ed085 FG |
507 | return false; |
508 | }; | |
3dfed10e XL |
509 | |
510 | // Find the trait object types in the argument, so we point at *only* the trait object. | |
136023e0 | 511 | self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty) |
3dfed10e XL |
512 | } |
513 | ||
514 | fn suggest_constrain_dyn_trait_in_impl( | |
515 | &self, | |
5e7ed085 | 516 | err: &mut Diagnostic, |
487cf647 | 517 | found_dids: &FxIndexSet<DefId>, |
3dfed10e XL |
518 | ident: Ident, |
519 | self_ty: &hir::Ty<'_>, | |
520 | ) -> bool { | |
521 | let mut suggested = false; | |
522 | for found_did in found_dids { | |
136023e0 XL |
523 | let mut traits = vec![]; |
524 | let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); | |
3dfed10e | 525 | hir_v.visit_ty(&self_ty); |
9c376795 FG |
526 | for &span in &traits { |
527 | let subdiag = DynTraitConstraintSuggestion { span, ident }; | |
528 | subdiag.add_to_diagnostic(err); | |
3dfed10e XL |
529 | suggested = true; |
530 | } | |
531 | } | |
532 | suggested | |
533 | } | |
534 | } | |
535 | ||
536 | /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. | |
487cf647 | 537 | pub struct TraitObjectVisitor(pub FxIndexSet<DefId>); |
3dfed10e | 538 | |
94222f64 | 539 | impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { |
94222f64 | 540 | fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { |
1b1a35ee | 541 | match t.kind() { |
f2b60f7d | 542 | ty::Dynamic(preds, re, _) if re.is_static() => { |
3dfed10e | 543 | if let Some(def_id) = preds.principal_def_id() { |
136023e0 | 544 | self.0.insert(def_id); |
3dfed10e | 545 | } |
9c376795 | 546 | ControlFlow::Continue(()) |
3dfed10e XL |
547 | } |
548 | _ => t.super_visit_with(self), | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. | |
923072b8 | 554 | pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId); |
3dfed10e | 555 | |
136023e0 | 556 | impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { |
3dfed10e | 557 | fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { |
1b1a35ee XL |
558 | if let TyKind::TraitObject( |
559 | poly_trait_refs, | |
487cf647 | 560 | Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, |
6a06907d | 561 | _, |
1b1a35ee XL |
562 | ) = t.kind |
563 | { | |
564 | for ptr in poly_trait_refs { | |
565 | if Some(self.1) == ptr.trait_ref.trait_def_id() { | |
566 | self.0.push(ptr.span); | |
8faf50e0 | 567 | } |
8faf50e0 XL |
568 | } |
569 | } | |
3dfed10e | 570 | walk_ty(self, t); |
8faf50e0 XL |
571 | } |
572 | } |