Rollup merge of #101153 - IntQuant:issue-100717-infer-2, r=davidtwco
Migrate another part of rustc_infer to session diagnostic Probably will migrate another file before marking this one as ready-to-merge. `@rustbot` label +A-translation r? rust-lang/diagnostics cc https://github.com/rust-lang/rust/issues/100717
This commit is contained in:
commit
e3ea8008cc
7 changed files with 805 additions and 397 deletions
|
@ -1,254 +0,0 @@
|
|||
use rustc_errors::{fluent, AddSubdiagnostic, DiagnosticMessage, DiagnosticStyledString};
|
||||
use rustc_hir::FnRetTy;
|
||||
use rustc_macros::SessionDiagnostic;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use crate::infer::error_reporting::{
|
||||
need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind},
|
||||
ObligationCauseAsDiagArg,
|
||||
};
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[diag(infer::opaque_hidden_type)]
|
||||
pub struct OpaqueHiddenTypeDiag {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[note(infer::opaque_type)]
|
||||
pub opaque_type: Span,
|
||||
#[note(infer::hidden_type)]
|
||||
pub hidden_type: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionSubdiagnostic)]
|
||||
#[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(SessionSubdiagnostic)]
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
// Has to be implemented manually because multipart suggestions are not supported by the derive macro.
|
||||
// Would be a part of `SourceKindSubdiag` otherwise.
|
||||
pub enum SourceKindMultiSuggestion<'a> {
|
||||
FullyQualified {
|
||||
span: Span,
|
||||
def_path: String,
|
||||
adjustment: &'a str,
|
||||
successor: (&'a str, BytePos),
|
||||
},
|
||||
ClosureReturn {
|
||||
ty_info: String,
|
||||
data: &'a FnRetTy<'a>,
|
||||
should_wrap_expr: Option<Span>,
|
||||
},
|
||||
}
|
||||
|
||||
impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
match self {
|
||||
Self::FullyQualified { span, def_path, adjustment, successor } => {
|
||||
let suggestion = vec![
|
||||
(span.shrink_to_lo(), format!("{def_path}({adjustment}")),
|
||||
(span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
|
||||
];
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::infer::source_kind_fully_qualified,
|
||||
suggestion,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
Self::ClosureReturn { ty_info, data, should_wrap_expr } => {
|
||||
let (arrow, post) = match data {
|
||||
FnRetTy::DefaultReturn(_) => ("-> ", " "),
|
||||
_ => ("", ""),
|
||||
};
|
||||
let suggestion = match should_wrap_expr {
|
||||
Some(end_span) => vec![
|
||||
(data.span(), format!("{}{}{}{{ ", arrow, ty_info, post)),
|
||||
(end_span, " }".to_string()),
|
||||
],
|
||||
None => vec![(data.span(), format!("{}{}{}", arrow, ty_info, post))],
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::infer::source_kind_closure_return,
|
||||
suggestion,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 AddSubdiagnostic for RegionOriginNote<'_> {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
499
compiler/rustc_infer/src/errors/mod.rs
Normal file
499
compiler/rustc_infer/src/errors/mod.rs
Normal file
|
@ -0,0 +1,499 @@
|
|||
use hir::GenericParamKind;
|
||||
use rustc_errors::{
|
||||
fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{FnRetTy, Ty};
|
||||
use rustc_macros::SessionDiagnostic;
|
||||
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(SessionDiagnostic)]
|
||||
#[diag(infer::opaque_hidden_type)]
|
||||
pub struct OpaqueHiddenTypeDiag {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[note(infer::opaque_type)]
|
||||
pub opaque_type: Span,
|
||||
#[note(infer::hidden_type)]
|
||||
pub hidden_type: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionDiagnostic)]
|
||||
#[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(SessionSubdiagnostic)]
|
||||
#[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(SessionSubdiagnostic)]
|
||||
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(SessionSubdiagnostic)]
|
||||
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 AddSubdiagnostic for RegionOriginNote<'_> {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
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 AddSubdiagnostic for LifetimeMismatchLabels {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
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 AddSubdiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
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(SessionDiagnostic)]
|
||||
#[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 AddSubdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
|
||||
fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImplNote {
|
||||
pub impl_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl AddSubdiagnostic for ImplNote {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
match self.impl_span {
|
||||
Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
|
||||
None => diag.note(fluent::infer::msl_impl_note),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TraitSubdiag {
|
||||
Note { span: Span },
|
||||
Sugg { span: Span },
|
||||
}
|
||||
|
||||
// FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support
|
||||
impl AddSubdiagnostic for TraitSubdiag {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
match self {
|
||||
TraitSubdiag::Note { span } => {
|
||||
diag.span_note(span, "this has an implicit `'static` lifetime requirement");
|
||||
}
|
||||
TraitSubdiag::Sugg { span } => {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"consider relaxing the implicit `'static` requirement",
|
||||
" + '_".to_owned(),
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[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 impl_note: ImplNote,
|
||||
#[subdiagnostic]
|
||||
pub trait_subdiags: Vec<TraitSubdiag>,
|
||||
}
|
179
compiler/rustc_infer/src/errors/note_and_explain.rs
Normal file
179
compiler/rustc_infer/src/errors/note_and_explain.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
use crate::infer::error_reporting::nice_region_error::find_anon_type;
|
||||
use rustc_errors::{self, fluent, AddSubdiagnostic, IntoDiagnosticArg};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::{symbol::kw, Span};
|
||||
|
||||
#[derive(Default)]
|
||||
struct DescriptionCtx<'a> {
|
||||
span: Option<Span>,
|
||||
kind: &'a str,
|
||||
arg: String,
|
||||
num_arg: u32,
|
||||
}
|
||||
|
||||
impl<'a> DescriptionCtx<'a> {
|
||||
fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
) -> Option<Self> {
|
||||
let mut me = DescriptionCtx::default();
|
||||
me.span = alt_span;
|
||||
match *region {
|
||||
ty::ReEarlyBound(_) | ty::ReFree(_) => {
|
||||
return Self::from_early_bound_and_free_regions(tcx, region);
|
||||
}
|
||||
ty::ReStatic => {
|
||||
me.kind = "restatic";
|
||||
}
|
||||
|
||||
ty::ReEmpty(ty::UniverseIndex::ROOT) => me.kind = "reempty",
|
||||
|
||||
ty::ReEmpty(ui) => {
|
||||
me.kind = "reemptyuni";
|
||||
me.arg = format!("{:?}", ui);
|
||||
}
|
||||
|
||||
ty::RePlaceholder(_) => return None,
|
||||
|
||||
// FIXME(#13998) RePlaceholder should probably print like
|
||||
// ReFree rather than dumping Debug output on the user.
|
||||
//
|
||||
// We shouldn't really be having unification failures with ReVar
|
||||
// and ReLateBound though.
|
||||
ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
|
||||
me.kind = "revar";
|
||||
me.arg = format!("{:?}", region);
|
||||
}
|
||||
};
|
||||
Some(me)
|
||||
}
|
||||
|
||||
fn from_early_bound_and_free_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
) -> Option<Self> {
|
||||
let mut me = DescriptionCtx::default();
|
||||
let scope = region.free_region_binding_scope(tcx).expect_local();
|
||||
match *region {
|
||||
ty::ReEarlyBound(ref br) => {
|
||||
let mut sp = tcx.def_span(scope);
|
||||
if let Some(param) =
|
||||
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
|
||||
{
|
||||
sp = param.span;
|
||||
}
|
||||
if br.has_name() {
|
||||
me.kind = "as_defined";
|
||||
me.arg = br.name.to_string();
|
||||
} else {
|
||||
me.kind = "as_defined_anon";
|
||||
};
|
||||
me.span = Some(sp)
|
||||
}
|
||||
ty::ReFree(ref fr) => {
|
||||
if !fr.bound_region.is_named()
|
||||
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
|
||||
{
|
||||
me.kind = "defined_here";
|
||||
me.span = Some(ty.span);
|
||||
} else {
|
||||
match fr.bound_region {
|
||||
ty::BoundRegionKind::BrNamed(_, name) => {
|
||||
let mut sp = tcx.def_span(scope);
|
||||
if let Some(param) =
|
||||
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
|
||||
{
|
||||
sp = param.span;
|
||||
}
|
||||
if name == kw::UnderscoreLifetime {
|
||||
me.kind = "as_defined_anon";
|
||||
} else {
|
||||
me.kind = "as_defined";
|
||||
me.arg = name.to_string();
|
||||
};
|
||||
me.span = Some(sp);
|
||||
}
|
||||
ty::BrAnon(idx) => {
|
||||
me.kind = "anon_num_here";
|
||||
me.num_arg = idx+1;
|
||||
me.span = Some(tcx.def_span(scope));
|
||||
},
|
||||
_ => {
|
||||
me.kind = "defined_here_reg";
|
||||
me.arg = region.to_string();
|
||||
me.span = Some(tcx.def_span(scope));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
Some(me)
|
||||
}
|
||||
|
||||
fn add_to(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
diag.set_arg("desc_kind", self.kind);
|
||||
diag.set_arg("desc_arg", self.arg);
|
||||
diag.set_arg("desc_num_arg", self.num_arg);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PrefixKind {
|
||||
Empty,
|
||||
}
|
||||
|
||||
pub enum SuffixKind {
|
||||
Continues,
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for PrefixKind {
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
let kind = match self {
|
||||
Self::Empty => "empty",
|
||||
}
|
||||
.into();
|
||||
rustc_errors::DiagnosticArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for SuffixKind {
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
let kind = match self {
|
||||
Self::Continues => "continues",
|
||||
}
|
||||
.into();
|
||||
rustc_errors::DiagnosticArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegionExplanation<'a> {
|
||||
desc: DescriptionCtx<'a>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
}
|
||||
|
||||
impl RegionExplanation<'_> {
|
||||
pub fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
) -> Option<Self> {
|
||||
Some(Self { desc: DescriptionCtx::new(tcx, region, alt_span)?, prefix, suffix })
|
||||
}
|
||||
}
|
||||
|
||||
impl AddSubdiagnostic for RegionExplanation<'_> {
|
||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
||||
if let Some(span) = self.desc.span {
|
||||
diag.span_note(span, fluent::infer::region_explanation);
|
||||
} else {
|
||||
diag.note(fluent::infer::region_explanation);
|
||||
}
|
||||
self.desc.add_to(diag);
|
||||
diag.set_arg("pref_kind", self.prefix);
|
||||
diag.set_arg("suff_kind", self.suffix);
|
||||
}
|
||||
}
|
|
@ -511,20 +511,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
_ => "",
|
||||
};
|
||||
|
||||
multi_suggestions.push(SourceKindMultiSuggestion::FullyQualified {
|
||||
span: receiver.span,
|
||||
multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified(
|
||||
receiver.span,
|
||||
def_path,
|
||||
adjustment,
|
||||
successor,
|
||||
});
|
||||
));
|
||||
}
|
||||
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
|
||||
let ty_info = ty_to_string(self, ty);
|
||||
multi_suggestions.push(SourceKindMultiSuggestion::ClosureReturn {
|
||||
multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return(
|
||||
ty_info,
|
||||
data,
|
||||
should_wrap_expr,
|
||||
});
|
||||
));
|
||||
}
|
||||
}
|
||||
match error_code {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
//! Error Reporting for Anonymous Region Lifetime Errors
|
||||
//! where both the regions are anonymous.
|
||||
|
||||
use crate::errors::AddLifetimeParamsSuggestion;
|
||||
use crate::errors::LifetimeMismatch;
|
||||
use crate::errors::LifetimeMismatchLabels;
|
||||
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo;
|
||||
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
|
@ -8,11 +11,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError;
|
|||
use crate::infer::SubregionOrigin;
|
||||
use crate::infer::TyCtxt;
|
||||
|
||||
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{GenericParamKind, Ty};
|
||||
use rustc_errors::AddSubdiagnostic;
|
||||
use rustc_errors::{Diagnostic, ErrorGuaranteed};
|
||||
use rustc_hir::Ty;
|
||||
use rustc_middle::ty::Region;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
|
||||
|
@ -98,137 +100,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
|||
let sub_is_ret_type =
|
||||
self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
|
||||
|
||||
let span_label_var1 = match anon_param_sup.pat.simple_ident() {
|
||||
Some(simple_ident) => format!(" from `{}`", simple_ident),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
let span_label_var2 = match anon_param_sub.pat.simple_ident() {
|
||||
Some(simple_ident) => format!(" into `{}`", simple_ident),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
|
||||
sub_is_ret_type, sup_is_ret_type
|
||||
);
|
||||
|
||||
let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
|
||||
|
||||
match (sup_is_ret_type, sub_is_ret_type) {
|
||||
let labels = match (sup_is_ret_type, sub_is_ret_type) {
|
||||
(ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
|
||||
let param_span =
|
||||
if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
|
||||
|
||||
err.span_label(
|
||||
LifetimeMismatchLabels::InRet {
|
||||
param_span,
|
||||
"this parameter and the return type are declared with different lifetimes...",
|
||||
);
|
||||
err.span_label(ret_span, "");
|
||||
err.span_label(span, format!("...but data{} is returned here", span_label_var1));
|
||||
}
|
||||
|
||||
(None, None) => {
|
||||
if ty_sup.hir_id == ty_sub.hir_id {
|
||||
err.span_label(ty_sup.span, "this type is declared with multiple lifetimes...");
|
||||
err.span_label(ty_sub.span, "");
|
||||
err.span_label(span, "...but data with one lifetime flows into the other here");
|
||||
} else {
|
||||
err.span_label(
|
||||
ty_sup.span,
|
||||
"these two types are declared with different lifetimes...",
|
||||
);
|
||||
err.span_label(ty_sub.span, "");
|
||||
err.span_label(
|
||||
span,
|
||||
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
|
||||
);
|
||||
ret_span,
|
||||
span,
|
||||
label_var1: anon_param_sup.pat.simple_ident(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) {
|
||||
err.note("each elided lifetime in input position becomes a distinct lifetime");
|
||||
}
|
||||
(None, None) => LifetimeMismatchLabels::Normal {
|
||||
hir_equal: ty_sup.hir_id == ty_sub.hir_id,
|
||||
ty_sup: ty_sup.span,
|
||||
ty_sub: ty_sub.span,
|
||||
span,
|
||||
sup: anon_param_sup.pat.simple_ident(),
|
||||
sub: anon_param_sub.pat.simple_ident(),
|
||||
},
|
||||
};
|
||||
|
||||
let reported = err.emit();
|
||||
let suggestion =
|
||||
AddLifetimeParamsSuggestion { tcx: self.tcx(), sub, ty_sup, ty_sub, add_note: true };
|
||||
let err = LifetimeMismatch { span, labels, suggestion };
|
||||
let reported = self.tcx().sess.emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently only used in rustc_borrowck, probably should be
|
||||
/// removed in favour of public_errors::AddLifetimeParamsSuggestion
|
||||
pub fn suggest_adding_lifetime_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
ty_sup: &Ty<'_>,
|
||||
ty_sub: &Ty<'_>,
|
||||
ty_sup: &'tcx Ty<'_>,
|
||||
ty_sub: &'tcx Ty<'_>,
|
||||
err: &mut Diagnostic,
|
||||
) -> bool {
|
||||
let (
|
||||
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
|
||||
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
|
||||
) = (ty_sub, ty_sup) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(anon_reg) = tcx.is_suitable_region(sub) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
|
||||
|
||||
let node = 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("&") = 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);
|
||||
}
|
||||
|
||||
let mut sugg = String::from("consider introducing a named lifetime parameter");
|
||||
if is_impl {
|
||||
sugg.push_str(" and update trait if needed");
|
||||
}
|
||||
err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect);
|
||||
|
||||
true
|
||||
) {
|
||||
let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false };
|
||||
suggestion.add_to_diagnostic(err);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
|
||||
//! to hold.
|
||||
|
||||
use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq};
|
||||
use crate::errors::{ImplNote, MismatchedStaticLifetime, TraitSubdiag};
|
||||
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
use crate::infer::error_reporting::note_and_explain_region;
|
||||
use crate::infer::lexical_region_resolve::RegionResolutionError;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::ObligationCauseCode;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_errors::{ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::ty::TypeVisitor;
|
||||
|
@ -39,12 +40,23 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
|||
= *parent.code() else {
|
||||
return None;
|
||||
};
|
||||
let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
|
||||
|
||||
// FIXME: we should point at the lifetime
|
||||
let mut multi_span: MultiSpan = vec![binding_span].into();
|
||||
multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement");
|
||||
err.span_note(multi_span, "because this has an unmet lifetime requirement");
|
||||
note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span));
|
||||
let multi_span: MultiSpan = vec![binding_span].into();
|
||||
let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq {
|
||||
unmet_requirements: multi_span,
|
||||
binding_span,
|
||||
};
|
||||
|
||||
let expl = note_and_explain::RegionExplanation::new(
|
||||
self.tcx(),
|
||||
sup,
|
||||
Some(binding_span),
|
||||
note_and_explain::PrefixKind::Empty,
|
||||
note_and_explain::SuffixKind::Continues,
|
||||
);
|
||||
let mut impl_span = None;
|
||||
let mut trait_subdiags = Vec::new();
|
||||
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
|
||||
// If an impl is local, then maybe this isn't what they want. Try to
|
||||
// be as helpful as possible with implicit lifetimes.
|
||||
|
@ -73,31 +85,30 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
|||
// there aren't trait objects or because none are implicit, then just
|
||||
// write a single note on the impl itself.
|
||||
|
||||
let impl_span = self.tcx().def_span(*impl_def_id);
|
||||
err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
} else {
|
||||
// Otherwise, point at all implicit static lifetimes
|
||||
|
||||
err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
|
||||
for span in &traits {
|
||||
err.span_note(*span, "this has an implicit `'static` lifetime requirement");
|
||||
trait_subdiags.push(TraitSubdiag::Note { span: *span });
|
||||
// It would be nice to put this immediately under the above note, but they get
|
||||
// pushed to the end.
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"consider relaxing the implicit `'static` requirement",
|
||||
" + '_",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
trait_subdiags.push(TraitSubdiag::Sugg { span: span.shrink_to_hi() });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise just point out the impl.
|
||||
|
||||
let impl_span = self.tcx().def_span(*impl_def_id);
|
||||
err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
}
|
||||
let reported = err.emit();
|
||||
let err = MismatchedStaticLifetime {
|
||||
cause_span: cause.span,
|
||||
unmet_lifetime_reqs: multispan_subdiag,
|
||||
expl,
|
||||
impl_note: ImplNote { impl_span },
|
||||
trait_subdiags,
|
||||
};
|
||||
let reported = self.tcx().sess.emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue