1
Fork 0

Add beginner friendly lifetime elision hint to E0623

Suggest adding a new lifetime parameter when two elided lifetimes should match up but don't

Issue #90170

This also changes the tests introduced by the previous commits because of another rustc issue (#90258)
This commit is contained in:
Nilstrieb 2021-11-02 21:22:41 +01:00
parent 18bc4bee97
commit 4b9e4606cb
11 changed files with 220 additions and 8 deletions

View file

@ -7,7 +7,10 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::SubregionOrigin;
use rustc_errors::{struct_span_err, ErrorReported};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::{GenericParamKind, Ty};
use rustc_middle::ty::Region;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
@ -160,11 +163,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}
};
let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
e.span_label(span_1, main_label);
e.span_label(span_2, String::new());
e.span_label(span, span_label);
err.span_label(span_1, main_label);
err.span_label(span_2, String::new());
err.span_label(span, span_label);
self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
if let Some(t) = future_return_type {
let snip = self
@ -178,14 +183,87 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
(_, "") => None,
_ => Some(s),
})
.unwrap_or("{unnamed_type}".to_string());
.unwrap_or_else(|| "{unnamed_type}".to_string());
e.span_label(
err.span_label(
t.span,
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
);
}
e.emit();
err.emit();
Some(ErrorReported)
}
fn suggest_adding_lifetime_params(
&self,
sub: Region<'tcx>,
ty_sup: &Ty<'_>,
ty_sub: &Ty<'_>,
err: &mut DiagnosticBuilder<'_>,
) {
if let (
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
) = (ty_sub, ty_sup)
{
if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
if let hir::Node::Item(&hir::Item {
kind: hir::ItemKind::Fn(_, ref generics, ..),
..
}) = self.tcx().hir().get(hir_id)
{
let (suggestion_param_name, introduce_new) = generics
.params
.iter()
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
.and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
.map(|name| (name, false))
.unwrap_or_else(|| ("'a".to_string(), true));
let mut suggestions = vec![
if let hir::LifetimeName::Underscore = lifetime_sub.name {
(lifetime_sub.span, suggestion_param_name.clone())
} else {
(
lifetime_sub.span.shrink_to_hi(),
suggestion_param_name.clone() + " ",
)
},
if let hir::LifetimeName::Underscore = lifetime_sup.name {
(lifetime_sup.span, suggestion_param_name.clone())
} else {
(
lifetime_sup.span.shrink_to_hi(),
suggestion_param_name.clone() + " ",
)
},
];
if introduce_new {
let new_param_suggestion = match &generics.params {
[] => (generics.span, format!("<{}>", suggestion_param_name)),
[first, ..] => (
first.span.shrink_to_lo(),
format!("{}, ", suggestion_param_name),
),
};
suggestions.push(new_param_suggestion);
}
err.multipart_suggestion(
"consider introducing a named lifetime parameter",
suggestions,
Applicability::MaybeIncorrect,
);
err.note(
"each elided lifetime in input position becomes a distinct lifetime",
);
}
}
}
}
}
}