Add hint for missing lifetime bound on trait object when type alias is used

This commit is contained in:
yanchen4791 2022-12-05 16:51:49 -08:00
parent c8e6a9e8b6
commit 62a1e76d2b
6 changed files with 176 additions and 19 deletions

View file

@ -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,
); );
} }
} }

View file

@ -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 {

View file

@ -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) {

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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