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.
This commit is contained in:
parent
1a73979886
commit
9f730e92f2
@ -4,6 +4,7 @@
|
|||||||
MultiSpan, SubdiagMessageOp, Subdiagnostic,
|
MultiSpan, SubdiagMessageOp, Subdiagnostic,
|
||||||
};
|
};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||||
use rustc_hir::FnRetTy;
|
use rustc_hir::FnRetTy;
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath;
|
use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath;
|
||||||
@ -355,18 +356,6 @@ fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
|||||||
_f: &F,
|
_f: &F,
|
||||||
) {
|
) {
|
||||||
let mut mk_suggestion = || {
|
let mut mk_suggestion = || {
|
||||||
let (
|
|
||||||
hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. },
|
|
||||||
hir::Ty { kind: hir::TyKind::Ref(lifetime_sup, _), .. },
|
|
||||||
) = (self.ty_sub, self.ty_sup)
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !lifetime_sub.is_anonymous() || !lifetime_sup.is_anonymous() {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else {
|
let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@ -393,21 +382,77 @@ fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
|||||||
let suggestion_param_name =
|
let suggestion_param_name =
|
||||||
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());
|
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());
|
||||||
|
|
||||||
debug!(?lifetime_sup.ident.span);
|
struct ImplicitLifetimeFinder {
|
||||||
debug!(?lifetime_sub.ident.span);
|
suggestions: Vec<(Span, String)>,
|
||||||
let make_suggestion = |ident: Ident| {
|
suggestion_param_name: String,
|
||||||
let sugg = if ident.name == kw::Empty {
|
}
|
||||||
format!("{suggestion_param_name}, ")
|
|
||||||
} else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
|
|
||||||
format!("{suggestion_param_name} ")
|
|
||||||
} else {
|
|
||||||
suggestion_param_name.clone()
|
|
||||||
};
|
|
||||||
(ident.span, sugg)
|
|
||||||
};
|
|
||||||
let mut suggestions =
|
|
||||||
vec![make_suggestion(lifetime_sub.ident), make_suggestion(lifetime_sup.ident)];
|
|
||||||
|
|
||||||
|
impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
|
||||||
|
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
|
||||||
|
let make_suggestion = |ident: Ident| {
|
||||||
|
if ident.name == kw::Empty && ident.span.is_empty() {
|
||||||
|
format!("{}, ", self.suggestion_param_name)
|
||||||
|
} else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
|
||||||
|
format!("{} ", self.suggestion_param_name)
|
||||||
|
} else {
|
||||||
|
self.suggestion_param_name.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match ty.kind {
|
||||||
|
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
|
||||||
|
for segment in path.segments {
|
||||||
|
if let Some(args) = segment.args {
|
||||||
|
if args.args.iter().all(|arg| {
|
||||||
|
matches!(
|
||||||
|
arg,
|
||||||
|
hir::GenericArg::Lifetime(lifetime)
|
||||||
|
if lifetime.ident.name == kw::Empty
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
self.suggestions.push((
|
||||||
|
segment.ident.span.shrink_to_hi(),
|
||||||
|
format!(
|
||||||
|
"<{}>",
|
||||||
|
args.args
|
||||||
|
.iter()
|
||||||
|
.map(|_| self.suggestion_param_name.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
for arg in args.args {
|
||||||
|
if let hir::GenericArg::Lifetime(lifetime) = arg
|
||||||
|
&& lifetime.is_anonymous()
|
||||||
|
{
|
||||||
|
self.suggestions.push((
|
||||||
|
lifetime.ident.span,
|
||||||
|
make_suggestion(lifetime.ident),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
|
||||||
|
self.suggestions
|
||||||
|
.push((lifetime.ident.span, make_suggestion(lifetime.ident)));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
walk_ty(self, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut visitor = ImplicitLifetimeFinder {
|
||||||
|
suggestions: vec![],
|
||||||
|
suggestion_param_name: suggestion_param_name.clone(),
|
||||||
|
};
|
||||||
|
visitor.visit_ty(self.ty_sub);
|
||||||
|
visitor.visit_ty(self.ty_sup);
|
||||||
|
if visitor.suggestions.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if introduce_new {
|
if introduce_new {
|
||||||
let new_param_suggestion = if let Some(first) =
|
let new_param_suggestion = if let Some(first) =
|
||||||
generics.params.iter().find(|p| !p.name.ident().span.is_empty())
|
generics.params.iter().find(|p| !p.name.ident().span.is_empty())
|
||||||
@ -417,15 +462,15 @@ fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
|||||||
(generics.span, format!("<{suggestion_param_name}>"))
|
(generics.span, format!("<{suggestion_param_name}>"))
|
||||||
};
|
};
|
||||||
|
|
||||||
suggestions.push(new_param_suggestion);
|
visitor.suggestions.push(new_param_suggestion);
|
||||||
}
|
}
|
||||||
|
diag.multipart_suggestion_verbose(
|
||||||
diag.multipart_suggestion(
|
|
||||||
fluent::infer_lifetime_param_suggestion,
|
fluent::infer_lifetime_param_suggestion,
|
||||||
suggestions,
|
visitor.suggestions,
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
diag.arg("is_impl", is_impl);
|
diag.arg("is_impl", is_impl);
|
||||||
|
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
if mk_suggestion() && self.add_note {
|
if mk_suggestion() && self.add_note {
|
||||||
|
@ -8,6 +8,11 @@ LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
|
|||||||
LL |
|
LL |
|
||||||
LL | if x > y { x } else { y }
|
LL | if x > y { x } else { y }
|
||||||
| ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
|
| ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter and update trait if needed
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ LL | fn foo<'a>(&self, x: &'a i32) -> &i32 {
|
|||||||
LL |
|
LL |
|
||||||
LL | x
|
LL | x
|
||||||
| ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
|
| ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter and update trait if needed
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(&'a self, x: &'a i32) -> &i32 {
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ LL | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo {
|
|||||||
LL |
|
LL |
|
||||||
LL | if true { x } else { self }
|
LL | if true { x } else { self }
|
||||||
| ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
|
| ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter and update trait if needed
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(&'a self, x: &'a Foo) -> &'a Foo {
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
|
|||||||
| has type `&mut Vec<Ref<'2, i32>>`
|
| has type `&mut Vec<Ref<'2, i32>>`
|
||||||
LL | x.push(y);
|
LL | x.push(y);
|
||||||
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<'a, i32>) {
|
||||||
|
| ++++ +++ +++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: Ref) {
|
|||||||
| has type `Ref<'_, '2>`
|
| has type `Ref<'_, '2>`
|
||||||
LL | x.b = y.b;
|
LL | x.b = y.b;
|
||||||
| ^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^^^ 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>) {
|
||||||
|
| ++++ ++++++++ ++++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ LL | fn foo(mut x: Ref) {
|
|||||||
| has type `Ref<'2, '_>`
|
| has type `Ref<'2, '_>`
|
||||||
LL | x.a = x.b;
|
LL | x.a = x.b;
|
||||||
| ^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut x: Ref<'a, 'a>) {
|
||||||
|
| ++++ ++++++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut x: Vec<Ref>, y: Ref) {
|
|||||||
| has type `Vec<Ref<'2>>`
|
| has type `Vec<Ref<'2>>`
|
||||||
LL | x.push(y);
|
LL | x.push(y);
|
||||||
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut x: Vec<Ref<'a>>, y: Ref<'a>) {
|
||||||
|
| ++++ ++++ ++++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) {
|
|||||||
| has type `Ref<'_, '1>`
|
| has type `Ref<'_, '1>`
|
||||||
LL | y = x.b;
|
LL | y = x.b;
|
||||||
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) {
|
||||||
|
| ++++ ++++++++ ++
|
||||||
|
|
||||||
error[E0384]: cannot assign to immutable argument `y`
|
error[E0384]: cannot assign to immutable argument `y`
|
||||||
--> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5
|
--> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) {
|
|||||||
| has type `Ref<'_, '2>`
|
| has type `Ref<'_, '2>`
|
||||||
LL | y.b = x;
|
LL | y.b = x;
|
||||||
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) {
|
||||||
|
| ++++ ++++++++ ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) {
|
|||||||
| has type `Ref<'_, '2>`
|
| has type `Ref<'_, '2>`
|
||||||
LL | y.b = x;
|
LL | y.b = x;
|
||||||
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) {
|
||||||
|
| ++++ ++++++++ ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) {
|
|||||||
| has type `Ref<'_, '2>`
|
| has type `Ref<'_, '2>`
|
||||||
LL | x.b = y;
|
LL | x.b = y;
|
||||||
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
| ^^^^^^^ assignment requires that `'1` must outlive `'2`
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) {
|
||||||
|
| ++++ ++++++++ ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -34,6 +34,11 @@ LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
|
|||||||
| | |
|
| | |
|
||||||
| | let's call the lifetime of this reference `'1`
|
| | let's call the lifetime of this reference `'1`
|
||||||
| lifetime `'a` defined here
|
| lifetime `'a` defined here
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter and update trait if needed
|
||||||
|
|
|
||||||
|
LL | async fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg }
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
@ -33,6 +33,11 @@ LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
|
|||||||
| -- ---- has type `Pin<&'1 Foo>` ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
|
| -- ---- has type `Pin<&'1 Foo>` ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
|
||||||
| |
|
| |
|
||||||
| lifetime `'a` defined here
|
| lifetime `'a` defined here
|
||||||
|
|
|
||||||
|
help: consider introducing a named lifetime parameter and update trait if needed
|
||||||
|
|
|
||||||
|
LL | fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg }
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user