mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Migrate E0623
This commit is contained in:
parent
3c72788461
commit
af3343ae29
@ -104,9 +104,36 @@ infer_relate_object_bound = ...so that it can be closed over into an object
|
||||
infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long
|
||||
infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at
|
||||
infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues ->
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
[true] ...
|
||||
*[false] {""}
|
||||
}
|
||||
infer_relate_param_bound_2 = ...that is required by this bound
|
||||
infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
|
||||
infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
|
||||
|
||||
infer_nothing = {""}
|
||||
|
||||
infer_lifetime_mismatch = lifetime mismatch
|
||||
|
||||
infer_declared_different = this parameter and the return type are declared with different lifetimes...
|
||||
infer_data_returned = ...but data{$label_var1_exists ->
|
||||
[true] {" "}from `{$label_var1}`
|
||||
*[false] {""}
|
||||
} is returned here
|
||||
|
||||
infer_data_lifetime_flow = ...but data with one lifetime flows into the other here
|
||||
infer_declared_multiple = this type is declared with multiple lifetimes...
|
||||
infer_types_declared_different = these two types are declared with different lifetimes...
|
||||
infer_data_flows = ...but data{$label_var1_exists ->
|
||||
[true] -> {" "}from `{$label_var1}`
|
||||
*[false] -> {""}
|
||||
} flows{$label_var2_exists ->
|
||||
[true] -> {" "}into `{$label_var2}`
|
||||
*[false] -> {""}
|
||||
} here
|
||||
|
||||
infer_lifetime_param_suggestion = consider introducing a named lifetime parameter{$is_impl ->
|
||||
[true] {" "}and update trait if needed
|
||||
*[false] {""}
|
||||
}
|
||||
infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime
|
||||
|
@ -1,7 +1,13 @@
|
||||
use rustc_errors::{fluent, AddSubdiagnostic, DiagnosticMessage, DiagnosticStyledString};
|
||||
use rustc_hir::FnRetTy;
|
||||
use hir::GenericParamKind;
|
||||
use rustc_errors::{
|
||||
fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{FnRetTy, Ty};
|
||||
use rustc_macros::SessionDiagnostic;
|
||||
use rustc_span::{BytePos, Span};
|
||||
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},
|
||||
@ -252,3 +258,164 @@ impl AddSubdiagnostic for RegionOriginNote<'_> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
label_var1: Option<Ident>,
|
||||
label_var2: 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,
|
||||
label_var1,
|
||||
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>,
|
||||
}
|
||||
|
@ -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(
|
||||
ret_span,
|
||||
span,
|
||||
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
|
||||
);
|
||||
}
|
||||
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,
|
||||
label_var1: anon_param_sup.pat.simple_ident(),
|
||||
label_var2: 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);
|
||||
}
|
||||
|
@ -36,5 +36,9 @@ extern crate tracing;
|
||||
extern crate rustc_middle;
|
||||
|
||||
mod errors;
|
||||
pub mod public_errors {
|
||||
// Probably would be useful in rustc_borrowck
|
||||
pub use super::errors::AddLifetimeParamsSuggestion;
|
||||
}
|
||||
pub mod infer;
|
||||
pub mod traits;
|
||||
|
Loading…
Reference in New Issue
Block a user