Add hint for missing lifetime bound on trait object when type alias is used
This commit is contained in:
parent
c8e6a9e8b6
commit
62a1e76d2b
6 changed files with 176 additions and 19 deletions
|
@ -813,17 +813,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
if *outlived_f != ty::ReStatic {
|
if *outlived_f != ty::ReStatic {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let suitable_region = self.infcx.tcx.is_suitable_region(f);
|
||||||
|
let Some(suitable_region) = suitable_region else { return; };
|
||||||
|
|
||||||
let fn_returns = self
|
let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
|
||||||
.infcx
|
|
||||||
.tcx
|
|
||||||
.is_suitable_region(f)
|
|
||||||
.map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if fn_returns.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
|
let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
|
||||||
param
|
param
|
||||||
|
@ -839,15 +832,43 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
let captures = format!("captures data from {arg}");
|
let captures = format!("captures data from {arg}");
|
||||||
|
|
||||||
return nice_region_error::suggest_new_region_bound(
|
if !fn_returns.is_empty() {
|
||||||
self.infcx.tcx,
|
nice_region_error::suggest_new_region_bound(
|
||||||
diag,
|
self.infcx.tcx,
|
||||||
fn_returns,
|
diag,
|
||||||
lifetime.to_string(),
|
fn_returns,
|
||||||
Some(arg),
|
lifetime.to_string(),
|
||||||
captures,
|
Some(arg),
|
||||||
Some((param.param_ty_span, param.param_ty.to_string())),
|
captures,
|
||||||
self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id),
|
Some((param.param_ty_span, param.param_ty.to_string())),
|
||||||
|
Some(suitable_region.def_id),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((alias_tys, alias_span)) = self
|
||||||
|
.infcx
|
||||||
|
.tcx
|
||||||
|
.return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
|
||||||
|
|
||||||
|
// in case the return type of the method is a type alias
|
||||||
|
let mut spans_suggs: Vec<_> = Vec::new();
|
||||||
|
for alias_ty in alias_tys {
|
||||||
|
if alias_ty.span.desugaring_kind().is_some() {
|
||||||
|
// Skip `async` desugaring `impl Future`.
|
||||||
|
()
|
||||||
|
}
|
||||||
|
if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
|
||||||
|
spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
|
||||||
|
diag.multipart_suggestion_verbose(
|
||||||
|
&format!(
|
||||||
|
"to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
|
||||||
|
),
|
||||||
|
spans_suggs,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3524,6 +3524,13 @@ impl<'hir> Node<'hir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> {
|
||||||
|
match self {
|
||||||
|
Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn body_id(&self) -> Option<BodyId> {
|
pub fn body_id(&self) -> Option<BodyId> {
|
||||||
match self {
|
match self {
|
||||||
Node::TraitItem(TraitItem {
|
Node::TraitItem(TraitItem {
|
||||||
|
|
|
@ -997,6 +997,30 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
v.0
|
v.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used
|
||||||
|
pub fn return_type_impl_or_dyn_traits_with_type_alias(
|
||||||
|
self,
|
||||||
|
scope_def_id: LocalDefId,
|
||||||
|
) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> {
|
||||||
|
let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
|
||||||
|
let mut v = TraitObjectVisitor(vec![], self.hir());
|
||||||
|
// when the return type is a type alias
|
||||||
|
if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id)
|
||||||
|
&& let hir::TyKind::Path(hir::QPath::Resolved(
|
||||||
|
None,
|
||||||
|
hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind
|
||||||
|
&& let Some(local_id) = def_id.as_local()
|
||||||
|
&& let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias
|
||||||
|
&& let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics()
|
||||||
|
{
|
||||||
|
v.visit_ty(alias_ty);
|
||||||
|
if !v.0.is_empty() {
|
||||||
|
return Some((v.0, alias_generics.span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
|
pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
|
||||||
// `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures.
|
// `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures.
|
||||||
match self.hir().get_by_def_id(scope_def_id) {
|
match self.hir().get_by_def_id(scope_def_id) {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
trait Greeter0 {
|
||||||
|
fn greet(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Greeter1 {
|
||||||
|
fn greet(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
|
||||||
|
//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
|
||||||
|
|
||||||
|
struct FixedGreeter<'a>(pub &'a str);
|
||||||
|
|
||||||
|
impl Greeter0 for FixedGreeter<'_> {
|
||||||
|
fn greet(&self) {
|
||||||
|
println!("0 {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Greeter1 for FixedGreeter<'_> {
|
||||||
|
fn greet(&self) {
|
||||||
|
println!("1 {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Greetings(pub Vec<String>);
|
||||||
|
|
||||||
|
impl Greetings {
|
||||||
|
pub fn get(&self, i: usize) -> BoxedGreeter {
|
||||||
|
(Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut g = Greetings {0 : vec!()};
|
||||||
|
g.0.push("a".to_string());
|
||||||
|
g.0.push("b".to_string());
|
||||||
|
g.get(0).0.greet();
|
||||||
|
g.get(0).1.greet();
|
||||||
|
g.get(1).0.greet();
|
||||||
|
g.get(1).1.greet();
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
trait Greeter0 {
|
||||||
|
fn greet(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Greeter1 {
|
||||||
|
fn greet(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoxedGreeter = (Box<dyn Greeter0>, Box<dyn Greeter1>);
|
||||||
|
//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
|
||||||
|
|
||||||
|
struct FixedGreeter<'a>(pub &'a str);
|
||||||
|
|
||||||
|
impl Greeter0 for FixedGreeter<'_> {
|
||||||
|
fn greet(&self) {
|
||||||
|
println!("0 {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Greeter1 for FixedGreeter<'_> {
|
||||||
|
fn greet(&self) {
|
||||||
|
println!("1 {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Greetings(pub Vec<String>);
|
||||||
|
|
||||||
|
impl Greetings {
|
||||||
|
pub fn get(&self, i: usize) -> BoxedGreeter {
|
||||||
|
(Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut g = Greetings {0 : vec!()};
|
||||||
|
g.0.push("a".to_string());
|
||||||
|
g.0.push("b".to_string());
|
||||||
|
g.get(0).0.greet();
|
||||||
|
g.get(0).1.greet();
|
||||||
|
g.get(1).0.greet();
|
||||||
|
g.get(1).1.greet();
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:32:9
|
||||||
|
|
|
||||||
|
LL | pub fn get(&self, i: usize) -> BoxedGreeter {
|
||||||
|
| - let's call the lifetime of this reference `'1`
|
||||||
|
LL | (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
|
||||||
|
|
|
||||||
|
help: to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
|
||||||
|
|
|
||||||
|
LL | type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
|
||||||
|
| ++++ ++++ ++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue