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