Skip to content

Commit a8bd0c0

Browse files
authored
Rollup merge of #106167 - yanchen4791:issue-105544-fix, r=oli-obk
Fix invalid syntax and incomplete suggestion in impl Trait parameter type suggestions for E0311 Fixes #105544 The problems: The suggestion given for E0311 has invalid syntax when the synthetic type parameter is used for Trait type in function declaration: ```rust fn foo(d: impl Sized) -> impl Sized ``` instead of explicitly specified like the following: ```rust fn foo<T: Sized>(d: T) -> impl Sized ``` In addition to the syntax error, the suggestions given for E0311 are not complete when multiple elided lifetimes are involved in lifetime bounds, not all involved parameters are given the named lifetime in the suggestions. For the following test case: ``` fn foo(d: impl Sized, p: &mut ()) -> impl Sized + '_ { (d, p) } ``` a good suggestion should add the lifetime 'a to both d and p, instead of d only: ``` fn foo<'a>(d: impl Sized + 'a, p: &'a mut ()) -> impl Sized + '_ { (d, p) } ``` The Solution: Fix the syntax problem in the suggestions when synthetic type parameter is used, and also add lifetimes for all involved parameters.
2 parents d4203ed + 621d412 commit a8bd0c0

14 files changed

+365
-39
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+77-17
Original file line numberDiff line numberDiff line change
@@ -2144,18 +2144,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
21442144
// suggest adding an explicit lifetime bound to it.
21452145
let generics = self.tcx.generics_of(generic_param_scope);
21462146
// type_param_span is (span, has_bounds)
2147+
let mut is_synthetic = false;
2148+
let mut ast_generics = None;
21472149
let type_param_span = match bound_kind {
21482150
GenericKind::Param(ref param) => {
21492151
// Account for the case where `param` corresponds to `Self`,
21502152
// which doesn't have the expected type argument.
21512153
if !(generics.has_self && param.index == 0) {
21522154
let type_param = generics.type_param(param, self.tcx);
2155+
is_synthetic = type_param.kind.is_synthetic();
21532156
type_param.def_id.as_local().map(|def_id| {
21542157
// Get the `hir::Param` to verify whether it already has any bounds.
21552158
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
21562159
// instead we suggest `T: 'a + 'b` in that case.
21572160
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
2158-
let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
2161+
ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
21592162
let bounds =
21602163
ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
21612164
// `sp` only covers `T`, change it so that it covers
@@ -2187,11 +2190,64 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
21872190
.unwrap_or("'lt".to_string())
21882191
};
21892192

2190-
let add_lt_sugg = generics
2191-
.params
2192-
.first()
2193-
.and_then(|param| param.def_id.as_local())
2194-
.map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt)));
2193+
let mut add_lt_suggs: Vec<Option<_>> = vec![];
2194+
if is_synthetic {
2195+
if let Some(ast_generics) = ast_generics {
2196+
let named_lifetime_param_exist = ast_generics.params.iter().any(|p| {
2197+
matches!(
2198+
p.kind,
2199+
hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
2200+
)
2201+
});
2202+
if named_lifetime_param_exist && let [param, ..] = ast_generics.params
2203+
{
2204+
add_lt_suggs.push(Some((
2205+
self.tcx.def_span(param.def_id).shrink_to_lo(),
2206+
format!("{new_lt}, "),
2207+
)));
2208+
} else {
2209+
add_lt_suggs
2210+
.push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>"))));
2211+
}
2212+
}
2213+
} else {
2214+
if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local()
2215+
{
2216+
add_lt_suggs
2217+
.push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, "))));
2218+
}
2219+
}
2220+
2221+
if let Some(ast_generics) = ast_generics {
2222+
for p in ast_generics.params {
2223+
if p.is_elided_lifetime() {
2224+
if self
2225+
.tcx
2226+
.sess
2227+
.source_map()
2228+
.span_to_prev_source(p.span.shrink_to_hi())
2229+
.ok()
2230+
.map_or(false, |s| *s.as_bytes().last().unwrap() == b'&')
2231+
{
2232+
add_lt_suggs
2233+
.push(Some(
2234+
(
2235+
p.span.shrink_to_hi(),
2236+
if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
2237+
&& snip.starts_with(' ')
2238+
{
2239+
format!("{new_lt}")
2240+
} else {
2241+
format!("{new_lt} ")
2242+
}
2243+
)
2244+
));
2245+
} else {
2246+
add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>"))));
2247+
}
2248+
}
2249+
}
2250+
}
21952251

21962252
let labeled_user_string = match bound_kind {
21972253
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
@@ -2215,20 +2271,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
22152271
);
22162272
}
22172273

2218-
fn binding_suggestion<S: fmt::Display>(
2274+
fn binding_suggestion<'tcx, S: fmt::Display>(
22192275
err: &mut Diagnostic,
22202276
type_param_span: Option<(Span, bool)>,
2221-
bound_kind: GenericKind<'_>,
2277+
bound_kind: GenericKind<'tcx>,
22222278
sub: S,
2223-
add_lt_sugg: Option<(Span, String)>,
2279+
add_lt_suggs: Vec<Option<(Span, String)>>,
22242280
) {
22252281
let msg = "consider adding an explicit lifetime bound";
22262282
if let Some((sp, has_lifetimes)) = type_param_span {
22272283
let suggestion =
22282284
if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
22292285
let mut suggestions = vec![(sp, suggestion)];
2230-
if let Some(add_lt_sugg) = add_lt_sugg {
2231-
suggestions.push(add_lt_sugg);
2286+
for add_lt_sugg in add_lt_suggs {
2287+
if let Some(add_lt_sugg) = add_lt_sugg {
2288+
suggestions.push(add_lt_sugg);
2289+
}
22322290
}
22332291
err.multipart_suggestion_verbose(
22342292
format!("{msg}..."),
@@ -2252,9 +2310,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
22522310
};
22532311
let mut sugg =
22542312
vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
2255-
if let Some(lt) = add_lt_sugg.clone() {
2256-
sugg.push(lt);
2257-
sugg.rotate_right(1);
2313+
for add_lt_sugg in add_lt_suggs.clone() {
2314+
if let Some(lt) = add_lt_sugg {
2315+
sugg.push(lt);
2316+
sugg.rotate_right(1);
2317+
}
22582318
}
22592319
// `MaybeIncorrect` due to issue #41966.
22602320
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
@@ -2358,7 +2418,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23582418
// for the bound is not suitable for suggestions when `-Zverbose` is set because it
23592419
// uses `Debug` output, so we handle it specially here so that suggestions are
23602420
// always correct.
2361-
binding_suggestion(&mut err, type_param_span, bound_kind, name, None);
2421+
binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]);
23622422
err
23632423
}
23642424

@@ -2371,7 +2431,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23712431
"{} may not live long enough",
23722432
labeled_user_string
23732433
);
2374-
binding_suggestion(&mut err, type_param_span, bound_kind, "'static", None);
2434+
binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]);
23752435
err
23762436
}
23772437

@@ -2410,7 +2470,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
24102470
type_param_span,
24112471
bound_kind,
24122472
new_lt,
2413-
add_lt_sugg,
2473+
add_lt_suggs,
24142474
);
24152475
}
24162476
}

tests/ui/error-codes/E0311.fixed

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
6+
with_restriction::<T>(x) //~ ERROR E0311
7+
}
8+
9+
fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
10+
x
11+
}
12+
13+
fn main() {}

tests/ui/error-codes/E0311.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
15
fn no_restriction<T>(x: &()) -> &() {
26
with_restriction::<T>(x) //~ ERROR E0311
37
}

tests/ui/error-codes/E0311.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error[E0311]: the parameter type `T` may not live long enough
2-
--> $DIR/E0311.rs:2:5
2+
--> $DIR/E0311.rs:6:5
33
|
44
LL | with_restriction::<T>(x)
55
| ^^^^^^^^^^^^^^^^^^^^^
66
|
77
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
8-
--> $DIR/E0311.rs:1:25
8+
--> $DIR/E0311.rs:5:25
99
|
1010
LL | fn no_restriction<T>(x: &()) -> &() {
1111
| ^^^
1212
note: ...so that the type `T` will meet its required lifetime bounds
13-
--> $DIR/E0311.rs:2:5
13+
--> $DIR/E0311.rs:6:5
1414
|
1515
LL | with_restriction::<T>(x)
1616
| ^^^^^^^^^^^^^^^^^^^^^
1717
help: consider adding an explicit lifetime bound...
1818
|
19-
LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() {
20-
| +++ ++++
19+
LL | fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
20+
| +++ ++++ ++
2121

2222
error: aborting due to previous error
2323

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
6+
with_restriction::<T>(x) //~ ERROR the parameter type `T` may not live long enough
7+
}
8+
9+
fn with_restriction<'b, T: 'b>(x: &'b ()) -> &'b () {
10+
x
11+
}
12+
13+
fn main() {}

tests/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
15
fn no_restriction<T>(x: &()) -> &() {
26
with_restriction::<T>(x) //~ ERROR the parameter type `T` may not live long enough
37
}

tests/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error[E0311]: the parameter type `T` may not live long enough
2-
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:2:5
2+
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:6:5
33
|
44
LL | with_restriction::<T>(x)
55
| ^^^^^^^^^^^^^^^^^^^^^
66
|
77
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
8-
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:1:25
8+
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:5:25
99
|
1010
LL | fn no_restriction<T>(x: &()) -> &() {
1111
| ^^^
1212
note: ...so that the type `T` will meet its required lifetime bounds
13-
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:2:5
13+
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:6:5
1414
|
1515
LL | with_restriction::<T>(x)
1616
| ^^^^^^^^^^^^^^^^^^^^^
1717
help: consider adding an explicit lifetime bound...
1818
|
19-
LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() {
20-
| +++ ++++
19+
LL | fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
20+
| +++ ++++ ++
2121

2222
error: aborting due to previous error
2323

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
fn foo<'a>(d: impl Sized + 'a, p: &'a mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `impl Sized` must be valid for the anonymous lifetime defined here...
6+
//~^ HELP consider adding an explicit lifetime bound
7+
(d, p)
8+
//~^ ERROR the parameter type `impl Sized` may not live long enough
9+
//~| NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
10+
}
11+
12+
fn foo1<'b>(d: impl Sized + 'b, p: &'b mut ()) -> impl Sized + '_ {
13+
//~^ HELP consider adding an explicit lifetime bound...
14+
(d, p) //~ NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
15+
//~^ ERROR the parameter type `impl Sized` may not live long enough
16+
}
17+
18+
fn foo2<'b, 'a>(d: impl Sized + 'a + 'b, p: &'b mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `impl Sized + 'a` must be valid for the anonymous lifetime defined here...
19+
//~^ HELP consider adding an explicit lifetime bound
20+
(d, p)
21+
//~^ ERROR the parameter type `impl Sized + 'a` may not live long enough
22+
//~| NOTE ...so that the type `impl Sized + 'a` will meet its required lifetime bounds
23+
}
24+
25+
fn bar<'a, T : Sized + 'a>(d: T, p: &'a mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `T` must be valid for the anonymous lifetime defined here...
26+
//~^ HELP consider adding an explicit lifetime bound
27+
(d, p)
28+
//~^ ERROR the parameter type `T` may not live long enough
29+
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
30+
}
31+
32+
fn bar1<'b, T : Sized + 'b>(d: T, p: &'b mut ()) -> impl Sized + '_ {
33+
//~^ HELP consider adding an explicit lifetime bound...
34+
(d, p) //~ NOTE ...so that the type `T` will meet its required lifetime bounds
35+
//~^ ERROR the parameter type `T` may not live long enough
36+
}
37+
38+
fn bar2<'b, 'a, T : Sized + 'a + 'b>(d: T, p: &'b mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `T` must be valid for the anonymous lifetime defined here...
39+
//~^ HELP consider adding an explicit lifetime bound
40+
(d, p)
41+
//~^ ERROR the parameter type `T` may not live long enough
42+
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
43+
}
44+
45+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
fn foo(d: impl Sized, p: &mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `impl Sized` must be valid for the anonymous lifetime defined here...
6+
//~^ HELP consider adding an explicit lifetime bound
7+
(d, p)
8+
//~^ ERROR the parameter type `impl Sized` may not live long enough
9+
//~| NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
10+
}
11+
12+
fn foo1<'b>(d: impl Sized, p: &'b mut ()) -> impl Sized + '_ {
13+
//~^ HELP consider adding an explicit lifetime bound...
14+
(d, p) //~ NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
15+
//~^ ERROR the parameter type `impl Sized` may not live long enough
16+
}
17+
18+
fn foo2<'a>(d: impl Sized + 'a, p: &mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `impl Sized + 'a` must be valid for the anonymous lifetime defined here...
19+
//~^ HELP consider adding an explicit lifetime bound
20+
(d, p)
21+
//~^ ERROR the parameter type `impl Sized + 'a` may not live long enough
22+
//~| NOTE ...so that the type `impl Sized + 'a` will meet its required lifetime bounds
23+
}
24+
25+
fn bar<T : Sized>(d: T, p: & mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `T` must be valid for the anonymous lifetime defined here...
26+
//~^ HELP consider adding an explicit lifetime bound
27+
(d, p)
28+
//~^ ERROR the parameter type `T` may not live long enough
29+
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
30+
}
31+
32+
fn bar1<'b, T : Sized>(d: T, p: &'b mut ()) -> impl Sized + '_ {
33+
//~^ HELP consider adding an explicit lifetime bound...
34+
(d, p) //~ NOTE ...so that the type `T` will meet its required lifetime bounds
35+
//~^ ERROR the parameter type `T` may not live long enough
36+
}
37+
38+
fn bar2<'a, T : Sized + 'a>(d: T, p: &mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `T` must be valid for the anonymous lifetime defined here...
39+
//~^ HELP consider adding an explicit lifetime bound
40+
(d, p)
41+
//~^ ERROR the parameter type `T` may not live long enough
42+
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
43+
}
44+
45+
fn main() {}

0 commit comments

Comments
 (0)