Skip to content

Commit 190589f

Browse files
committed
Use structured suggestion for restricting bounds
When a trait bound is not met and restricting a type parameter would make the restriction hold, use a structured suggestion pointing at an appropriate place (type param in param list or `where` clause). Account for opaque parameters where instead of suggesting extending the `where` clause, we suggest appending the new restriction: `fn foo(impl Trait + UnmetTrait)`.
1 parent 237d54f commit 190589f

25 files changed

+335
-68
lines changed

src/librustc/traits/error_reporting.rs

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,8 +715,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
715715
// these notes will often be of the form
716716
// "the type `T` can't be frobnicated"
717717
// which is somewhat confusing.
718-
err.help(&format!("consider adding a `where {}` bound",
719-
trait_ref.to_predicate()));
718+
self.suggest_restricting_param_bound(
719+
&mut err,
720+
&trait_ref,
721+
obligation.cause.body_id,
722+
);
720723
} else {
721724
if !have_alt_message {
722725
// Can't show anything else useful, try to find similar impls.
@@ -960,6 +963,96 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
960963
err.emit();
961964
}
962965

966+
fn suggest_restricting_param_bound(
967+
&self,
968+
err: &mut DiagnosticBuilder<'_>,
969+
trait_ref: &ty::PolyTraitRef<'_>,
970+
body_id: hir::HirId,
971+
) {
972+
let node = self.tcx.hir().find(self.tcx.hir().get_parent_item(body_id));
973+
if let ty::Param(param_ty) = &trait_ref.self_ty().kind {
974+
let restrict_msg = "consider further restricting this bound";
975+
let param_name = param_ty.name.as_str();
976+
977+
if let Some(hir::Node::Item(hir::Item {
978+
kind: hir::ItemKind::Struct(_, generics), span, ..
979+
})) |
980+
Some(hir::Node::Item(hir::Item {
981+
kind: hir::ItemKind::Enum(_, generics), span, ..
982+
})) |
983+
Some(hir::Node::Item(hir::Item {
984+
kind: hir::ItemKind::Union(_, generics), span, ..
985+
})) |
986+
Some(hir::Node::Item(hir::Item {
987+
kind: hir::ItemKind::Trait(_, _, generics, ..), span, ..
988+
})) |
989+
Some(hir::Node::Item(hir::Item {
990+
kind: hir::ItemKind::Impl(_, _, _, generics, ..), span, ..
991+
})) |
992+
Some(hir::Node::Item(hir::Item {
993+
kind: hir::ItemKind::Fn(_, _, generics, _), span, ..
994+
})) = &node {
995+
for param in &generics.params {
996+
if param_name == param.name.ident().as_str() {
997+
if param_name.starts_with("impl ") {
998+
err.span_suggestion(
999+
param.span,
1000+
restrict_msg,
1001+
// `impl CurrentTrait + MissingTrait`
1002+
format!("{} + {}", param.name.ident(), trait_ref),
1003+
Applicability::MachineApplicable,
1004+
);
1005+
} else {
1006+
if generics.where_clause.predicates.is_empty() &&
1007+
param.bounds.is_empty()
1008+
{
1009+
err.span_suggestion(
1010+
param.span,
1011+
"consider restricting this bound",
1012+
format!("{}", trait_ref.to_predicate()),
1013+
Applicability::MachineApplicable,
1014+
);
1015+
} else if !generics.where_clause.predicates.is_empty() {
1016+
err.span_suggestion(
1017+
generics.where_clause.span().unwrap().shrink_to_hi(),
1018+
&format!(
1019+
"consider further restricting type parameter `{}`",
1020+
param_ty,
1021+
),
1022+
format!(", {}", trait_ref.to_predicate()),
1023+
Applicability::MachineApplicable,
1024+
);
1025+
} else {
1026+
let sp = param.span.with_hi(span.hi());
1027+
let span = self.tcx.sess.source_map().span_through_char(sp, ':');
1028+
if sp != param.span && sp != span {
1029+
// Only suggest if we have high certainty that the span covers
1030+
// the colon in `foo<T: Trait>`.
1031+
err.span_suggestion(span, restrict_msg, format!(
1032+
"{} + ",
1033+
trait_ref.to_predicate(),
1034+
), Applicability::MachineApplicable);
1035+
} else {
1036+
err.span_label(param.span, &format!(
1037+
"consider adding a `where {}` bound",
1038+
trait_ref.to_predicate(),
1039+
));
1040+
}
1041+
}
1042+
}
1043+
return;
1044+
}
1045+
}
1046+
}
1047+
}
1048+
// FIXME: Add special check for `?Sized` so we don't suggest `T: Sized + ?Sized`.
1049+
1050+
// Fallback in case we didn't find the type argument. Can happen on associated types
1051+
// bounds and when `Self` needs to be restricted, like in the ui test
1052+
// `associated-types-projection-to-unrelated-trait-in-method-without-default.rs`.
1053+
err.help(&format!("consider adding a `where {}` bound", trait_ref.to_predicate()));
1054+
}
1055+
9631056
/// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a
9641057
/// suggestion to borrow the initializer in order to use have a slice instead.
9651058
fn suggest_borrow_on_unsized_slice(

src/test/ui/associated-const/associated-const-type-parameter-arrays-2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ error[E0277]: the trait bound `A: Foo` is not satisfied
44
LL | const Y: usize;
55
| --------------- required by `Foo::Y`
66
...
7+
LL | pub fn test<A: Foo, B: Foo>() {
8+
| -- help: consider further restricting this bound: `A: Foo +`
79
LL | let _array = [4; <A as Foo>::Y];
810
| ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A`
9-
|
10-
= help: consider adding a `where A: Foo` bound
1111

1212
error: aborting due to previous error
1313

src/test/ui/associated-const/associated-const-type-parameter-arrays.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ error[E0277]: the trait bound `A: Foo` is not satisfied
44
LL | const Y: usize;
55
| --------------- required by `Foo::Y`
66
...
7+
LL | pub fn test<A: Foo, B: Foo>() {
8+
| -- help: consider further restricting this bound: `A: Foo +`
79
LL | let _array: [u32; <A as Foo>::Y];
810
| ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A`
9-
|
10-
= help: consider adding a `where A: Foo` bound
1111

1212
error: aborting due to previous error
1313

src/test/ui/associated-types/associated-types-invalid-trait-ref-issue-18865.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
error[E0277]: the trait bound `T: Foo<usize>` is not satisfied
22
--> $DIR/associated-types-invalid-trait-ref-issue-18865.rs:10:12
33
|
4+
LL | fn f<T:Foo<isize>>(t: &T) {
5+
| -- help: consider further restricting this bound: `T: Foo<usize> +`
46
LL | let u: <T as Foo<usize>>::Bar = t.get_bar();
57
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo<usize>` is not implemented for `T`
6-
|
7-
= help: consider adding a `where T: Foo<usize>` bound
88

99
error: aborting due to previous error
1010

src/test/ui/bad/bad-method-typaram-kind.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
error[E0277]: `T` cannot be sent between threads safely
22
--> $DIR/bad-method-typaram-kind.rs:2:7
33
|
4+
LL | fn foo<T:'static>() {
5+
| -- help: consider further restricting this bound: `T: std::marker::Send +`
46
LL | 1.bar::<T>();
57
| ^^^ `T` cannot be sent between threads safely
68
|
79
= help: the trait `std::marker::Send` is not implemented for `T`
8-
= help: consider adding a `where T: std::marker::Send` bound
910

1011
error: aborting due to previous error
1112

src/test/ui/closures/closure-bounds-subtype.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ error[E0277]: `F` cannot be shared between threads safely
44
LL | fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send {
55
| ---------------- ---- required by this bound in `take_const_owned`
66
...
7+
LL | fn give_owned<F>(f: F) where F: FnOnce() + Send {
8+
| - help: consider further restricting type parameter `F`: `, F: std::marker::Sync`
9+
LL | take_any(f);
710
LL | take_const_owned(f);
811
| ^ `F` cannot be shared between threads safely
912
|
1013
= help: the trait `std::marker::Sync` is not implemented for `F`
11-
= help: consider adding a `where F: std::marker::Sync` bound
1214

1315
error: aborting due to previous error
1416

src/test/ui/dst/dst-object-from-unsized-type.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
error[E0277]: the size for values of type `T` cannot be known at compilation time
22
--> $DIR/dst-object-from-unsized-type.rs:8:23
33
|
4+
LL | fn test1<T: ?Sized + Foo>(t: &T) {
5+
| -- help: consider further restricting this bound: `T: std::marker::Sized +`
46
LL | let u: &dyn Foo = t;
57
| ^ doesn't have a size known at compile-time
68
|
79
= help: the trait `std::marker::Sized` is not implemented for `T`
810
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
9-
= help: consider adding a `where T: std::marker::Sized` bound
1011
= note: required for the cast to the object type `dyn Foo`
1112

1213
error[E0277]: the size for values of type `T` cannot be known at compilation time
1314
--> $DIR/dst-object-from-unsized-type.rs:13:23
1415
|
16+
LL | fn test2<T: ?Sized + Foo>(t: &T) {
17+
| -- help: consider further restricting this bound: `T: std::marker::Sized +`
1518
LL | let v: &dyn Foo = t as &dyn Foo;
1619
| ^ doesn't have a size known at compile-time
1720
|
1821
= help: the trait `std::marker::Sized` is not implemented for `T`
1922
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
20-
= help: consider adding a `where T: std::marker::Sized` bound
2123
= note: required for the cast to the object type `dyn Foo`
2224

2325
error[E0277]: the size for values of type `str` cannot be known at compilation time

src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ LL | fn want_bar_for_any_ccx<B>(b: &B)
66
LL | where B : for<'ccx> Bar<'ccx>
77
| ------------------- required by this bound in `want_bar_for_any_ccx`
88
...
9+
LL | where B : Qux
10+
| - help: consider further restricting type parameter `B`: `, for<'ccx> B: Bar<'ccx>`
11+
...
912
LL | want_bar_for_any_ccx(b);
1013
| ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`
11-
|
12-
= help: consider adding a `where for<'ccx> B: Bar<'ccx>` bound
1314

1415
error: aborting due to previous error
1516

src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
error[E0277]: the trait bound `for<'tcx> F: Foo<'tcx>` is not satisfied
22
--> $DIR/hrtb-higher-ranker-supertraits.rs:18:26
33
|
4+
LL | where F : Foo<'x>
5+
| - help: consider further restricting type parameter `F`: `, for<'tcx> F: Foo<'tcx>`
6+
...
47
LL | want_foo_for_any_tcx(f);
58
| ^ the trait `for<'tcx> Foo<'tcx>` is not implemented for `F`
69
...
710
LL | fn want_foo_for_any_tcx<F>(f: &F)
811
| --------------------
912
LL | where F : for<'tcx> Foo<'tcx>
1013
| ------------------- required by this bound in `want_foo_for_any_tcx`
11-
|
12-
= help: consider adding a `where for<'tcx> F: Foo<'tcx>` bound
1314

1415
error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied
1516
--> $DIR/hrtb-higher-ranker-supertraits.rs:35:26
1617
|
18+
LL | where B : Bar<'x>
19+
| - help: consider further restricting type parameter `B`: `, for<'ccx> B: Bar<'ccx>`
20+
...
1721
LL | want_bar_for_any_ccx(b);
1822
| ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`
1923
...
2024
LL | fn want_bar_for_any_ccx<B>(b: &B)
2125
| --------------------
2226
LL | where B : for<'ccx> Bar<'ccx>
2327
| ------------------- required by this bound in `want_bar_for_any_ccx`
24-
|
25-
= help: consider adding a `where for<'ccx> B: Bar<'ccx>` bound
2628

2729
error: aborting due to 2 previous errors
2830

src/test/ui/kindck/kindck-impl-type-params.stderr

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,50 @@
11
error[E0277]: `T` cannot be sent between threads safely
22
--> $DIR/kindck-impl-type-params.rs:18:13
33
|
4+
LL | fn f<T>(val: T) {
5+
| - help: consider restricting this bound: `T: std::marker::Send`
6+
LL | let t: S<T> = S(marker::PhantomData);
47
LL | let a = &t as &dyn Gettable<T>;
58
| ^^ `T` cannot be sent between threads safely
69
|
710
= help: the trait `std::marker::Send` is not implemented for `T`
8-
= help: consider adding a `where T: std::marker::Send` bound
911
= note: required because of the requirements on the impl of `Gettable<T>` for `S<T>`
1012
= note: required for the cast to the object type `dyn Gettable<T>`
1113

1214
error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
1315
--> $DIR/kindck-impl-type-params.rs:18:13
1416
|
17+
LL | fn f<T>(val: T) {
18+
| - help: consider restricting this bound: `T: std::marker::Copy`
19+
LL | let t: S<T> = S(marker::PhantomData);
1520
LL | let a = &t as &dyn Gettable<T>;
1621
| ^^ the trait `std::marker::Copy` is not implemented for `T`
1722
|
18-
= help: consider adding a `where T: std::marker::Copy` bound
1923
= note: required because of the requirements on the impl of `Gettable<T>` for `S<T>`
2024
= note: required for the cast to the object type `dyn Gettable<T>`
2125

2226
error[E0277]: `T` cannot be sent between threads safely
2327
--> $DIR/kindck-impl-type-params.rs:25:31
2428
|
29+
LL | fn g<T>(val: T) {
30+
| - help: consider restricting this bound: `T: std::marker::Send`
31+
LL | let t: S<T> = S(marker::PhantomData);
2532
LL | let a: &dyn Gettable<T> = &t;
2633
| ^^ `T` cannot be sent between threads safely
2734
|
2835
= help: the trait `std::marker::Send` is not implemented for `T`
29-
= help: consider adding a `where T: std::marker::Send` bound
3036
= note: required because of the requirements on the impl of `Gettable<T>` for `S<T>`
3137
= note: required for the cast to the object type `dyn Gettable<T>`
3238

3339
error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
3440
--> $DIR/kindck-impl-type-params.rs:25:31
3541
|
42+
LL | fn g<T>(val: T) {
43+
| - help: consider restricting this bound: `T: std::marker::Copy`
44+
LL | let t: S<T> = S(marker::PhantomData);
3645
LL | let a: &dyn Gettable<T> = &t;
3746
| ^^ the trait `std::marker::Copy` is not implemented for `T`
3847
|
39-
= help: consider adding a `where T: std::marker::Copy` bound
4048
= note: required because of the requirements on the impl of `Gettable<T>` for `S<T>`
4149
= note: required for the cast to the object type `dyn Gettable<T>`
4250

0 commit comments

Comments
 (0)