Skip to content

Commit ef8ac78

Browse files
authored
Rollup merge of rust-lang#65192 - estebank:restrict-bound, r=matthewjasper
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)`. Fix rust-lang#64565, fix rust-lang#41817, fix rust-lang#24354, cc rust-lang#26026, cc rust-lang#37808, cc rust-lang#24159, fix rust-lang#37138, fix rust-lang#24354, cc rust-lang#20671.
2 parents e5b8c11 + c6dce78 commit ef8ac78

File tree

102 files changed

+847
-243
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+847
-243
lines changed

src/librustc/hir/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,12 @@ impl WhereClause {
669669
Some(self.span)
670670
}
671671
}
672+
673+
/// The `WhereClause` under normal circumstances points at either the predicates or the empty
674+
/// space where the `where` clause should be. Only of use for diagnostic suggestions.
675+
pub fn span_for_predicates_or_empty_place(&self) -> Span {
676+
self.span
677+
}
672678
}
673679

674680
/// A single predicate in a where-clause.

src/librustc/traits/error_reporting.rs

+174-2
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,175 @@ 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 self_ty = trait_ref.self_ty();
973+
let (param_ty, projection) = match &self_ty.kind {
974+
ty::Param(_) => (true, None),
975+
ty::Projection(projection) => (false, Some(projection)),
976+
_ => return,
977+
};
978+
979+
let mut suggest_restriction = |generics: &hir::Generics, msg| {
980+
let span = generics.where_clause.span_for_predicates_or_empty_place();
981+
if !span.from_expansion() && span.desugaring_kind().is_none() {
982+
err.span_suggestion(
983+
generics.where_clause.span_for_predicates_or_empty_place().shrink_to_hi(),
984+
&format!("consider further restricting {}", msg),
985+
format!(
986+
"{} {} ",
987+
if !generics.where_clause.predicates.is_empty() {
988+
","
989+
} else {
990+
" where"
991+
},
992+
trait_ref.to_predicate(),
993+
),
994+
Applicability::MachineApplicable,
995+
);
996+
}
997+
};
998+
999+
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
1000+
// don't suggest `T: Sized + ?Sized`.
1001+
let mut hir_id = body_id;
1002+
while let Some(node) = self.tcx.hir().find(hir_id) {
1003+
match node {
1004+
hir::Node::TraitItem(hir::TraitItem {
1005+
generics,
1006+
kind: hir::TraitItemKind::Method(..), ..
1007+
}) if param_ty && self_ty == self.tcx.types.self_param => {
1008+
// Restricting `Self` for a single method.
1009+
suggest_restriction(&generics, "`Self`");
1010+
return;
1011+
}
1012+
1013+
hir::Node::Item(hir::Item {
1014+
kind: hir::ItemKind::Fn(_, _, generics, _), ..
1015+
}) |
1016+
hir::Node::TraitItem(hir::TraitItem {
1017+
generics,
1018+
kind: hir::TraitItemKind::Method(..), ..
1019+
}) |
1020+
hir::Node::ImplItem(hir::ImplItem {
1021+
generics,
1022+
kind: hir::ImplItemKind::Method(..), ..
1023+
}) |
1024+
hir::Node::Item(hir::Item {
1025+
kind: hir::ItemKind::Trait(_, _, generics, _, _), ..
1026+
}) |
1027+
hir::Node::Item(hir::Item {
1028+
kind: hir::ItemKind::Impl(_, _, _, generics, ..), ..
1029+
}) if projection.is_some() => {
1030+
// Missing associated type bound.
1031+
suggest_restriction(&generics, "the associated type");
1032+
return;
1033+
}
1034+
1035+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(_, generics), span, .. }) |
1036+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics), span, .. }) |
1037+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(_, generics), span, .. }) |
1038+
hir::Node::Item(hir::Item {
1039+
kind: hir::ItemKind::Trait(_, _, generics, ..), span, ..
1040+
}) |
1041+
hir::Node::Item(hir::Item {
1042+
kind: hir::ItemKind::Impl(_, _, _, generics, ..), span, ..
1043+
}) |
1044+
hir::Node::Item(hir::Item {
1045+
kind: hir::ItemKind::Fn(_, _, generics, _), span, ..
1046+
}) |
1047+
hir::Node::Item(hir::Item {
1048+
kind: hir::ItemKind::TyAlias(_, generics), span, ..
1049+
}) |
1050+
hir::Node::Item(hir::Item {
1051+
kind: hir::ItemKind::TraitAlias(generics, _), span, ..
1052+
}) |
1053+
hir::Node::Item(hir::Item {
1054+
kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), span, ..
1055+
}) |
1056+
hir::Node::TraitItem(hir::TraitItem { generics, span, .. }) |
1057+
hir::Node::ImplItem(hir::ImplItem { generics, span, .. })
1058+
if param_ty => {
1059+
// Missing generic type parameter bound.
1060+
let restrict_msg = "consider further restricting this bound";
1061+
let param_name = self_ty.to_string();
1062+
for param in generics.params.iter().filter(|p| {
1063+
&param_name == std::convert::AsRef::<str>::as_ref(&p.name.ident().as_str())
1064+
}) {
1065+
if param_name.starts_with("impl ") {
1066+
// `impl Trait` in argument:
1067+
// `fn foo(x: impl Trait) {}` → `fn foo(t: impl Trait + Trait2) {}`
1068+
err.span_suggestion(
1069+
param.span,
1070+
restrict_msg,
1071+
// `impl CurrentTrait + MissingTrait`
1072+
format!("{} + {}", param.name.ident(), trait_ref),
1073+
Applicability::MachineApplicable,
1074+
);
1075+
} else if generics.where_clause.predicates.is_empty() &&
1076+
param.bounds.is_empty()
1077+
{
1078+
// If there are no bounds whatsoever, suggest adding a constraint
1079+
// to the type parameter:
1080+
// `fn foo<T>(t: T) {}` → `fn foo<T: Trait>(t: T) {}`
1081+
err.span_suggestion(
1082+
param.span,
1083+
"consider restricting this bound",
1084+
format!("{}", trait_ref.to_predicate()),
1085+
Applicability::MachineApplicable,
1086+
);
1087+
} else if !generics.where_clause.predicates.is_empty() {
1088+
// There is a `where` clause, so suggest expanding it:
1089+
// `fn foo<T>(t: T) where T: Debug {}` →
1090+
// `fn foo<T>(t: T) where T: Debug, T: Trait {}`
1091+
err.span_suggestion(
1092+
generics.where_clause.span().unwrap().shrink_to_hi(),
1093+
&format!(
1094+
"consider further restricting type parameter `{}`",
1095+
param_name,
1096+
),
1097+
format!(", {}", trait_ref.to_predicate()),
1098+
Applicability::MachineApplicable,
1099+
);
1100+
} else {
1101+
// If there is no `where` clause lean towards constraining to the
1102+
// type parameter:
1103+
// `fn foo<X: Bar, T>(t: T, x: X) {}` → `fn foo<T: Trait>(t: T) {}`
1104+
// `fn foo<T: Bar>(t: T) {}` → `fn foo<T: Bar + Trait>(t: T) {}`
1105+
let sp = param.span.with_hi(span.hi());
1106+
let span = self.tcx.sess.source_map()
1107+
.span_through_char(sp, ':');
1108+
if sp != param.span && sp != span {
1109+
// Only suggest if we have high certainty that the span
1110+
// covers the colon in `foo<T: Trait>`.
1111+
err.span_suggestion(span, restrict_msg, format!(
1112+
"{} + ",
1113+
trait_ref.to_predicate(),
1114+
), Applicability::MachineApplicable);
1115+
} else {
1116+
err.span_label(param.span, &format!(
1117+
"consider adding a `where {}` bound",
1118+
trait_ref.to_predicate(),
1119+
));
1120+
}
1121+
}
1122+
return;
1123+
}
1124+
}
1125+
1126+
hir::Node::Crate => return,
1127+
1128+
_ => {}
1129+
}
1130+
1131+
hir_id = self.tcx.hir().get_parent_item(hir_id);
1132+
}
1133+
}
1134+
9631135
/// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a
9641136
/// suggestion to borrow the initializer in order to use have a slice instead.
9651137
fn suggest_borrow_on_unsized_slice(

src/librustc_typeck/check/demand.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
350350

351351
// If the span is from a macro, then it's hard to extract the text
352352
// and make a good suggestion, so don't bother.
353-
let is_desugaring = match sp.desugaring_kind() {
354-
Some(k) => sp.is_desugaring(k),
355-
None => false
356-
};
357-
let is_macro = sp.from_expansion() && !is_desugaring;
353+
let is_macro = sp.from_expansion() && sp.desugaring_kind().is_none();
358354

359355
// `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
360356
let expr = expr.peel_drop_temps();

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

+2-2
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

+2-2
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-type-bounds/bad-bounds-on-assoc-in-trait.stderr

+12-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ LL | impl Case1 for S1 {
99
error[E0277]: `<<T as Case1>::C as std::iter::Iterator>::Item` is not an iterator
1010
--> $DIR/bad-bounds-on-assoc-in-trait.rs:37:1
1111
|
12-
LL | / fn assume_case1<T: Case1>() {
12+
LL | fn assume_case1<T: Case1>() {
13+
| ^ - help: consider further restricting the associated type: `where <<T as Case1>::C as std::iter::Iterator>::Item: std::iter::Iterator`
14+
| _|
15+
| |
1316
LL | |
1417
LL | |
1518
LL | |
@@ -19,15 +22,17 @@ LL | | }
1922
| |_^ `<<T as Case1>::C as std::iter::Iterator>::Item` is not an iterator
2023
|
2124
= help: the trait `std::iter::Iterator` is not implemented for `<<T as Case1>::C as std::iter::Iterator>::Item`
22-
= help: consider adding a `where <<T as Case1>::C as std::iter::Iterator>::Item: std::iter::Iterator` bound
2325

2426
error[E0277]: `<<T as Case1>::C as std::iter::Iterator>::Item` cannot be sent between threads safely
2527
--> $DIR/bad-bounds-on-assoc-in-trait.rs:37:1
2628
|
2729
LL | trait Case1 {
2830
| ----------- required by `Case1`
2931
...
30-
LL | / fn assume_case1<T: Case1>() {
32+
LL | fn assume_case1<T: Case1>() {
33+
| ^ - help: consider further restricting the associated type: `where <<T as Case1>::C as std::iter::Iterator>::Item: std::marker::Send`
34+
| _|
35+
| |
3136
LL | |
3237
LL | |
3338
LL | |
@@ -37,15 +42,17 @@ LL | | }
3742
| |_^ `<<T as Case1>::C as std::iter::Iterator>::Item` cannot be sent between threads safely
3843
|
3944
= help: the trait `std::marker::Send` is not implemented for `<<T as Case1>::C as std::iter::Iterator>::Item`
40-
= help: consider adding a `where <<T as Case1>::C as std::iter::Iterator>::Item: std::marker::Send` bound
4145

4246
error[E0277]: `<<T as Case1>::C as std::iter::Iterator>::Item` cannot be shared between threads safely
4347
--> $DIR/bad-bounds-on-assoc-in-trait.rs:37:1
4448
|
4549
LL | trait Case1 {
4650
| ----------- required by `Case1`
4751
...
48-
LL | / fn assume_case1<T: Case1>() {
52+
LL | fn assume_case1<T: Case1>() {
53+
| ^ - help: consider further restricting the associated type: `where <<T as Case1>::C as std::iter::Iterator>::Item: std::marker::Sync`
54+
| _|
55+
| |
4956
LL | |
5057
LL | |
5158
LL | |
@@ -55,7 +62,6 @@ LL | | }
5562
| |_^ `<<T as Case1>::C as std::iter::Iterator>::Item` cannot be shared between threads safely
5663
|
5764
= help: the trait `std::marker::Sync` is not implemented for `<<T as Case1>::C as std::iter::Iterator>::Item`
58-
= help: consider adding a `where <<T as Case1>::C as std::iter::Iterator>::Item: std::marker::Sync` bound
5965

6066
error[E0277]: `<_ as Lam<&'a u8>>::App` doesn't implement `std::fmt::Debug`
6167
--> $DIR/bad-bounds-on-assoc-in-trait.rs:37:1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run-rustfix
2+
// Test equality constraints on associated types in a where clause.
3+
#![allow(dead_code)]
4+
5+
pub trait ToInt {
6+
fn to_int(&self) -> isize;
7+
}
8+
9+
pub trait GetToInt
10+
{
11+
type R;
12+
13+
fn get(&self) -> <Self as GetToInt>::R;
14+
}
15+
16+
fn foo<G>(g: G) -> isize
17+
where G : GetToInt, <G as GetToInt>::R: ToInt
18+
{
19+
ToInt::to_int(&g.get()) //~ ERROR E0277
20+
}
21+
22+
fn bar<G : GetToInt>(g: G) -> isize
23+
where G::R : ToInt
24+
{
25+
ToInt::to_int(&g.get()) // OK
26+
}
27+
28+
pub fn main() {
29+
}

src/test/ui/associated-types/associated-types-bound-failure.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
// run-rustfix
12
// Test equality constraints on associated types in a where clause.
3+
#![allow(dead_code)]
24

35
pub trait ToInt {
46
fn to_int(&self) -> isize;

src/test/ui/associated-types/associated-types-bound-failure.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
error[E0277]: the trait bound `<G as GetToInt>::R: ToInt` is not satisfied
2-
--> $DIR/associated-types-bound-failure.rs:17:19
2+
--> $DIR/associated-types-bound-failure.rs:19:19
33
|
44
LL | fn to_int(&self) -> isize;
55
| -------------------------- required by `ToInt::to_int`
66
...
7+
LL | where G : GetToInt
8+
| - help: consider further restricting the associated type: `, <G as GetToInt>::R: ToInt`
9+
LL | {
710
LL | ToInt::to_int(&g.get())
811
| ^^^^^^^^ the trait `ToInt` is not implemented for `<G as GetToInt>::R`
9-
|
10-
= help: consider adding a `where <G as GetToInt>::R: ToInt` bound
1112

1213
error: aborting due to previous error
1314

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-rustfix
2+
#![allow(unused_variables)]
3+
4+
trait Get {
5+
type Value;
6+
fn get(&self) -> <Self as Get>::Value;
7+
}
8+
9+
trait Other {
10+
fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Get {}
11+
//~^ ERROR the trait bound `Self: Get` is not satisfied
12+
}
13+
14+
fn main() {
15+
}

src/test/ui/associated-types/associated-types-for-unimpl-trait.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// run-rustfix
2+
#![allow(unused_variables)]
3+
14
trait Get {
25
type Value;
36
fn get(&self) -> <Self as Get>::Value;

src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
error[E0277]: the trait bound `Self: Get` is not satisfied
2-
--> $DIR/associated-types-for-unimpl-trait.rs:7:5
2+
--> $DIR/associated-types-for-unimpl-trait.rs:10:5
33
|
44
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
6-
|
7-
= help: consider adding a `where Self: Get` bound
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^
6+
| | |
7+
| | help: consider further restricting `Self`: `where Self: Get`
8+
| the trait `Get` is not implemented for `Self`
89

910
error: aborting due to previous error
1011

0 commit comments

Comments
 (0)