| use hir::GenericParamKind; |
| use rustc_errors::{ |
| fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, |
| MultiSpan, SubdiagnosticMessage, |
| }; |
| use rustc_hir as hir; |
| use rustc_hir::{FnRetTy, Ty}; |
| use rustc_macros::{Diagnostic, Subdiagnostic}; |
| use rustc_middle::ty::{Region, TyCtxt}; |
| use rustc_span::symbol::kw; |
| use rustc_span::{symbol::Ident, BytePos, Span}; |
| |
| use crate::infer::error_reporting::{ |
| need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, |
| ObligationCauseAsDiagArg, |
| }; |
| |
| pub mod note_and_explain; |
| |
| #[derive(Diagnostic)] |
| #[diag(infer_opaque_hidden_type)] |
| pub struct OpaqueHiddenTypeDiag { |
| #[primary_span] |
| #[label] |
| pub span: Span, |
| #[note(opaque_type)] |
| pub opaque_type: Span, |
| #[note(hidden_type)] |
| pub hidden_type: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(infer_type_annotations_needed, code = "E0282")] |
| pub struct AnnotationRequired<'a> { |
| #[primary_span] |
| pub span: Span, |
| pub source_kind: &'static str, |
| pub source_name: &'a str, |
| #[label] |
| pub failure_span: Option<Span>, |
| #[subdiagnostic] |
| pub bad_label: Option<InferenceBadError<'a>>, |
| #[subdiagnostic] |
| pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, |
| #[subdiagnostic] |
| pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, |
| } |
| |
| // Copy of `AnnotationRequired` for E0283 |
| #[derive(Diagnostic)] |
| #[diag(infer_type_annotations_needed, code = "E0283")] |
| pub struct AmbigousImpl<'a> { |
| #[primary_span] |
| pub span: Span, |
| pub source_kind: &'static str, |
| pub source_name: &'a str, |
| #[label] |
| pub failure_span: Option<Span>, |
| #[subdiagnostic] |
| pub bad_label: Option<InferenceBadError<'a>>, |
| #[subdiagnostic] |
| pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, |
| #[subdiagnostic] |
| pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, |
| } |
| |
| // Copy of `AnnotationRequired` for E0284 |
| #[derive(Diagnostic)] |
| #[diag(infer_type_annotations_needed, code = "E0284")] |
| pub struct AmbigousReturn<'a> { |
| #[primary_span] |
| pub span: Span, |
| pub source_kind: &'static str, |
| pub source_name: &'a str, |
| #[label] |
| pub failure_span: Option<Span>, |
| #[subdiagnostic] |
| pub bad_label: Option<InferenceBadError<'a>>, |
| #[subdiagnostic] |
| pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, |
| #[subdiagnostic] |
| pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(infer_need_type_info_in_generator, code = "E0698")] |
| pub struct NeedTypeInfoInGenerator<'a> { |
| #[primary_span] |
| pub span: Span, |
| pub generator_kind: GeneratorKindAsDiagArg, |
| #[subdiagnostic] |
| pub bad_label: InferenceBadError<'a>, |
| } |
| |
| // Used when a better one isn't available |
| #[derive(Subdiagnostic)] |
| #[label(infer_label_bad)] |
| pub struct InferenceBadError<'a> { |
| #[primary_span] |
| pub span: Span, |
| pub bad_kind: &'static str, |
| pub prefix_kind: UnderspecifiedArgKind, |
| pub has_parent: bool, |
| pub prefix: &'a str, |
| pub parent_prefix: &'a str, |
| pub parent_name: String, |
| pub name: String, |
| } |
| |
| #[derive(Subdiagnostic)] |
| pub enum SourceKindSubdiag<'a> { |
| #[suggestion_verbose( |
| infer_source_kind_subdiag_let, |
| code = ": {type_name}", |
| applicability = "has-placeholders" |
| )] |
| LetLike { |
| #[primary_span] |
| span: Span, |
| name: String, |
| type_name: String, |
| kind: &'static str, |
| x_kind: &'static str, |
| prefix_kind: UnderspecifiedArgKind, |
| prefix: &'a str, |
| arg_name: String, |
| }, |
| #[label(infer_source_kind_subdiag_generic_label)] |
| GenericLabel { |
| #[primary_span] |
| span: Span, |
| is_type: bool, |
| param_name: String, |
| parent_exists: bool, |
| parent_prefix: String, |
| parent_name: String, |
| }, |
| #[suggestion_verbose( |
| infer_source_kind_subdiag_generic_suggestion, |
| code = "::<{args}>", |
| applicability = "has-placeholders" |
| )] |
| GenericSuggestion { |
| #[primary_span] |
| span: Span, |
| arg_count: usize, |
| args: String, |
| }, |
| } |
| |
| #[derive(Subdiagnostic)] |
| pub enum SourceKindMultiSuggestion<'a> { |
| #[multipart_suggestion_verbose( |
| infer_source_kind_fully_qualified, |
| applicability = "has-placeholders" |
| )] |
| FullyQualified { |
| #[suggestion_part(code = "{def_path}({adjustment}")] |
| span_lo: Span, |
| #[suggestion_part(code = "{successor_pos}")] |
| span_hi: Span, |
| def_path: String, |
| adjustment: &'a str, |
| successor_pos: &'a str, |
| }, |
| #[multipart_suggestion_verbose( |
| infer_source_kind_closure_return, |
| applicability = "has-placeholders" |
| )] |
| ClosureReturn { |
| #[suggestion_part(code = "{start_span_code}")] |
| start_span: Span, |
| start_span_code: String, |
| #[suggestion_part(code = " }}")] |
| end_span: Option<Span>, |
| }, |
| } |
| |
| impl<'a> SourceKindMultiSuggestion<'a> { |
| pub fn new_fully_qualified( |
| span: Span, |
| def_path: String, |
| adjustment: &'a str, |
| successor: (&'a str, BytePos), |
| ) -> Self { |
| Self::FullyQualified { |
| span_lo: span.shrink_to_lo(), |
| span_hi: span.shrink_to_hi().with_hi(successor.1), |
| def_path, |
| adjustment, |
| successor_pos: successor.0, |
| } |
| } |
| |
| pub fn new_closure_return( |
| ty_info: String, |
| data: &'a FnRetTy<'a>, |
| should_wrap_expr: Option<Span>, |
| ) -> Self { |
| let (arrow, post) = match data { |
| FnRetTy::DefaultReturn(_) => ("-> ", " "), |
| _ => ("", ""), |
| }; |
| let (start_span, start_span_code, end_span) = match should_wrap_expr { |
| Some(end_span) => { |
| (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post), Some(end_span)) |
| } |
| None => (data.span(), format!("{}{}{}", arrow, ty_info, post), None), |
| }; |
| Self::ClosureReturn { start_span, start_span_code, end_span } |
| } |
| } |
| |
| pub enum RegionOriginNote<'a> { |
| Plain { |
| span: Span, |
| msg: DiagnosticMessage, |
| }, |
| WithName { |
| span: Span, |
| msg: DiagnosticMessage, |
| name: &'a str, |
| continues: bool, |
| }, |
| WithRequirement { |
| span: Span, |
| requirement: ObligationCauseAsDiagArg<'a>, |
| expected_found: Option<(DiagnosticStyledString, DiagnosticStyledString)>, |
| }, |
| } |
| |
| impl AddToDiagnostic for RegionOriginNote<'_> { |
| fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) |
| where |
| F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, |
| { |
| let mut label_or_note = |span, msg: DiagnosticMessage| { |
| let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); |
| let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); |
| let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); |
| if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { |
| diag.span_label(span, msg); |
| } else if span_is_primary && expanded_sub_count == 0 { |
| diag.note(msg); |
| } else { |
| diag.span_note(span, msg); |
| } |
| }; |
| match self { |
| RegionOriginNote::Plain { span, msg } => { |
| label_or_note(span, msg); |
| } |
| RegionOriginNote::WithName { span, msg, name, continues } => { |
| label_or_note(span, msg); |
| diag.set_arg("name", name); |
| diag.set_arg("continues", continues); |
| } |
| RegionOriginNote::WithRequirement { |
| span, |
| requirement, |
| expected_found: Some((expected, found)), |
| } => { |
| label_or_note(span, fluent::infer_subtype); |
| diag.set_arg("requirement", requirement); |
| |
| diag.note_expected_found(&"", expected, &"", found); |
| } |
| RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { |
| // FIXME: this really should be handled at some earlier stage. Our |
| // handling of region checking when type errors are present is |
| // *terrible*. |
| label_or_note(span, fluent::infer_subtype_2); |
| diag.set_arg("requirement", requirement); |
| } |
| }; |
| } |
| } |
| |
| pub enum LifetimeMismatchLabels { |
| InRet { |
| param_span: Span, |
| ret_span: Span, |
| span: Span, |
| label_var1: Option<Ident>, |
| }, |
| Normal { |
| hir_equal: bool, |
| ty_sup: Span, |
| ty_sub: Span, |
| span: Span, |
| sup: Option<Ident>, |
| sub: Option<Ident>, |
| }, |
| } |
| |
| impl AddToDiagnostic for LifetimeMismatchLabels { |
| fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) |
| where |
| F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, |
| { |
| match self { |
| LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { |
| diag.span_label(param_span, fluent::infer_declared_different); |
| diag.span_label(ret_span, fluent::infer_nothing); |
| diag.span_label(span, fluent::infer_data_returned); |
| diag.set_arg("label_var1_exists", label_var1.is_some()); |
| diag.set_arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); |
| } |
| LifetimeMismatchLabels::Normal { |
| hir_equal, |
| ty_sup, |
| ty_sub, |
| span, |
| sup: label_var1, |
| sub: label_var2, |
| } => { |
| if hir_equal { |
| diag.span_label(ty_sup, fluent::infer_declared_multiple); |
| diag.span_label(ty_sub, fluent::infer_nothing); |
| diag.span_label(span, fluent::infer_data_lifetime_flow); |
| } else { |
| diag.span_label(ty_sup, fluent::infer_types_declared_different); |
| diag.span_label(ty_sub, fluent::infer_nothing); |
| diag.span_label(span, fluent::infer_data_flows); |
| diag.set_arg("label_var1_exists", label_var1.is_some()); |
| diag.set_arg( |
| "label_var1", |
| label_var1.map(|x| x.to_string()).unwrap_or_default(), |
| ); |
| diag.set_arg("label_var2_exists", label_var2.is_some()); |
| diag.set_arg( |
| "label_var2", |
| label_var2.map(|x| x.to_string()).unwrap_or_default(), |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| pub struct AddLifetimeParamsSuggestion<'a> { |
| pub tcx: TyCtxt<'a>, |
| pub sub: Region<'a>, |
| pub ty_sup: &'a Ty<'a>, |
| pub ty_sub: &'a Ty<'a>, |
| pub add_note: bool, |
| } |
| |
| impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { |
| fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) |
| where |
| F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, |
| { |
| let mut mk_suggestion = || { |
| let ( |
| hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, |
| hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, |
| ) = (self.ty_sub, self.ty_sup) else { |
| return false; |
| }; |
| |
| if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { |
| return false; |
| }; |
| |
| let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { |
| return false; |
| }; |
| |
| let hir_id = self.tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); |
| |
| let node = self.tcx.hir().get(hir_id); |
| let is_impl = matches!(&node, hir::Node::ImplItem(_)); |
| let generics = match node { |
| hir::Node::Item(&hir::Item { |
| kind: hir::ItemKind::Fn(_, ref generics, ..), |
| .. |
| }) |
| | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) |
| | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, |
| _ => return false, |
| }; |
| |
| let suggestion_param_name = generics |
| .params |
| .iter() |
| .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) |
| .map(|p| p.name.ident().name) |
| .find(|i| *i != kw::UnderscoreLifetime); |
| let introduce_new = suggestion_param_name.is_none(); |
| let suggestion_param_name = |
| suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); |
| |
| debug!(?lifetime_sup.span); |
| debug!(?lifetime_sub.span); |
| let make_suggestion = |span: rustc_span::Span| { |
| if span.is_empty() { |
| (span, format!("{}, ", suggestion_param_name)) |
| } else if let Ok("&") = self.tcx.sess.source_map().span_to_snippet(span).as_deref() |
| { |
| (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) |
| } else { |
| (span, suggestion_param_name.clone()) |
| } |
| }; |
| let mut suggestions = |
| vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; |
| |
| if introduce_new { |
| let new_param_suggestion = if let Some(first) = |
| generics.params.iter().find(|p| !p.name.ident().span.is_empty()) |
| { |
| (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) |
| } else { |
| (generics.span, format!("<{}>", suggestion_param_name)) |
| }; |
| |
| suggestions.push(new_param_suggestion); |
| } |
| |
| diag.multipart_suggestion( |
| fluent::infer_lifetime_param_suggestion, |
| suggestions, |
| Applicability::MaybeIncorrect, |
| ); |
| diag.set_arg("is_impl", is_impl); |
| true |
| }; |
| if mk_suggestion() && self.add_note { |
| diag.note(fluent::infer_lifetime_param_suggestion_elided); |
| } |
| } |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(infer_lifetime_mismatch, code = "E0623")] |
| pub struct LifetimeMismatch<'a> { |
| #[primary_span] |
| pub span: Span, |
| #[subdiagnostic] |
| pub labels: LifetimeMismatchLabels, |
| #[subdiagnostic] |
| pub suggestion: AddLifetimeParamsSuggestion<'a>, |
| } |
| |
| pub struct IntroducesStaticBecauseUnmetLifetimeReq { |
| pub unmet_requirements: MultiSpan, |
| pub binding_span: Span, |
| } |
| |
| impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { |
| fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, _: F) |
| where |
| F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, |
| { |
| self.unmet_requirements |
| .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); |
| diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); |
| } |
| } |
| |
| // FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that |
| #[derive(Subdiagnostic)] |
| pub enum DoesNotOutliveStaticFromImpl { |
| #[note(infer_does_not_outlive_static_from_impl)] |
| Spanned { |
| #[primary_span] |
| span: Span, |
| }, |
| #[note(infer_does_not_outlive_static_from_impl)] |
| Unspanned, |
| } |
| |
| #[derive(Subdiagnostic)] |
| pub enum ImplicitStaticLifetimeSubdiag { |
| #[note(infer_implicit_static_lifetime_note)] |
| Note { |
| #[primary_span] |
| span: Span, |
| }, |
| #[suggestion_verbose( |
| infer_implicit_static_lifetime_suggestion, |
| code = " + '_", |
| applicability = "maybe-incorrect" |
| )] |
| Sugg { |
| #[primary_span] |
| span: Span, |
| }, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(infer_mismatched_static_lifetime)] |
| pub struct MismatchedStaticLifetime<'a> { |
| #[primary_span] |
| pub cause_span: Span, |
| #[subdiagnostic] |
| pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, |
| #[subdiagnostic] |
| pub expl: Option<note_and_explain::RegionExplanation<'a>>, |
| #[subdiagnostic] |
| pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, |
| #[subdiagnostic(eager)] |
| pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, |
| } |