Skip to content

Commit 0740015

Browse files
authored
Rollup merge of #85050 - FabianWolff:issue-84592, r=jackh726
Fix suggestions for missing return type lifetime specifiers This pull request aims to fix #84592. The issue is that the current code seems to assume that there is only a single relevant span pointing to the missing lifetime, and only looks at the first one: https://github.com/rust-lang/rust/blob/e5f83d24aee866a14753a7cedbb4e301dfe5bef5/compiler/rustc_resolve/src/late/lifetimes.rs#L2959 This is incorrect, though, and leads to incorrect error messages and invalid suggestions. For instance, the example from #84592: ```rust struct TwoLifetimes<'x, 'y> { x: &'x (), y: &'y (), } fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> { TwoLifetimes { x: &(), y: &() } } ``` currently leads to: ``` error[E0106]: missing lifetime specifiers --> src/main.rs:6:57 | 6 | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> { | --- --- ^^ expected 2 lifetime parameters | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` help: consider introducing a named lifetime parameter | 6 | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'_<'a, 'a>, '_> { | ^^^^ ^^^^^^ ^^^^^^ ^^^^^^^^^^ ``` There are two problems: - The error message is wrong. There is only _one_ lifetime parameter expected at the location pointed to by the error message (and another one at a separate location). - The suggestion is incorrect and will not lead to correct code. With the changes in this PR, I get the following output: ``` error[E0106]: missing lifetime specifiers --> p.rs:6:57 | 6 | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> { | --- --- ^^ ^^ expected named lifetime parameter | | | expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` help: consider introducing a named lifetime parameter | 6 | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> { | ^^^^ ^^^^^^ ^^^^^^ ^^ ^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0106`. ``` Mainly, I changed `add_missing_lifetime_specifiers_label()` to receive a _vector_ of spans (and counts) instead of just one, and adjusted its body accordingly.
2 parents 1b30245 + 2448c76 commit 0740015

File tree

8 files changed

+493
-148
lines changed

8 files changed

+493
-148
lines changed

compiler/rustc_errors/src/diagnostic.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,22 @@ impl Diagnostic {
282282
msg: &str,
283283
suggestion: Vec<(Span, String)>,
284284
applicability: Applicability,
285+
) -> &mut Self {
286+
self.multipart_suggestion_with_style(
287+
msg,
288+
suggestion,
289+
applicability,
290+
SuggestionStyle::ShowCode,
291+
)
292+
}
293+
294+
/// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
295+
pub fn multipart_suggestion_with_style(
296+
&mut self,
297+
msg: &str,
298+
suggestion: Vec<(Span, String)>,
299+
applicability: Applicability,
300+
style: SuggestionStyle,
285301
) -> &mut Self {
286302
assert!(!suggestion.is_empty());
287303
self.suggestions.push(CodeSuggestion {
@@ -292,7 +308,7 @@ impl Diagnostic {
292308
.collect(),
293309
}],
294310
msg: msg.to_owned(),
295-
style: SuggestionStyle::ShowCode,
311+
style,
296312
applicability,
297313
tool_metadata: Default::default(),
298314
});

compiler/rustc_resolve/src/late/diagnostics.rs

+191-143
Large diffs are not rendered by default.

compiler/rustc_resolve/src/late/lifetimes.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -2956,7 +2956,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
29562956
return;
29572957
}
29582958

2959-
let span = lifetime_refs[0].span;
29602959
let mut late_depth = 0;
29612960
let mut scope = self.scope;
29622961
let mut lifetime_names = FxHashSet::default();
@@ -3035,18 +3034,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
30353034
}
30363035
};
30373036

3038-
let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
3037+
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
3038+
spans.sort();
3039+
let mut spans_dedup = spans.clone();
3040+
spans_dedup.dedup();
3041+
let spans_with_counts: Vec<_> = spans_dedup
3042+
.into_iter()
3043+
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
3044+
.collect();
3045+
3046+
let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
30393047

30403048
if let Some(params) = error {
30413049
// If there's no lifetime available, suggest `'static`.
30423050
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
30433051
lifetime_names.insert(kw::StaticLifetime);
30443052
}
30453053
}
3054+
30463055
self.add_missing_lifetime_specifiers_label(
30473056
&mut err,
3048-
span,
3049-
lifetime_refs.len(),
3057+
spans_with_counts,
30503058
&lifetime_names,
30513059
lifetime_spans,
30523060
error.unwrap_or(&[]),
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* Checks whether issue #84592 has been resolved. The issue was
2+
* that in this example, there are two expected/missing lifetime
3+
* parameters with *different spans*, leading to incorrect
4+
* suggestions from rustc.
5+
*/
6+
7+
struct TwoLifetimes<'x, 'y> {
8+
x: &'x (),
9+
y: &'y (),
10+
}
11+
12+
fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
13+
//~^ ERROR missing lifetime specifiers [E0106]
14+
TwoLifetimes { x: &(), y: &() }
15+
}
16+
17+
fn main() {}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0106]: missing lifetime specifiers
2+
--> $DIR/issue-84592.rs:12:57
3+
|
4+
LL | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
5+
| --- --- ^^ ^^ expected named lifetime parameter
6+
| |
7+
| expected named lifetime parameter
8+
|
9+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> {
13+
| ^^^^ ^^^^^^ ^^^^^^ ^^ ^^
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0106`.

src/test/ui/suggestions/missing-lt-for-hrtb.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ note: these named lifetimes are available to use
4444
|
4545
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
4646
| ^^ ^^
47+
help: consider using one of the available lifetimes here
48+
|
49+
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &'lifetime X);
50+
| ^^^^^^^^^^
4751

4852
error[E0106]: missing lifetime specifier
4953
--> $DIR/missing-lt-for-hrtb.rs:5:41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* Checks all four scenarios possible in report_elision_failure() of
2+
* rustc_resolve::late::lifetimes::LifetimeContext related to returning
3+
* borrowed values, in various configurations.
4+
*/
5+
6+
fn f1() -> &i32 { loop {} }
7+
//~^ ERROR missing lifetime specifier [E0106]
8+
fn f1_() -> (&i32, &i32) { loop {} }
9+
//~^ ERROR missing lifetime specifier [E0106]
10+
//~^^ ERROR missing lifetime specifier [E0106]
11+
12+
fn f2(a: i32, b: i32) -> &i32 { loop {} }
13+
//~^ ERROR missing lifetime specifier [E0106]
14+
fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
15+
//~^ ERROR missing lifetime specifier [E0106]
16+
//~^^ ERROR missing lifetime specifier [E0106]
17+
18+
struct S<'a, 'b> { a: &'a i32, b: &'b i32 }
19+
fn f3(s: &S) -> &i32 { loop {} }
20+
//~^ ERROR missing lifetime specifier [E0106]
21+
fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
22+
//~^ ERROR missing lifetime specifier [E0106]
23+
//~^^ ERROR missing lifetime specifier [E0106]
24+
25+
fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
26+
//~^ ERROR missing lifetime specifier [E0106]
27+
fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
28+
//~^ ERROR missing lifetime specifier [E0106]
29+
//~^^ ERROR missing lifetime specifier [E0106]
30+
31+
fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
32+
//~^ ERROR missing lifetime specifier [E0106]
33+
fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
34+
//~^ ERROR missing lifetime specifier [E0106]
35+
//~^^ ERROR missing lifetime specifier [E0106]
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
error[E0106]: missing lifetime specifier
2+
--> $DIR/return-elided-lifetime.rs:6:12
3+
|
4+
LL | fn f1() -> &i32 { loop {} }
5+
| ^ expected named lifetime parameter
6+
|
7+
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
8+
help: consider using the `'static` lifetime
9+
|
10+
LL | fn f1() -> &'static i32 { loop {} }
11+
| ^^^^^^^^
12+
13+
error[E0106]: missing lifetime specifier
14+
--> $DIR/return-elided-lifetime.rs:8:14
15+
|
16+
LL | fn f1_() -> (&i32, &i32) { loop {} }
17+
| ^ expected named lifetime parameter
18+
|
19+
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
20+
help: consider using the `'static` lifetime
21+
|
22+
LL | fn f1_() -> (&'static i32, &i32) { loop {} }
23+
| ^^^^^^^^
24+
25+
error[E0106]: missing lifetime specifier
26+
--> $DIR/return-elided-lifetime.rs:8:20
27+
|
28+
LL | fn f1_() -> (&i32, &i32) { loop {} }
29+
| ^ expected named lifetime parameter
30+
|
31+
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
32+
help: consider using the `'static` lifetime
33+
|
34+
LL | fn f1_() -> (&i32, &'static i32) { loop {} }
35+
| ^^^^^^^^
36+
37+
error[E0106]: missing lifetime specifier
38+
--> $DIR/return-elided-lifetime.rs:12:26
39+
|
40+
LL | fn f2(a: i32, b: i32) -> &i32 { loop {} }
41+
| ^ expected named lifetime parameter
42+
|
43+
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
44+
help: consider using the `'static` lifetime
45+
|
46+
LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} }
47+
| ^^^^^^^^
48+
49+
error[E0106]: missing lifetime specifier
50+
--> $DIR/return-elided-lifetime.rs:14:28
51+
|
52+
LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
53+
| ^ expected named lifetime parameter
54+
|
55+
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
56+
help: consider using the `'static` lifetime
57+
|
58+
LL | fn f2_(a: i32, b: i32) -> (&'static i32, &i32) { loop {} }
59+
| ^^^^^^^^
60+
61+
error[E0106]: missing lifetime specifier
62+
--> $DIR/return-elided-lifetime.rs:14:34
63+
|
64+
LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
65+
| ^ expected named lifetime parameter
66+
|
67+
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
68+
help: consider using the `'static` lifetime
69+
|
70+
LL | fn f2_(a: i32, b: i32) -> (&i32, &'static i32) { loop {} }
71+
| ^^^^^^^^
72+
73+
error[E0106]: missing lifetime specifier
74+
--> $DIR/return-elided-lifetime.rs:19:17
75+
|
76+
LL | fn f3(s: &S) -> &i32 { loop {} }
77+
| -- ^ expected named lifetime parameter
78+
|
79+
= help: this function's return type contains a borrowed value, but the signature does not say which one of `s`'s 3 lifetimes it is borrowed from
80+
help: consider introducing a named lifetime parameter
81+
|
82+
LL | fn f3<'a>(s: &'a S) -> &'a i32 { loop {} }
83+
| ^^^^ ^^^^^ ^^^
84+
85+
error[E0106]: missing lifetime specifier
86+
--> $DIR/return-elided-lifetime.rs:21:26
87+
|
88+
LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
89+
| -- -- ^ expected named lifetime parameter
90+
|
91+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
92+
help: consider introducing a named lifetime parameter
93+
|
94+
LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&'a i32, &i32) { loop {} }
95+
| ^^^^ ^^^^^ ^^^^^ ^^^
96+
97+
error[E0106]: missing lifetime specifier
98+
--> $DIR/return-elided-lifetime.rs:21:32
99+
|
100+
LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
101+
| -- -- ^ expected named lifetime parameter
102+
|
103+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
104+
help: consider introducing a named lifetime parameter
105+
|
106+
LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&i32, &'a i32) { loop {} }
107+
| ^^^^ ^^^^^ ^^^^^ ^^^
108+
109+
error[E0106]: missing lifetime specifier
110+
--> $DIR/return-elided-lifetime.rs:25:42
111+
|
112+
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
113+
| ------- ------- ^ expected named lifetime parameter
114+
|
115+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
116+
note: these named lifetimes are available to use
117+
--> $DIR/return-elided-lifetime.rs:25:7
118+
|
119+
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
120+
| ^^ ^^
121+
help: consider using one of the available lifetimes here
122+
|
123+
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &'lifetime i32 { loop {} }
124+
| ^^^^^^^^^^
125+
126+
error[E0106]: missing lifetime specifier
127+
--> $DIR/return-elided-lifetime.rs:27:44
128+
|
129+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
130+
| ------- ------- ^ expected named lifetime parameter
131+
|
132+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
133+
note: these named lifetimes are available to use
134+
--> $DIR/return-elided-lifetime.rs:27:8
135+
|
136+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
137+
| ^^ ^^
138+
help: consider using one of the available lifetimes here
139+
|
140+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&'lifetime i32, &i32) { loop {} }
141+
| ^^^^^^^^^^
142+
143+
error[E0106]: missing lifetime specifier
144+
--> $DIR/return-elided-lifetime.rs:27:50
145+
|
146+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
147+
| ------- ------- ^ expected named lifetime parameter
148+
|
149+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
150+
note: these named lifetimes are available to use
151+
--> $DIR/return-elided-lifetime.rs:27:8
152+
|
153+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
154+
| ^^ ^^
155+
help: consider using one of the available lifetimes here
156+
|
157+
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &'lifetime i32) { loop {} }
158+
| ^^^^^^^^^^
159+
160+
error[E0106]: missing lifetime specifier
161+
--> $DIR/return-elided-lifetime.rs:31:35
162+
|
163+
LL | fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
164+
| ------- ---- ^ expected named lifetime parameter
165+
|
166+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
167+
help: consider using the `'a` lifetime
168+
|
169+
LL | fn f5<'a>(a: &'a i32, b: &i32) -> &'a i32 { loop {} }
170+
| ^^^
171+
172+
error[E0106]: missing lifetime specifier
173+
--> $DIR/return-elided-lifetime.rs:33:37
174+
|
175+
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
176+
| ------- ---- ^ expected named lifetime parameter
177+
|
178+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
179+
help: consider using the `'a` lifetime
180+
|
181+
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&'a i32, &i32) { loop {} }
182+
| ^^^
183+
184+
error[E0106]: missing lifetime specifier
185+
--> $DIR/return-elided-lifetime.rs:33:43
186+
|
187+
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
188+
| ------- ---- ^ expected named lifetime parameter
189+
|
190+
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
191+
help: consider using the `'a` lifetime
192+
|
193+
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &'a i32) { loop {} }
194+
| ^^^
195+
196+
error: aborting due to 15 previous errors
197+
198+
For more information about this error, try `rustc --explain E0106`.

0 commit comments

Comments
 (0)