Skip to content

Fix invalid syntax and incomplete suggestion in impl Trait parameter type suggestions for E0311 #106167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 77 additions & 17 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2144,18 +2144,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// suggest adding an explicit lifetime bound to it.
let generics = self.tcx.generics_of(generic_param_scope);
// type_param_span is (span, has_bounds)
let mut is_synthetic = false;
let mut ast_generics = None;
let type_param_span = match bound_kind {
GenericKind::Param(ref param) => {
// Account for the case where `param` corresponds to `Self`,
// which doesn't have the expected type argument.
if !(generics.has_self && param.index == 0) {
let type_param = generics.type_param(param, self.tcx);
is_synthetic = type_param.kind.is_synthetic();
type_param.def_id.as_local().map(|def_id| {
// Get the `hir::Param` to verify whether it already has any bounds.
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
// instead we suggest `T: 'a + 'b` in that case.
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
let bounds =
ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
// `sp` only covers `T`, change it so that it covers
Expand Down Expand Up @@ -2187,11 +2190,64 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
.unwrap_or("'lt".to_string())
};

let add_lt_sugg = generics
.params
.first()
.and_then(|param| param.def_id.as_local())
.map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt)));
let mut add_lt_suggs: Vec<Option<_>> = vec![];
if is_synthetic {
if let Some(ast_generics) = ast_generics {
let named_lifetime_param_exist = ast_generics.params.iter().any(|p| {
matches!(
p.kind,
hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
)
});
if named_lifetime_param_exist && let [param, ..] = ast_generics.params
{
add_lt_suggs.push(Some((
self.tcx.def_span(param.def_id).shrink_to_lo(),
format!("{new_lt}, "),
)));
} else {
add_lt_suggs
.push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>"))));
}
}
} else {
if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local()
{
add_lt_suggs
.push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, "))));
}
}

if let Some(ast_generics) = ast_generics {
for p in ast_generics.params {
if p.is_elided_lifetime() {
if self
.tcx
.sess
.source_map()
.span_to_prev_source(p.span.shrink_to_hi())
.ok()
.map_or(false, |s| *s.as_bytes().last().unwrap() == b'&')
{
add_lt_suggs
.push(Some(
(
p.span.shrink_to_hi(),
if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
&& snip.starts_with(' ')
{
format!("{new_lt}")
} else {
format!("{new_lt} ")
}
)
));
} else {
add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>"))));
}
}
}
}

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

fn binding_suggestion<S: fmt::Display>(
fn binding_suggestion<'tcx, S: fmt::Display>(
err: &mut Diagnostic,
type_param_span: Option<(Span, bool)>,
bound_kind: GenericKind<'_>,
bound_kind: GenericKind<'tcx>,
sub: S,
add_lt_sugg: Option<(Span, String)>,
add_lt_suggs: Vec<Option<(Span, String)>>,
) {
let msg = "consider adding an explicit lifetime bound";
if let Some((sp, has_lifetimes)) = type_param_span {
let suggestion =
if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
let mut suggestions = vec![(sp, suggestion)];
if let Some(add_lt_sugg) = add_lt_sugg {
suggestions.push(add_lt_sugg);
for add_lt_sugg in add_lt_suggs {
if let Some(add_lt_sugg) = add_lt_sugg {
suggestions.push(add_lt_sugg);
}
}
err.multipart_suggestion_verbose(
format!("{msg}..."),
Expand All @@ -2252,9 +2310,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
let mut sugg =
vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
if let Some(lt) = add_lt_sugg.clone() {
sugg.push(lt);
sugg.rotate_right(1);
for add_lt_sugg in add_lt_suggs.clone() {
if let Some(lt) = add_lt_sugg {
sugg.push(lt);
sugg.rotate_right(1);
}
}
// `MaybeIncorrect` due to issue #41966.
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
Expand Down Expand Up @@ -2358,7 +2418,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// for the bound is not suitable for suggestions when `-Zverbose` is set because it
// uses `Debug` output, so we handle it specially here so that suggestions are
// always correct.
binding_suggestion(&mut err, type_param_span, bound_kind, name, None);
binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]);
err
}

Expand All @@ -2371,7 +2431,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
"{} may not live long enough",
labeled_user_string
);
binding_suggestion(&mut err, type_param_span, bound_kind, "'static", None);
binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]);
err
}

Expand Down Expand Up @@ -2410,7 +2470,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
type_param_span,
bound_kind,
new_lt,
add_lt_sugg,
add_lt_suggs,
);
}
}
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/error-codes/E0311.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// run-rustfix

#![allow(warnings)]

fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
with_restriction::<T>(x) //~ ERROR E0311
}

fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
x
}

fn main() {}
4 changes: 4 additions & 0 deletions tests/ui/error-codes/E0311.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// run-rustfix

#![allow(warnings)]

fn no_restriction<T>(x: &()) -> &() {
with_restriction::<T>(x) //~ ERROR E0311
}
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/error-codes/E0311.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/E0311.rs:2:5
--> $DIR/E0311.rs:6:5
|
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> $DIR/E0311.rs:1:25
--> $DIR/E0311.rs:5:25
|
LL | fn no_restriction<T>(x: &()) -> &() {
| ^^^
note: ...so that the type `T` will meet its required lifetime bounds
--> $DIR/E0311.rs:2:5
--> $DIR/E0311.rs:6:5
|
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
help: consider adding an explicit lifetime bound...
|
LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() {
| +++ ++++
LL | fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
| +++ ++++ ++

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// run-rustfix

#![allow(warnings)]

fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
with_restriction::<T>(x) //~ ERROR the parameter type `T` may not live long enough
}

fn with_restriction<'b, T: 'b>(x: &'b ()) -> &'b () {
x
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// run-rustfix

#![allow(warnings)]

fn no_restriction<T>(x: &()) -> &() {
with_restriction::<T>(x) //~ ERROR the parameter type `T` may not live long enough
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:2:5
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:6:5
|
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:1:25
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:5:25
|
LL | fn no_restriction<T>(x: &()) -> &() {
| ^^^
note: ...so that the type `T` will meet its required lifetime bounds
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:2:5
--> $DIR/suggest-introducing-and-adding-missing-lifetime.rs:6:5
|
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
help: consider adding an explicit lifetime bound...
|
LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() {
| +++ ++++
LL | fn no_restriction<'a, T: 'a>(x: &'a ()) -> &() {
| +++ ++++ ++

error: aborting due to previous error

Expand Down
45 changes: 45 additions & 0 deletions tests/ui/suggestions/lifetimes/issue-105544.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// run-rustfix

#![allow(warnings)]

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `impl Sized` may not live long enough
//~| NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
}

fn foo1<'b>(d: impl Sized + 'b, p: &'b mut ()) -> impl Sized + '_ {
//~^ HELP consider adding an explicit lifetime bound...
(d, p) //~ NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
//~^ ERROR the parameter type `impl Sized` may not live long enough
}

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `impl Sized + 'a` may not live long enough
//~| NOTE ...so that the type `impl Sized + 'a` will meet its required lifetime bounds
}

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `T` may not live long enough
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
}

fn bar1<'b, T : Sized + 'b>(d: T, p: &'b mut ()) -> impl Sized + '_ {
//~^ HELP consider adding an explicit lifetime bound...
(d, p) //~ NOTE ...so that the type `T` will meet its required lifetime bounds
//~^ ERROR the parameter type `T` may not live long enough
}

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `T` may not live long enough
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
}

fn main() {}
45 changes: 45 additions & 0 deletions tests/ui/suggestions/lifetimes/issue-105544.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// run-rustfix

#![allow(warnings)]

fn foo(d: impl Sized, p: &mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `impl Sized` must be valid for the anonymous lifetime defined here...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `impl Sized` may not live long enough
//~| NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
}

fn foo1<'b>(d: impl Sized, p: &'b mut ()) -> impl Sized + '_ {
//~^ HELP consider adding an explicit lifetime bound...
(d, p) //~ NOTE ...so that the type `impl Sized` will meet its required lifetime bounds
//~^ ERROR the parameter type `impl Sized` may not live long enough
}

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `impl Sized + 'a` may not live long enough
//~| NOTE ...so that the type `impl Sized + 'a` will meet its required lifetime bounds
}

fn bar<T : Sized>(d: T, p: & mut ()) -> impl Sized + '_ { //~ NOTE the parameter type `T` must be valid for the anonymous lifetime defined here...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `T` may not live long enough
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
}

fn bar1<'b, T : Sized>(d: T, p: &'b mut ()) -> impl Sized + '_ {
//~^ HELP consider adding an explicit lifetime bound...
(d, p) //~ NOTE ...so that the type `T` will meet its required lifetime bounds
//~^ ERROR the parameter type `T` may not live long enough
}

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...
//~^ HELP consider adding an explicit lifetime bound
(d, p)
//~^ ERROR the parameter type `T` may not live long enough
//~| NOTE ...so that the type `T` will meet its required lifetime bounds
}

fn main() {}
Loading