Skip to content

Commit 2472f8d

Browse files
committed
Suggest setting lifetime in borrowck error involving types with elided lifetimes
``` error: lifetime may not live long enough --> $DIR/ex3-both-anon-regions-both-are-structs-2.rs:7:5 | LL | fn foo(mut x: Ref, y: Ref) { | ----- - has type `Ref<'_, '1>` | | | has type `Ref<'_, '2>` LL | x.b = y.b; | ^^^^^^^^^ assignment requires that `'1` must outlive `'2` | help: consider introducing a named lifetime parameter | LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: Ref<'a, 'a>) { | ++++ ++++++++ ++++++++ ``` As can be seen above, it currently doesn't try to compare the `ty::Ty` lifetimes that diverged vs the `hir::Ty` to correctly suggest the following ``` help: consider introducing a named lifetime parameter | LL | fn foo<'a>(mut x: Ref<'_, 'a>, y: Ref<'_, 'a>) { | ++++ ++++++++ ++++++++ ``` but I believe this to still be an improvement over the status quo. CC #40990.
1 parent 20aa2d8 commit 2472f8d

14 files changed

+140
-30
lines changed

compiler/rustc_infer/src/errors/mod.rs

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_errors::{
44
MultiSpan, SubdiagMessageOp, Subdiagnostic,
55
};
66
use rustc_hir as hir;
7+
use rustc_hir::intravisit::{walk_ty, Visitor};
78
use rustc_hir::FnRetTy;
89
use rustc_macros::{Diagnostic, Subdiagnostic};
910
use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath;
@@ -355,18 +356,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
355356
_f: &F,
356357
) {
357358
let mut mk_suggestion = || {
358-
let (
359-
hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. },
360-
hir::Ty { kind: hir::TyKind::Ref(lifetime_sup, _), .. },
361-
) = (self.ty_sub, self.ty_sup)
362-
else {
363-
return false;
364-
};
365-
366-
if !lifetime_sub.is_anonymous() || !lifetime_sup.is_anonymous() {
367-
return false;
368-
};
369-
370359
let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else {
371360
return false;
372361
};
@@ -393,21 +382,77 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
393382
let suggestion_param_name =
394383
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());
395384

396-
debug!(?lifetime_sup.ident.span);
397-
debug!(?lifetime_sub.ident.span);
398-
let make_suggestion = |ident: Ident| {
399-
let sugg = if ident.name == kw::Empty {
400-
format!("{suggestion_param_name}, ")
401-
} else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
402-
format!("{suggestion_param_name} ")
403-
} else {
404-
suggestion_param_name.clone()
405-
};
406-
(ident.span, sugg)
407-
};
408-
let mut suggestions =
409-
vec![make_suggestion(lifetime_sub.ident), make_suggestion(lifetime_sup.ident)];
385+
struct ImplicitLifetimeFinder {
386+
suggestions: Vec<(Span, String)>,
387+
suggestion_param_name: String,
388+
}
410389

390+
impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
391+
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
392+
let make_suggestion = |ident: Ident| {
393+
if ident.name == kw::Empty && ident.span.is_empty() {
394+
format!("{}, ", self.suggestion_param_name)
395+
} else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
396+
format!("{} ", self.suggestion_param_name)
397+
} else {
398+
self.suggestion_param_name.clone()
399+
}
400+
};
401+
match ty.kind {
402+
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
403+
for segment in path.segments {
404+
if let Some(args) = segment.args {
405+
if args.args.iter().all(|arg| {
406+
matches!(
407+
arg,
408+
hir::GenericArg::Lifetime(lifetime)
409+
if lifetime.ident.name == kw::Empty
410+
)
411+
}) {
412+
self.suggestions.push((
413+
segment.ident.span.shrink_to_hi(),
414+
format!(
415+
"<{}>",
416+
args.args
417+
.iter()
418+
.map(|_| self.suggestion_param_name.clone())
419+
.collect::<Vec<_>>()
420+
.join(", ")
421+
),
422+
));
423+
} else {
424+
for arg in args.args {
425+
if let hir::GenericArg::Lifetime(lifetime) = arg
426+
&& lifetime.is_anonymous()
427+
{
428+
self.suggestions.push((
429+
lifetime.ident.span,
430+
make_suggestion(lifetime.ident),
431+
));
432+
}
433+
}
434+
}
435+
}
436+
}
437+
}
438+
hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
439+
self.suggestions
440+
.push((lifetime.ident.span, make_suggestion(lifetime.ident)));
441+
}
442+
_ => {}
443+
}
444+
walk_ty(self, ty);
445+
}
446+
}
447+
let mut visitor = ImplicitLifetimeFinder {
448+
suggestions: vec![],
449+
suggestion_param_name: suggestion_param_name.clone(),
450+
};
451+
visitor.visit_ty(self.ty_sub);
452+
visitor.visit_ty(self.ty_sup);
453+
if visitor.suggestions.is_empty() {
454+
return false;
455+
}
411456
if introduce_new {
412457
let new_param_suggestion = if let Some(first) =
413458
generics.params.iter().find(|p| !p.name.ident().span.is_empty())
@@ -417,15 +462,15 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
417462
(generics.span, format!("<{suggestion_param_name}>"))
418463
};
419464

420-
suggestions.push(new_param_suggestion);
465+
visitor.suggestions.push(new_param_suggestion);
421466
}
422-
423-
diag.multipart_suggestion(
467+
diag.multipart_suggestion_verbose(
424468
fluent::infer_lifetime_param_suggestion,
425-
suggestions,
469+
visitor.suggestions,
426470
Applicability::MaybeIncorrect,
427471
);
428472
diag.arg("is_impl", is_impl);
473+
429474
true
430475
};
431476
if mk_suggestion() && self.add_note {

tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
88
LL |
99
LL | if x > y { x } else { y }
1010
| ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
11+
|
12+
help: consider introducing a named lifetime parameter and update trait if needed
13+
|
14+
LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
15+
| ++
1116

1217
error: aborting due to 1 previous error
1318

tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | fn foo<'a>(&self, x: &'a i32) -> &i32 {
88
LL |
99
LL | x
1010
| ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
11+
|
12+
help: consider introducing a named lifetime parameter and update trait if needed
13+
|
14+
LL | fn foo<'a>(&'a self, x: &'a i32) -> &i32 {
15+
| ++
1116

1217
error: aborting due to 1 previous error
1318

tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo {
88
LL |
99
LL | if true { x } else { self }
1010
| ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
11+
|
12+
help: consider introducing a named lifetime parameter and update trait if needed
13+
|
14+
LL | fn foo<'a>(&'a self, x: &'a Foo) -> &'a Foo {
15+
| ++
1116

1217
error: aborting due to 1 previous error
1318

tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
77
| has type `&mut Vec<Ref<'2, i32>>`
88
LL | x.push(y);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<'a, i32>) {
14+
| ++++ +++ +++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: Ref) {
77
| has type `Ref<'_, '2>`
88
LL | x.b = y.b;
99
| ^^^^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: Ref<'a, 'a>) {
14+
| ++++ ++++++++ ++++++++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | fn foo(mut x: Ref) {
88
| has type `Ref<'2, '_>`
99
LL | x.a = x.b;
1010
| ^^^^^^^^^ assignment requires that `'1` must outlive `'2`
11+
|
12+
help: consider introducing a named lifetime parameter
13+
|
14+
LL | fn foo<'a>(mut x: Ref<'a, 'a>) {
15+
| ++++ ++++++++
1116

1217
error: aborting due to 1 previous error
1318

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut x: Vec<Ref>, y: Ref) {
77
| has type `Vec<Ref<'2>>`
88
LL | x.push(y);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut x: Vec<Ref<'a>>, y: Ref<'a>) {
14+
| ++++ ++++ ++++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) {
77
| has type `Ref<'_, '1>`
88
LL | y = x.b;
99
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) {
14+
| ++++ ++++++++ ++
1015

1116
error[E0384]: cannot assign to immutable argument `y`
1217
--> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) {
77
| has type `Ref<'_, '2>`
88
LL | y.b = x;
99
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) {
14+
| ++++ ++++++++ ++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) {
77
| has type `Ref<'_, '2>`
88
LL | y.b = x;
99
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) {
14+
| ++++ ++++++++ ++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) {
77
| has type `Ref<'_, '2>`
88
LL | x.b = y;
99
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) {
14+
| ++++ ++++++++ ++
1015

1116
error: aborting due to 1 previous error
1217

tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
3434
| | |
3535
| | let's call the lifetime of this reference `'1`
3636
| lifetime `'a` defined here
37+
|
38+
help: consider introducing a named lifetime parameter and update trait if needed
39+
|
40+
LL | async fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg }
41+
| ++
3742

3843
error: aborting due to 3 previous errors
3944

tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
3333
| -- ---- has type `Pin<&'1 Foo>` ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
3434
| |
3535
| lifetime `'a` defined here
36+
|
37+
help: consider introducing a named lifetime parameter and update trait if needed
38+
|
39+
LL | fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg }
40+
| ++
3641

3742
error: aborting due to 3 previous errors
3843

0 commit comments

Comments
 (0)