]>
Commit | Line | Data |
---|---|---|
f2b60f7d FG |
1 | use hir::GenericParamKind; |
2 | use rustc_errors::{ | |
3 | fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan, | |
4 | }; | |
5 | use rustc_hir as hir; | |
6 | use rustc_hir::{FnRetTy, Ty}; | |
7 | use rustc_macros::SessionDiagnostic; | |
8 | use rustc_middle::ty::{Region, TyCtxt}; | |
9 | use rustc_span::symbol::kw; | |
10 | use rustc_span::{symbol::Ident, BytePos, Span}; | |
11 | ||
12 | use crate::infer::error_reporting::{ | |
13 | need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, | |
14 | ObligationCauseAsDiagArg, | |
15 | }; | |
16 | ||
17 | pub mod note_and_explain; | |
18 | ||
19 | #[derive(SessionDiagnostic)] | |
20 | #[diag(infer::opaque_hidden_type)] | |
21 | pub struct OpaqueHiddenTypeDiag { | |
22 | #[primary_span] | |
23 | #[label] | |
24 | pub span: Span, | |
25 | #[note(infer::opaque_type)] | |
26 | pub opaque_type: Span, | |
27 | #[note(infer::hidden_type)] | |
28 | pub hidden_type: Span, | |
29 | } | |
30 | ||
31 | #[derive(SessionDiagnostic)] | |
32 | #[diag(infer::type_annotations_needed, code = "E0282")] | |
33 | pub struct AnnotationRequired<'a> { | |
34 | #[primary_span] | |
35 | pub span: Span, | |
36 | pub source_kind: &'static str, | |
37 | pub source_name: &'a str, | |
38 | #[label] | |
39 | pub failure_span: Option<Span>, | |
40 | #[subdiagnostic] | |
41 | pub bad_label: Option<InferenceBadError<'a>>, | |
42 | #[subdiagnostic] | |
43 | pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, | |
44 | #[subdiagnostic] | |
45 | pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, | |
46 | } | |
47 | ||
48 | // Copy of `AnnotationRequired` for E0283 | |
49 | #[derive(SessionDiagnostic)] | |
50 | #[diag(infer::type_annotations_needed, code = "E0283")] | |
51 | pub struct AmbigousImpl<'a> { | |
52 | #[primary_span] | |
53 | pub span: Span, | |
54 | pub source_kind: &'static str, | |
55 | pub source_name: &'a str, | |
56 | #[label] | |
57 | pub failure_span: Option<Span>, | |
58 | #[subdiagnostic] | |
59 | pub bad_label: Option<InferenceBadError<'a>>, | |
60 | #[subdiagnostic] | |
61 | pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, | |
62 | #[subdiagnostic] | |
63 | pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, | |
64 | } | |
65 | ||
66 | // Copy of `AnnotationRequired` for E0284 | |
67 | #[derive(SessionDiagnostic)] | |
68 | #[diag(infer::type_annotations_needed, code = "E0284")] | |
69 | pub struct AmbigousReturn<'a> { | |
70 | #[primary_span] | |
71 | pub span: Span, | |
72 | pub source_kind: &'static str, | |
73 | pub source_name: &'a str, | |
74 | #[label] | |
75 | pub failure_span: Option<Span>, | |
76 | #[subdiagnostic] | |
77 | pub bad_label: Option<InferenceBadError<'a>>, | |
78 | #[subdiagnostic] | |
79 | pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, | |
80 | #[subdiagnostic] | |
81 | pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, | |
82 | } | |
83 | ||
84 | #[derive(SessionDiagnostic)] | |
85 | #[diag(infer::need_type_info_in_generator, code = "E0698")] | |
86 | pub struct NeedTypeInfoInGenerator<'a> { | |
87 | #[primary_span] | |
88 | pub span: Span, | |
89 | pub generator_kind: GeneratorKindAsDiagArg, | |
90 | #[subdiagnostic] | |
91 | pub bad_label: InferenceBadError<'a>, | |
92 | } | |
93 | ||
94 | // Used when a better one isn't available | |
95 | #[derive(SessionSubdiagnostic)] | |
96 | #[label(infer::label_bad)] | |
97 | pub struct InferenceBadError<'a> { | |
98 | #[primary_span] | |
99 | pub span: Span, | |
100 | pub bad_kind: &'static str, | |
101 | pub prefix_kind: UnderspecifiedArgKind, | |
102 | pub has_parent: bool, | |
103 | pub prefix: &'a str, | |
104 | pub parent_prefix: &'a str, | |
105 | pub parent_name: String, | |
106 | pub name: String, | |
107 | } | |
108 | ||
109 | #[derive(SessionSubdiagnostic)] | |
110 | pub enum SourceKindSubdiag<'a> { | |
111 | #[suggestion_verbose( | |
112 | infer::source_kind_subdiag_let, | |
113 | code = ": {type_name}", | |
114 | applicability = "has-placeholders" | |
115 | )] | |
116 | LetLike { | |
117 | #[primary_span] | |
118 | span: Span, | |
119 | name: String, | |
120 | type_name: String, | |
121 | kind: &'static str, | |
122 | x_kind: &'static str, | |
123 | prefix_kind: UnderspecifiedArgKind, | |
124 | prefix: &'a str, | |
125 | arg_name: String, | |
126 | }, | |
127 | #[label(infer::source_kind_subdiag_generic_label)] | |
128 | GenericLabel { | |
129 | #[primary_span] | |
130 | span: Span, | |
131 | is_type: bool, | |
132 | param_name: String, | |
133 | parent_exists: bool, | |
134 | parent_prefix: String, | |
135 | parent_name: String, | |
136 | }, | |
137 | #[suggestion_verbose( | |
138 | infer::source_kind_subdiag_generic_suggestion, | |
139 | code = "::<{args}>", | |
140 | applicability = "has-placeholders" | |
141 | )] | |
142 | GenericSuggestion { | |
143 | #[primary_span] | |
144 | span: Span, | |
145 | arg_count: usize, | |
146 | args: String, | |
147 | }, | |
148 | } | |
149 | ||
150 | #[derive(SessionSubdiagnostic)] | |
151 | pub enum SourceKindMultiSuggestion<'a> { | |
152 | #[multipart_suggestion_verbose( | |
153 | infer::source_kind_fully_qualified, | |
154 | applicability = "has-placeholders" | |
155 | )] | |
156 | FullyQualified { | |
157 | #[suggestion_part(code = "{def_path}({adjustment}")] | |
158 | span_lo: Span, | |
159 | #[suggestion_part(code = "{successor_pos}")] | |
160 | span_hi: Span, | |
161 | def_path: String, | |
162 | adjustment: &'a str, | |
163 | successor_pos: &'a str, | |
164 | }, | |
165 | #[multipart_suggestion_verbose( | |
166 | infer::source_kind_closure_return, | |
167 | applicability = "has-placeholders" | |
168 | )] | |
169 | ClosureReturn { | |
170 | #[suggestion_part(code = "{start_span_code}")] | |
171 | start_span: Span, | |
172 | start_span_code: String, | |
173 | #[suggestion_part(code = " }}")] | |
174 | end_span: Option<Span>, | |
175 | }, | |
176 | } | |
177 | ||
178 | impl<'a> SourceKindMultiSuggestion<'a> { | |
179 | pub fn new_fully_qualified( | |
180 | span: Span, | |
181 | def_path: String, | |
182 | adjustment: &'a str, | |
183 | successor: (&'a str, BytePos), | |
184 | ) -> Self { | |
185 | Self::FullyQualified { | |
186 | span_lo: span.shrink_to_lo(), | |
187 | span_hi: span.shrink_to_hi().with_hi(successor.1), | |
188 | def_path, | |
189 | adjustment, | |
190 | successor_pos: successor.0, | |
191 | } | |
192 | } | |
193 | ||
194 | pub fn new_closure_return( | |
195 | ty_info: String, | |
196 | data: &'a FnRetTy<'a>, | |
197 | should_wrap_expr: Option<Span>, | |
198 | ) -> Self { | |
199 | let (arrow, post) = match data { | |
200 | FnRetTy::DefaultReturn(_) => ("-> ", " "), | |
201 | _ => ("", ""), | |
202 | }; | |
203 | let (start_span, start_span_code, end_span) = match should_wrap_expr { | |
204 | Some(end_span) => { | |
205 | (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post), Some(end_span)) | |
206 | } | |
207 | None => (data.span(), format!("{}{}{}", arrow, ty_info, post), None), | |
208 | }; | |
209 | Self::ClosureReturn { start_span, start_span_code, end_span } | |
210 | } | |
211 | } | |
212 | ||
213 | pub enum RegionOriginNote<'a> { | |
214 | Plain { | |
215 | span: Span, | |
216 | msg: DiagnosticMessage, | |
217 | }, | |
218 | WithName { | |
219 | span: Span, | |
220 | msg: DiagnosticMessage, | |
221 | name: &'a str, | |
222 | continues: bool, | |
223 | }, | |
224 | WithRequirement { | |
225 | span: Span, | |
226 | requirement: ObligationCauseAsDiagArg<'a>, | |
227 | expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>, | |
228 | }, | |
229 | } | |
230 | ||
231 | impl AddSubdiagnostic for RegionOriginNote<'_> { | |
232 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
233 | let mut label_or_note = |span, msg: DiagnosticMessage| { | |
234 | let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); | |
235 | let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); | |
236 | let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); | |
237 | if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { | |
238 | diag.span_label(span, msg); | |
239 | } else if span_is_primary && expanded_sub_count == 0 { | |
240 | diag.note(msg); | |
241 | } else { | |
242 | diag.span_note(span, msg); | |
243 | } | |
244 | }; | |
245 | match self { | |
246 | RegionOriginNote::Plain { span, msg } => { | |
247 | label_or_note(span, msg); | |
248 | } | |
249 | RegionOriginNote::WithName { span, msg, name, continues } => { | |
250 | label_or_note(span, msg); | |
251 | diag.set_arg("name", name); | |
252 | diag.set_arg("continues", continues); | |
253 | } | |
254 | RegionOriginNote::WithRequirement { | |
255 | span, | |
256 | requirement, | |
257 | expected_found: Some((expected, found)), | |
258 | } => { | |
259 | label_or_note(span, fluent::infer::subtype); | |
260 | diag.set_arg("requirement", requirement); | |
261 | ||
262 | diag.note_expected_found(&"", expected, &"", found); | |
263 | } | |
264 | RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { | |
265 | // FIXME: this really should be handled at some earlier stage. Our | |
266 | // handling of region checking when type errors are present is | |
267 | // *terrible*. | |
268 | label_or_note(span, fluent::infer::subtype_2); | |
269 | diag.set_arg("requirement", requirement); | |
270 | } | |
271 | }; | |
272 | } | |
273 | } | |
274 | ||
275 | pub enum LifetimeMismatchLabels { | |
276 | InRet { | |
277 | param_span: Span, | |
278 | ret_span: Span, | |
279 | span: Span, | |
280 | label_var1: Option<Ident>, | |
281 | }, | |
282 | Normal { | |
283 | hir_equal: bool, | |
284 | ty_sup: Span, | |
285 | ty_sub: Span, | |
286 | span: Span, | |
287 | sup: Option<Ident>, | |
288 | sub: Option<Ident>, | |
289 | }, | |
290 | } | |
291 | ||
292 | impl AddSubdiagnostic for LifetimeMismatchLabels { | |
293 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
294 | match self { | |
295 | LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { | |
296 | diag.span_label(param_span, fluent::infer::declared_different); | |
297 | diag.span_label(ret_span, fluent::infer::nothing); | |
298 | diag.span_label(span, fluent::infer::data_returned); | |
299 | diag.set_arg("label_var1_exists", label_var1.is_some()); | |
300 | diag.set_arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); | |
301 | } | |
302 | LifetimeMismatchLabels::Normal { | |
303 | hir_equal, | |
304 | ty_sup, | |
305 | ty_sub, | |
306 | span, | |
307 | sup: label_var1, | |
308 | sub: label_var2, | |
309 | } => { | |
310 | if hir_equal { | |
311 | diag.span_label(ty_sup, fluent::infer::declared_multiple); | |
312 | diag.span_label(ty_sub, fluent::infer::nothing); | |
313 | diag.span_label(span, fluent::infer::data_lifetime_flow); | |
314 | } else { | |
315 | diag.span_label(ty_sup, fluent::infer::types_declared_different); | |
316 | diag.span_label(ty_sub, fluent::infer::nothing); | |
317 | diag.span_label(span, fluent::infer::data_flows); | |
318 | diag.set_arg("label_var1_exists", label_var1.is_some()); | |
319 | diag.set_arg( | |
320 | "label_var1", | |
321 | label_var1.map(|x| x.to_string()).unwrap_or_default(), | |
322 | ); | |
323 | diag.set_arg("label_var2_exists", label_var2.is_some()); | |
324 | diag.set_arg( | |
325 | "label_var2", | |
326 | label_var2.map(|x| x.to_string()).unwrap_or_default(), | |
327 | ); | |
328 | } | |
329 | } | |
330 | } | |
331 | } | |
332 | } | |
333 | ||
334 | pub struct AddLifetimeParamsSuggestion<'a> { | |
335 | pub tcx: TyCtxt<'a>, | |
336 | pub sub: Region<'a>, | |
337 | pub ty_sup: &'a Ty<'a>, | |
338 | pub ty_sub: &'a Ty<'a>, | |
339 | pub add_note: bool, | |
340 | } | |
341 | ||
342 | impl AddSubdiagnostic for AddLifetimeParamsSuggestion<'_> { | |
343 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
344 | let mut mk_suggestion = || { | |
345 | let ( | |
346 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, | |
347 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, | |
348 | ) = (self.ty_sub, self.ty_sup) else { | |
349 | return false; | |
350 | }; | |
351 | ||
352 | if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { | |
353 | return false; | |
354 | }; | |
355 | ||
356 | let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { | |
357 | return false; | |
358 | }; | |
359 | ||
360 | let hir_id = self.tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); | |
361 | ||
362 | let node = self.tcx.hir().get(hir_id); | |
363 | let is_impl = matches!(&node, hir::Node::ImplItem(_)); | |
364 | let generics = match node { | |
365 | hir::Node::Item(&hir::Item { | |
366 | kind: hir::ItemKind::Fn(_, ref generics, ..), | |
367 | .. | |
368 | }) | |
369 | | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) | |
370 | | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, | |
371 | _ => return false, | |
372 | }; | |
373 | ||
374 | let suggestion_param_name = generics | |
375 | .params | |
376 | .iter() | |
377 | .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) | |
378 | .map(|p| p.name.ident().name) | |
379 | .find(|i| *i != kw::UnderscoreLifetime); | |
380 | let introduce_new = suggestion_param_name.is_none(); | |
381 | let suggestion_param_name = | |
382 | suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); | |
383 | ||
384 | debug!(?lifetime_sup.span); | |
385 | debug!(?lifetime_sub.span); | |
386 | let make_suggestion = |span: rustc_span::Span| { | |
387 | if span.is_empty() { | |
388 | (span, format!("{}, ", suggestion_param_name)) | |
389 | } else if let Ok("&") = self.tcx.sess.source_map().span_to_snippet(span).as_deref() | |
390 | { | |
391 | (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) | |
392 | } else { | |
393 | (span, suggestion_param_name.clone()) | |
394 | } | |
395 | }; | |
396 | let mut suggestions = | |
397 | vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; | |
398 | ||
399 | if introduce_new { | |
400 | let new_param_suggestion = if let Some(first) = | |
401 | generics.params.iter().find(|p| !p.name.ident().span.is_empty()) | |
402 | { | |
403 | (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) | |
404 | } else { | |
405 | (generics.span, format!("<{}>", suggestion_param_name)) | |
406 | }; | |
407 | ||
408 | suggestions.push(new_param_suggestion); | |
409 | } | |
410 | ||
411 | diag.multipart_suggestion( | |
412 | fluent::infer::lifetime_param_suggestion, | |
413 | suggestions, | |
414 | Applicability::MaybeIncorrect, | |
415 | ); | |
416 | diag.set_arg("is_impl", is_impl); | |
417 | true | |
418 | }; | |
419 | if mk_suggestion() && self.add_note { | |
420 | diag.note(fluent::infer::lifetime_param_suggestion_elided); | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | #[derive(SessionDiagnostic)] | |
426 | #[diag(infer::lifetime_mismatch, code = "E0623")] | |
427 | pub struct LifetimeMismatch<'a> { | |
428 | #[primary_span] | |
429 | pub span: Span, | |
430 | #[subdiagnostic] | |
431 | pub labels: LifetimeMismatchLabels, | |
432 | #[subdiagnostic] | |
433 | pub suggestion: AddLifetimeParamsSuggestion<'a>, | |
434 | } | |
435 | ||
436 | pub struct IntroducesStaticBecauseUnmetLifetimeReq { | |
437 | pub unmet_requirements: MultiSpan, | |
438 | pub binding_span: Span, | |
439 | } | |
440 | ||
441 | impl AddSubdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { | |
442 | fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { | |
443 | self.unmet_requirements | |
444 | .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); | |
445 | diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req); | |
446 | } | |
447 | } | |
448 | ||
449 | pub struct ImplNote { | |
450 | pub impl_span: Option<Span>, | |
451 | } | |
452 | ||
453 | impl AddSubdiagnostic for ImplNote { | |
454 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
455 | match self.impl_span { | |
456 | Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), | |
457 | None => diag.note(fluent::infer::msl_impl_note), | |
458 | }; | |
459 | } | |
460 | } | |
461 | ||
462 | pub enum TraitSubdiag { | |
463 | Note { span: Span }, | |
464 | Sugg { span: Span }, | |
465 | } | |
466 | ||
467 | // FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support | |
468 | impl AddSubdiagnostic for TraitSubdiag { | |
469 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
470 | match self { | |
471 | TraitSubdiag::Note { span } => { | |
472 | diag.span_note(span, "this has an implicit `'static` lifetime requirement"); | |
473 | } | |
474 | TraitSubdiag::Sugg { span } => { | |
475 | diag.span_suggestion_verbose( | |
476 | span, | |
477 | "consider relaxing the implicit `'static` requirement", | |
478 | " + '_".to_owned(), | |
479 | rustc_errors::Applicability::MaybeIncorrect, | |
480 | ); | |
481 | } | |
482 | } | |
483 | } | |
484 | } | |
485 | ||
486 | #[derive(SessionDiagnostic)] | |
487 | #[diag(infer::mismatched_static_lifetime)] | |
488 | pub struct MismatchedStaticLifetime<'a> { | |
489 | #[primary_span] | |
490 | pub cause_span: Span, | |
491 | #[subdiagnostic] | |
492 | pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, | |
493 | #[subdiagnostic] | |
494 | pub expl: Option<note_and_explain::RegionExplanation<'a>>, | |
495 | #[subdiagnostic] | |
496 | pub impl_note: ImplNote, | |
497 | #[subdiagnostic] | |
498 | pub trait_subdiags: Vec<TraitSubdiag>, | |
499 | } |