Skip to content

Commit 381b334

Browse files
committed
Point at return type with implicit lifetime and suggest making it explicit
``` error: lifetime may not live long enough --> $DIR/conflicting-lifetime-bounds-from-impl-in-self-literal.rs:21:25 | LL | impl<'a> Path<'a> { | -- lifetime `'a` defined here ... LL | pub fn sub(&mut self) -> Path { | - ---- this returned type has an implicit lifetime `'1` | | | let's call the lifetime of this reference `'1` ... LL | Self { | ---- requires lifetime `'a` from the `impl` ... LL | string: self.string, | ^^^^^^^^^^^ this usage requires that `'1` must outlive `'a` | help: consider using the type's name directly instead, to avoid conflicting lifetime bounds from the `impl` | LL - Self { LL + Path { | help: consider making explicit the implicit `'1` lifetime in the return type | LL | pub fn sub(&mut self) -> Path<'_> { | ++++ ```
1 parent c095f0f commit 381b334

5 files changed

Lines changed: 408 additions & 48 deletions

‎compiler/rustc_borrowck/src/diagnostics/region_errors.rs‎

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -826,14 +826,100 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
826826
self.suggest_move_on_borrowing_closure(&mut diag);
827827
self.suggest_deref_closure_return(&mut diag);
828828
self.detect_self_ty(&mut diag, *span, category, outlived_fr_name);
829-
830-
// FIXME(estebank): we should detect when a lifetime obligation comes from a return type
831-
// that has a hidden implicit `'_` lifetime, like when using `Type` when
832-
// it was declared `struct Type<'a>(&'a ());`.
829+
self.point_at_implicit_lifetime_in_return_type(&mut diag, *fr, fr_name);
833830

834831
diag
835832
}
836833

834+
/// Point at `Path` in return type when declared `struct Path<'a>(&'a ());`.
835+
///
836+
/// ```text
837+
/// LL | pub fn sub(&mut self) -> Path {
838+
/// | - ---- this returned type has an implicit lifetime `'1`
839+
/// | |
840+
/// | let's call the lifetime of this reference `'1`
841+
/// ...
842+
/// help: consider making explicit the implicit `'1` lifetime in the return type
843+
/// |
844+
/// LL | pub fn sub(&mut self) -> Path<'_> {
845+
/// | ++++
846+
/// ```
847+
fn point_at_implicit_lifetime_in_return_type(
848+
&self,
849+
diag: &mut Diag<'_>,
850+
fr: RegionVid,
851+
fr_name: RegionName,
852+
) {
853+
let body = self.infcx.tcx.hir_body_owned_by(self.mir_def_id());
854+
let expr = &body.value.peel_blocks();
855+
if let Some(fn_hir_id) = self.infcx.tcx.hir_get_fn_id_for_return_block(expr.hir_id)
856+
&& let Some(fn_decl) = self.infcx.tcx.hir_node(fn_hir_id).fn_decl()
857+
&& let hir::FnRetTy::Return(ret) = fn_decl.output
858+
{
859+
struct V<'tcx> {
860+
lifetimes: Vec<&'tcx hir::Lifetime>,
861+
tcx: TyCtxt<'tcx>,
862+
}
863+
impl<'tcx> Visitor<'tcx> for V<'tcx> {
864+
type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
865+
866+
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
867+
self.tcx
868+
}
869+
870+
fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) -> Self::Result {
871+
self.lifetimes.push(lifetime);
872+
hir::intravisit::walk_lifetime(self, lifetime)
873+
}
874+
}
875+
let tcx = self.infcx.tcx;
876+
let mut v = V { lifetimes: vec![], tcx };
877+
v.visit_ty_unambig(ret);
878+
if let Some(region) = self.regioncx.to_error_region(fr) {
879+
for lifetime in v.lifetimes {
880+
if let hir::LifetimeKind::Param(lt_def_id) = lifetime.kind
881+
&& lifetime.is_implicit()
882+
&& let hir::LifetimeSource::Path { angle_brackets } = lifetime.source
883+
&& let Some(def_id) =
884+
region.opt_param_def_id(self.infcx.tcx, self.mir_def_id().to_def_id())
885+
&& def_id == lt_def_id.to_def_id()
886+
{
887+
let (msg, sugg) = match angle_brackets {
888+
hir::AngleBrackets::Missing => (
889+
format!("this returned type has an implicit lifetime `{fr_name}`"),
890+
format!("<{region}>"),
891+
),
892+
hir::AngleBrackets::Empty => (
893+
format!(
894+
"there is an implicit lifetime `{fr_name}` in this empty \
895+
parameter list",
896+
),
897+
format!("{region}"),
898+
),
899+
hir::AngleBrackets::Full => (
900+
format!(
901+
"there is an implicit lifetime `{fr_name}` in this parameter \
902+
list",
903+
),
904+
format!("{region}, "),
905+
),
906+
};
907+
diag.span_label(lifetime.ident.span, msg);
908+
diag.span_suggestion_verbose(
909+
lifetime.ident.span.shrink_to_hi(),
910+
format!(
911+
"consider making explicit the implicit `{fr_name}` lifetime in the \
912+
return type",
913+
),
914+
sugg,
915+
Applicability::MachineApplicable,
916+
);
917+
}
918+
}
919+
}
920+
}
921+
}
922+
837923
/// Detect lifetime errors caused by lifetime requirements coming from the enclosing `impl`
838924
/// because of the use of `Self` as a binding type or as a struct/enum literal.
839925
///

‎tests/ui/borrowck/conflicting-lifetime-bounds-from-impl-in-self-literal.fixed‎

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
// https://github.com/rust-lang/rust/issues/101393
22
//@ run-rustfix
3-
#![allow(unused, mismatched_lifetime_syntaxes)]
3+
#![allow(unused)]
44

55
mod _struct {
6-
pub struct Path<'a> {
7-
string: &'a mut String,
6+
pub struct Path<'a, T = String> {
7+
string: &'a mut T,
88
}
99

1010
impl<'a> Path<'a> {
1111
//~^ NOTE lifetime `'a` defined here
1212
//~| NOTE lifetime `'a` defined here
1313
//~| NOTE lifetime `'a` defined here
14-
pub fn sub(&mut self) -> Path/*<'_>*/ {
14+
pub fn sub(&mut self) -> Path<'_> {
1515
//~^ NOTE: let's call the lifetime of this reference `'1`
16-
// FIXME(estebank): we should extend the diagnostic to mention that `Path` in the
17-
// return type has a hidden `'_` lifetime in it.
16+
//~| NOTE: this returned type has an implicit lifetime `'1`
17+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
1818
Path {
1919
//~^ NOTE: requires lifetime `'a` from the `impl`
2020
//~| HELP: consider using the type's name directly instead
@@ -23,8 +23,10 @@ mod _struct {
2323
//~| NOTE: this usage requires that `'1` must outlive `'a`
2424
}
2525
}
26-
pub fn sup(&mut self) -> Path/*<'_>*/ {
26+
pub fn sup(&mut self) -> Path<'_> {
2727
//~^ NOTE: let's call the lifetime of this reference `'1`
28+
//~| NOTE: this returned type has an implicit lifetime `'1`
29+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
2830
let x = Path {
2931
//~^ NOTE: requires lifetime `'a` from the `impl`
3032
//~| HELP: consider using the type's name directly instead
@@ -34,8 +36,10 @@ mod _struct {
3436
};
3537
x
3638
}
37-
pub fn soup(&mut self) -> Path/*<'_>*/ {
39+
pub fn soup(&mut self) -> Path<'_> {
3840
//~^ NOTE: let's call the lifetime of this reference `'1`
41+
//~| NOTE: this returned type has an implicit lifetime `'1`
42+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
3943
let x: Path<'_> = Path {
4044
//~^ ERROR: lifetime may not live long enough
4145
//~| NOTE: type annotation requires that `'1` must outlive `'a`
@@ -47,15 +51,76 @@ mod _struct {
4751
}
4852
}
4953

54+
mod _struct2 {
55+
pub struct Path<'a, T = String> {
56+
string: &'a mut T,
57+
}
58+
59+
impl<'a> Path<'a, String> {
60+
//~^ NOTE lifetime `'a` defined here
61+
//~| NOTE lifetime `'a` defined here
62+
//~| NOTE lifetime `'a` defined here
63+
//~| NOTE lifetime `'a` defined here
64+
pub fn sub(&mut self) -> Path<'_, String> {
65+
//~^ NOTE: let's call the lifetime of this reference `'1`
66+
//~| NOTE: there is an implicit lifetime `'1` in this parameter list
67+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
68+
Path {
69+
//~^ NOTE: requires lifetime `'a` from the `impl`
70+
//~| HELP: consider using the type's name directly instead
71+
string: self.string,
72+
//~^ ERROR: lifetime may not live long enough
73+
//~| NOTE: this usage requires that `'1` must outlive `'a`
74+
}
75+
}
76+
pub fn sup(&mut self) -> Path<'_> {
77+
//~^ NOTE: let's call the lifetime of this reference `'1`
78+
//~| NOTE: there is an implicit lifetime `'1` in this empty parameter list
79+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
80+
let x = Path {
81+
//~^ NOTE: requires lifetime `'a` from the `impl`
82+
//~| HELP: consider using the type's name directly instead
83+
string: self.string,
84+
//~^ ERROR: lifetime may not live long enough
85+
//~| NOTE: this usage requires that `'1` must outlive `'a`
86+
};
87+
x
88+
}
89+
pub fn soup(&mut self) -> Path<'_> {
90+
//~^ NOTE: let's call the lifetime of this reference `'1`
91+
let x: Path<'_> = Path {
92+
//~^ ERROR: lifetime may not live long enough
93+
//~| NOTE: type annotation requires that `'1` must outlive `'a`
94+
//~| HELP: consider using the type directly instead
95+
string: self.string,
96+
};
97+
x
98+
}
99+
pub fn sap<'b>(&'b mut self) -> Path<'b> {
100+
//~^ NOTE: lifetime `'b` defined here
101+
Path {
102+
//~^ NOTE: requires lifetime `'a` from the `impl`
103+
//~| HELP: consider using the type's name directly instead
104+
string: self.string,
105+
//~^ ERROR: lifetime may not live long enough
106+
//~| NOTE: this usage requires that `'b` must outlive `'a`
107+
//~| HELP: consider adding the following bound: `'b: 'a`
108+
}
109+
}
110+
}
111+
}
112+
50113
mod tuple_struct {
51114
pub struct Path<'a>(&'a mut String);
52115

53116
impl<'a> Path<'a> {
54117
//~^ NOTE lifetime `'a` defined here
55118
//~| NOTE lifetime `'a` defined here
56119
//~| NOTE lifetime `'a` defined here
57-
pub fn sub(&mut self) -> Path/*<'_>*/ {
120+
pub fn sub(&mut self) -> Path<'_> {
58121
//~^ NOTE: let's call the lifetime of this reference `'1`
122+
//~| NOTE: this returned type has an implicit lifetime `'1`
123+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
59124
Path(
60125
//~^ HELP: consider using the type's name directly instead
61126
//~| NOTE: requires lifetime `'a` from the `impl`
@@ -64,8 +129,10 @@ mod tuple_struct {
64129
//~| NOTE: this usage requires that `'1` must outlive `'a`
65130
)
66131
}
67-
pub fn sup(&mut self) -> Path/*<'_>*/ {
132+
pub fn sup(&mut self) -> Path<'_> {
68133
//~^ NOTE: let's call the lifetime of this reference `'1`
134+
//~| NOTE: this returned type has an implicit lifetime `'1`
135+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
69136
let x = Path(
70137
//~^ NOTE: requires lifetime `'a` from the `impl`
71138
//~| HELP: consider using the type's name directly instead
@@ -75,8 +142,10 @@ mod tuple_struct {
75142
);
76143
x
77144
}
78-
pub fn soup(&mut self) -> Path/*<'_>*/ {
145+
pub fn soup(&mut self) -> Path<'_> {
79146
//~^ NOTE: let's call the lifetime of this reference `'1`
147+
//~| NOTE: this returned type has an implicit lifetime `'1`
148+
//~| HELP: consider making explicit the implicit `'1` lifetime in the return type
80149
let x: Path<'_> = Path(
81150
//~^ ERROR: lifetime may not live long enough
82151
//~| NOTE: type annotation requires that `'1` must outlive `'a`

0 commit comments

Comments
 (0)