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:
parent
18bc4bee97
commit
4b9e4606cb
11 changed files with 220 additions and 8 deletions
|
@ -7,7 +7,10 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||||
use crate::infer::lexical_region_resolve::RegionResolutionError;
|
use crate::infer::lexical_region_resolve::RegionResolutionError;
|
||||||
use crate::infer::SubregionOrigin;
|
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> {
|
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||||
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
|
/// 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);
|
err.span_label(span_1, main_label);
|
||||||
e.span_label(span_2, String::new());
|
err.span_label(span_2, String::new());
|
||||||
e.span_label(span, span_label);
|
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 {
|
if let Some(t) = future_return_type {
|
||||||
let snip = self
|
let snip = self
|
||||||
|
@ -178,14 +183,87 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||||
(_, "") => None,
|
(_, "") => None,
|
||||||
_ => Some(s),
|
_ => Some(s),
|
||||||
})
|
})
|
||||||
.unwrap_or("{unnamed_type}".to_string());
|
.unwrap_or_else(|| "{unnamed_type}".to_string());
|
||||||
|
|
||||||
e.span_label(
|
err.span_label(
|
||||||
t.span,
|
t.span,
|
||||||
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
|
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
e.emit();
|
err.emit();
|
||||||
Some(ErrorReported)
|
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",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
9
src/test/ui/lifetimes/issue-90170-elision-mismatch.fixed
Normal file
9
src/test/ui/lifetimes/issue-90170-elision-mismatch.fixed
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,29 @@
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:3:40
|
||||||
|
|
|
||||||
|
LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
|
||||||
|
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
||||||
|
| | |
|
||||||
|
| | let's call the lifetime of this reference `'1`
|
||||||
|
| let's call the lifetime of this reference `'2`
|
||||||
|
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:5:44
|
||||||
|
|
|
||||||
|
LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
|
||||||
|
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
||||||
|
| | |
|
||||||
|
| | let's call the lifetime of this reference `'1`
|
||||||
|
| let's call the lifetime of this reference `'2`
|
||||||
|
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:7:63
|
||||||
|
|
|
||||||
|
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
|
||||||
|
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
||||||
|
| | |
|
||||||
|
| | let's call the lifetime of this reference `'1`
|
||||||
|
| let's call the lifetime of this reference `'2`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
9
src/test/ui/lifetimes/issue-90170-elision-mismatch.rs
Normal file
9
src/test/ui/lifetimes/issue-90170-elision-mismatch.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
|
||||||
|
|
||||||
|
fn main() {}
|
45
src/test/ui/lifetimes/issue-90170-elision-mismatch.stderr
Normal file
45
src/test/ui/lifetimes/issue-90170-elision-mismatch.stderr
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
error[E0623]: lifetime mismatch
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:3:47
|
||||||
|
|
|
||||||
|
LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
|
||||||
|
| --- --- ^ ...but data from `y` flows into `x` here
|
||||||
|
| |
|
||||||
|
| these two types are declared with different lifetimes...
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
|
error[E0623]: lifetime mismatch
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:5:51
|
||||||
|
|
|
||||||
|
LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
|
||||||
|
| ------ --- ^ ...but data from `y` flows into `x` here
|
||||||
|
| |
|
||||||
|
| these two types are declared with different lifetimes...
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
|
||||||
|
| ++++ ~~ ++
|
||||||
|
|
||||||
|
error[E0623]: lifetime mismatch
|
||||||
|
--> $DIR/issue-90170-elision-mismatch.rs:7:70
|
||||||
|
|
|
||||||
|
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
|
||||||
|
| --- --- ^ ...but data from `y` flows into `x` here
|
||||||
|
| |
|
||||||
|
| these two types are declared with different lifetimes...
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
|
||||||
|
| ++ ++
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0623`.
|
|
@ -5,6 +5,12 @@ LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | *v = x;
|
LL | *v = x;
|
||||||
| ^ ...but data from `x` flows here
|
| ^ ...but data from `x` flows here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | z.push((x,y));
|
LL | z.push((x,y));
|
||||||
| ^ ...but data flows into `z` here
|
| ^ ...but data flows into `z` here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error[E0623]: lifetime mismatch
|
error[E0623]: lifetime mismatch
|
||||||
--> $DIR/ex3-both-anon-regions-3.rs:2:15
|
--> $DIR/ex3-both-anon-regions-3.rs:2:15
|
||||||
|
@ -13,6 +19,12 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | z.push((x,y));
|
LL | z.push((x,y));
|
||||||
| ^ ...but data flows into `z` here
|
| ^ ...but data flows into `z` here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | y.push(z);
|
LL | y.push(z);
|
||||||
| ^ ...but data from `z` flows into `y` here
|
| ^ ...but data from `z` flows into `y` here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | y.push(z);
|
LL | y.push(z);
|
||||||
| ^ ...but data from `z` flows into `y` here
|
| ^ ...but data from `z` flows into `y` here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||||
| --- --- these two types are declared with different lifetimes...
|
| --- --- these two types are declared with different lifetimes...
|
||||||
LL | x.push(y);
|
LL | x.push(y);
|
||||||
| ^ ...but data from `y` flows into `x` here
|
| ^ ...but data from `y` flows into `x` here
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
|
||||||
|
| ++++ ++ ++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ LL | fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); }
|
||||||
| ------ ------ ^ ...but data from `y` flows into `x` here
|
| ------ ------ ^ ...but data from `y` flows into `x` here
|
||||||
| |
|
| |
|
||||||
| these two types are declared with different lifetimes...
|
| these two types are declared with different lifetimes...
|
||||||
|
|
|
||||||
|
= note: each elided lifetime in input position becomes a distinct lifetime
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
|
||||||
|
| ++++ ~~ ~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue