Skip to content

Commit e60e19b

Browse files
committed
Auto merge of #90179 - Nilstrieb:lifetime-elision-mismatch-hint, r=estebank
Add beginner friendly lifetime elision hint to E0623 Address #90170 Suggest adding a new lifetime parameter when two elided lifetimes should match up but don't. Example: ``` error[E0623]: lifetime mismatch --> $DIR/issue-90170-elision-mismatch.rs:2:35 | LL | fn foo(slice_a: &mut [u8], slice_b: &mut [u8]) { | --------- --------- these two types are declared with different lifetimes... LL | core::mem::swap(&mut slice_a, &mut slice_b); | ^^^^^^^^^^^^ ...but data from `slice_b` flows into `slice_a` here | = note: each elided lifetime in input position becomes a distinct lifetime help: explicitly declare a lifetime and assign it to both | LL | fn foo<'a>(slice_a: &'a mut [u8], slice_b: &'a mut [u8]) { | ++++ ++ ++ ``` for ```rust fn foo(slice_a: &mut [u8], slice_b: &mut [u8]) { core::mem::swap(&mut slice_a, &mut slice_b); } ```
2 parents 4ff9023 + 4b9e460 commit e60e19b

11 files changed

+220
-8
lines changed

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs

+86-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
77
use crate::infer::lexical_region_resolve::RegionResolutionError;
88
use crate::infer::SubregionOrigin;
99

10-
use rustc_errors::{struct_span_err, ErrorReported};
10+
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
11+
use rustc_hir as hir;
12+
use rustc_hir::{GenericParamKind, Ty};
13+
use rustc_middle::ty::Region;
1114

1215
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
1316
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
@@ -160,11 +163,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
160163
}
161164
};
162165

163-
let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
166+
let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
164167

165-
e.span_label(span_1, main_label);
166-
e.span_label(span_2, String::new());
167-
e.span_label(span, span_label);
168+
err.span_label(span_1, main_label);
169+
err.span_label(span_2, String::new());
170+
err.span_label(span, span_label);
171+
172+
self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
168173

169174
if let Some(t) = future_return_type {
170175
let snip = self
@@ -178,14 +183,87 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
178183
(_, "") => None,
179184
_ => Some(s),
180185
})
181-
.unwrap_or("{unnamed_type}".to_string());
186+
.unwrap_or_else(|| "{unnamed_type}".to_string());
182187

183-
e.span_label(
188+
err.span_label(
184189
t.span,
185190
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
186191
);
187192
}
188-
e.emit();
193+
err.emit();
189194
Some(ErrorReported)
190195
}
196+
197+
fn suggest_adding_lifetime_params(
198+
&self,
199+
sub: Region<'tcx>,
200+
ty_sup: &Ty<'_>,
201+
ty_sub: &Ty<'_>,
202+
err: &mut DiagnosticBuilder<'_>,
203+
) {
204+
if let (
205+
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
206+
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
207+
) = (ty_sub, ty_sup)
208+
{
209+
if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
210+
if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
211+
let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
212+
if let hir::Node::Item(&hir::Item {
213+
kind: hir::ItemKind::Fn(_, ref generics, ..),
214+
..
215+
}) = self.tcx().hir().get(hir_id)
216+
{
217+
let (suggestion_param_name, introduce_new) = generics
218+
.params
219+
.iter()
220+
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
221+
.and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
222+
.map(|name| (name, false))
223+
.unwrap_or_else(|| ("'a".to_string(), true));
224+
225+
let mut suggestions = vec![
226+
if let hir::LifetimeName::Underscore = lifetime_sub.name {
227+
(lifetime_sub.span, suggestion_param_name.clone())
228+
} else {
229+
(
230+
lifetime_sub.span.shrink_to_hi(),
231+
suggestion_param_name.clone() + " ",
232+
)
233+
},
234+
if let hir::LifetimeName::Underscore = lifetime_sup.name {
235+
(lifetime_sup.span, suggestion_param_name.clone())
236+
} else {
237+
(
238+
lifetime_sup.span.shrink_to_hi(),
239+
suggestion_param_name.clone() + " ",
240+
)
241+
},
242+
];
243+
244+
if introduce_new {
245+
let new_param_suggestion = match &generics.params {
246+
[] => (generics.span, format!("<{}>", suggestion_param_name)),
247+
[first, ..] => (
248+
first.span.shrink_to_lo(),
249+
format!("{}, ", suggestion_param_name),
250+
),
251+
};
252+
253+
suggestions.push(new_param_suggestion);
254+
}
255+
256+
err.multipart_suggestion(
257+
"consider introducing a named lifetime parameter",
258+
suggestions,
259+
Applicability::MaybeIncorrect,
260+
);
261+
err.note(
262+
"each elided lifetime in input position becomes a distinct lifetime",
263+
);
264+
}
265+
}
266+
}
267+
}
268+
}
191269
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// run-rustfix
2+
3+
pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
4+
5+
pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
6+
7+
pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } //~ ERROR lifetime mismatch
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/issue-90170-elision-mismatch.rs:3:40
3+
|
4+
LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
5+
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
6+
| | |
7+
| | let's call the lifetime of this reference `'1`
8+
| let's call the lifetime of this reference `'2`
9+
10+
error: lifetime may not live long enough
11+
--> $DIR/issue-90170-elision-mismatch.rs:5:44
12+
|
13+
LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
14+
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
15+
| | |
16+
| | let's call the lifetime of this reference `'1`
17+
| let's call the lifetime of this reference `'2`
18+
19+
error: lifetime may not live long enough
20+
--> $DIR/issue-90170-elision-mismatch.rs:7:63
21+
|
22+
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
23+
| - - ^^^^^^^^^ argument requires that `'1` must outlive `'2`
24+
| | |
25+
| | let's call the lifetime of this reference `'1`
26+
| let's call the lifetime of this reference `'2`
27+
28+
error: aborting due to 3 previous errors
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// run-rustfix
2+
3+
pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
4+
5+
pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
6+
7+
pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } //~ ERROR lifetime mismatch
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/issue-90170-elision-mismatch.rs:3:47
3+
|
4+
LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
5+
| --- --- ^ ...but data from `y` flows into `x` here
6+
| |
7+
| these two types are declared with different lifetimes...
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
13+
| ++++ ++ ++
14+
15+
error[E0623]: lifetime mismatch
16+
--> $DIR/issue-90170-elision-mismatch.rs:5:51
17+
|
18+
LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
19+
| ------ --- ^ ...but data from `y` flows into `x` here
20+
| |
21+
| these two types are declared with different lifetimes...
22+
|
23+
= note: each elided lifetime in input position becomes a distinct lifetime
24+
help: consider introducing a named lifetime parameter
25+
|
26+
LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
27+
| ++++ ~~ ++
28+
29+
error[E0623]: lifetime mismatch
30+
--> $DIR/issue-90170-elision-mismatch.rs:7:70
31+
|
32+
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
33+
| --- --- ^ ...but data from `y` flows into `x` here
34+
| |
35+
| these two types are declared with different lifetimes...
36+
|
37+
= note: each elided lifetime in input position becomes a distinct lifetime
38+
help: consider introducing a named lifetime parameter
39+
|
40+
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
41+
| ++ ++
42+
43+
error: aborting due to 3 previous errors
44+
45+
For more information about this error, try `rustc --explain E0623`.

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) {
55
| --- --- these two types are declared with different lifetimes...
66
LL | *v = x;
77
| ^ ...but data from `x` flows here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
13+
| ++++ ++ ++
814

915
error: aborting due to previous error
1016

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
55
| --- --- these two types are declared with different lifetimes...
66
LL | z.push((x,y));
77
| ^ ...but data flows into `z` here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
13+
| ++++ ++ ++
814

915
error[E0623]: lifetime mismatch
1016
--> $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)) {
1319
| --- --- these two types are declared with different lifetimes...
1420
LL | z.push((x,y));
1521
| ^ ...but data flows into `z` here
22+
|
23+
= note: each elided lifetime in input position becomes a distinct lifetime
24+
help: consider introducing a named lifetime parameter
25+
|
26+
LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
27+
| ++++ ++ ++
1628

1729
error: aborting due to 2 previous errors
1830

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
55
| --- --- these two types are declared with different lifetimes...
66
LL | y.push(z);
77
| ^ ...but data from `z` flows into `y` here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
13+
| ++++ ++ ++
814

915
error: aborting due to previous error
1016

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
55
| --- --- these two types are declared with different lifetimes...
66
LL | y.push(z);
77
| ^ ...but data from `z` flows into `y` here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
13+
| ++++ ++ ++
814

915
error: aborting due to previous error
1016

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
55
| --- --- these two types are declared with different lifetimes...
66
LL | x.push(y);
77
| ^ ...but data from `y` flows into `x` here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
13+
| ++++ ++ ++
814

915
error: aborting due to previous error
1016

src/test/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); }
55
| ------ ------ ^ ...but data from `y` flows into `x` here
66
| |
77
| these two types are declared with different lifetimes...
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
13+
| ++++ ~~ ~~
814

915
error: aborting due to previous error
1016

0 commit comments

Comments
 (0)