Skip to content

Commit 79cfed4

Browse files
committed
Account for non-overlapping unmet trait bounds in suggestion
When a method not found on a type parameter could have been provided by any of multiple traits, suggest each trait individually, instead of a single suggestion to restrict the type parameter with *all* of them. Before: ``` error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied --> $DIR/method-on-unbounded-type-param.rs:5:10 | LL | (&a).cmp(&b) | ^^^ method cannot be called on `&T` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `T: Ord` which is required by `&T: Ord` `&T: Iterator` which is required by `&mut &T: Iterator` `T: Iterator` which is required by `&mut T: Iterator` help: consider restricting the type parameters to satisfy the trait bounds | LL | fn g<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord { | +++++++++++++++++++++++++ ``` After: ``` error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied --> $DIR/method-on-unbounded-type-param.rs:5:10 | LL | (&a).cmp(&b) | ^^^ method cannot be called on `&T` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `T: Ord` which is required by `&T: Ord` `&T: Iterator` which is required by `&mut &T: Iterator` `T: Iterator` which is required by `&mut T: Iterator` = help: items from traits can only be used if the type parameter is bounded by the trait help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them: | LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering { | +++++ LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering { | ++++++++++ ``` Fix #108428.
1 parent 11dbd30 commit 79cfed4

9 files changed

+62
-52
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+21-37
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
549549

550550
let mut bound_spans = vec![];
551551
let mut restrict_type_params = false;
552+
let mut suggested_derive = false;
552553
let mut unsatisfied_bounds = false;
553554
if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
554555
let msg = "consider using `len` instead";
@@ -932,20 +933,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
932933
.enumerate()
933934
.collect::<Vec<(usize, String)>>();
934935

935-
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
936-
restrict_type_params = true;
937-
// #74886: Sort here so that the output is always the same.
938-
let obligations = obligations.into_sorted_stable_ord();
939-
err.span_suggestion_verbose(
940-
span,
941-
format!(
942-
"consider restricting the type parameter{s} to satisfy the \
943-
trait bound{s}",
944-
s = pluralize!(obligations.len())
945-
),
946-
format!("{} {}", add_where_or_comma, obligations.join(", ")),
947-
Applicability::MaybeIncorrect,
948-
);
936+
if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) {
937+
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
938+
restrict_type_params = true;
939+
// #74886: Sort here so that the output is always the same.
940+
let obligations = obligations.into_sorted_stable_ord();
941+
err.span_suggestion_verbose(
942+
span,
943+
format!(
944+
"consider restricting the type parameter{s} to satisfy the trait \
945+
bound{s}",
946+
s = pluralize!(obligations.len())
947+
),
948+
format!("{} {}", add_where_or_comma, obligations.join(", ")),
949+
Applicability::MaybeIncorrect,
950+
);
951+
}
949952
}
950953

951954
bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
@@ -993,7 +996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
993996
"the following trait bounds were not satisfied:\n{bound_list}"
994997
));
995998
}
996-
self.suggest_derive(&mut err, unsatisfied_predicates);
999+
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);
9971000

9981001
unsatisfied_bounds = true;
9991002
}
@@ -1187,7 +1190,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11871190
err.span_label(span, msg);
11881191
}
11891192

1190-
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
1193+
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
11911194
} else {
11921195
self.suggest_traits_to_import(
11931196
&mut err,
@@ -1197,7 +1200,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11971200
args.map(|args| args.len() + 1),
11981201
source,
11991202
no_match_data.out_of_scope_traits.clone(),
1200-
unsatisfied_predicates,
12011203
static_candidates,
12021204
unsatisfied_bounds,
12031205
expected.only_has_type(self),
@@ -2447,7 +2449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24472449
Option<ty::Predicate<'tcx>>,
24482450
Option<ObligationCause<'tcx>>,
24492451
)],
2450-
) {
2452+
) -> bool {
24512453
let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
24522454
derives.sort();
24532455
derives.dedup();
@@ -2472,6 +2474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24722474
Applicability::MaybeIncorrect,
24732475
);
24742476
}
2477+
!derives_grouped.is_empty()
24752478
}
24762479

24772480
fn note_derefed_ty_has_method(
@@ -2674,11 +2677,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26742677
inputs_len: Option<usize>,
26752678
source: SelfSource<'tcx>,
26762679
valid_out_of_scope_traits: Vec<DefId>,
2677-
unsatisfied_predicates: &[(
2678-
ty::Predicate<'tcx>,
2679-
Option<ty::Predicate<'tcx>>,
2680-
Option<ObligationCause<'tcx>>,
2681-
)],
26822680
static_candidates: &[CandidateSource],
26832681
unsatisfied_bounds: bool,
26842682
return_type: Option<Ty<'tcx>>,
@@ -2943,20 +2941,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29432941
item.visibility(self.tcx).is_public() || info.def_id.is_local()
29442942
})
29452943
.is_some()
2946-
&& (matches!(rcvr_ty.kind(), ty::Param(_))
2947-
|| unsatisfied_predicates.iter().all(|(p, _, _)| {
2948-
match p.kind().skip_binder() {
2949-
// Hide traits if they are present in predicates as they can be fixed without
2950-
// having to implement them.
2951-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
2952-
t.def_id() == info.def_id
2953-
}
2954-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
2955-
p.projection_ty.def_id == info.def_id
2956-
}
2957-
_ => false,
2958-
}
2959-
}))
29602944
})
29612945
.collect::<Vec<_>>();
29622946
for span in &arbitrary_rcvr {

tests/ui/box/unit/unique-object-noncopyable.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ LL | let _z = y.clone();
1919
which is required by `Box<dyn Foo>: Clone`
2020
`dyn Foo: Clone`
2121
which is required by `Box<dyn Foo>: Clone`
22+
= help: items from traits can only be used if the trait is implemented and in scope
23+
= note: the following trait defines an item `clone`, perhaps you need to implement it:
24+
candidate #1: `Clone`
2225

2326
error: aborting due to 1 previous error
2427

tests/ui/box/unit/unique-pinned-nocopy.stderr

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ LL | let _j = i.clone();
1414
= note: the following trait bounds were not satisfied:
1515
`R: Clone`
1616
which is required by `Box<R>: Clone`
17-
= help: items from traits can only be used if the trait is implemented and in scope
18-
= note: the following trait defines an item `clone`, perhaps you need to implement it:
19-
candidate #1: `Clone`
2017
help: consider annotating `R` with `#[derive(Clone)]`
2118
|
2219
LL + #[derive(Clone)]

tests/ui/derives/derive-assoc-type-not-impl.stderr

-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ note: trait bound `NotClone: Clone` was not satisfied
1818
|
1919
LL | #[derive(Clone)]
2020
| ^^^^^ unsatisfied trait bound introduced in this `derive` macro
21-
= help: items from traits can only be used if the trait is implemented and in scope
22-
= note: the following trait defines an item `clone`, perhaps you need to implement it:
23-
candidate #1: `Clone`
2421
help: consider annotating `NotClone` with `#[derive(Clone)]`
2522
|
2623
LL + #[derive(Clone)]

tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait X {
55
type Y<T>;
66
}
77

8-
trait M {
8+
trait M { //~ NOTE
99
fn f(&self) {}
1010
}
1111

tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ LL | impl<T: X<Y<i32> = i32>> M for T {}
1818
| ^^^^^^^^^^^^ - -
1919
| |
2020
| unsatisfied trait bound introduced here
21+
= help: items from traits can only be used if the trait is implemented and in scope
22+
note: `M` defines an item `f`, perhaps you need to implement it
23+
--> $DIR/method-unsatisfied-assoc-type-predicate.rs:8:1
24+
|
25+
LL | trait M {
26+
| ^^^^^^^
2127

2228
error: aborting due to 1 previous error
2329

tests/ui/higher-ranked/trait-bounds/issue-30786.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ note: the following trait bounds were not satisfied:
1818
|
1919
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
2020
| --------- - ^^^^^^ unsatisfied trait bound introduced here
21+
= help: items from traits can only be used if the trait is implemented and in scope
22+
note: `StreamExt` defines an item `filterx`, perhaps you need to implement it
23+
--> $DIR/issue-30786.rs:66:1
24+
|
25+
LL | pub trait StreamExt
26+
| ^^^^^^^^^^^^^^^^^^^
2127

2228
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, fn(&u64) -> &u64 {identity::<u64>}>, {[email protected]:131:30}>`, but its trait bounds were not satisfied
2329
--> $DIR/issue-30786.rs:132:24
@@ -39,6 +45,12 @@ note: the following trait bounds were not satisfied:
3945
|
4046
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
4147
| --------- - ^^^^^^ unsatisfied trait bound introduced here
48+
= help: items from traits can only be used if the trait is implemented and in scope
49+
note: `StreamExt` defines an item `countx`, perhaps you need to implement it
50+
--> $DIR/issue-30786.rs:66:1
51+
|
52+
LL | pub trait StreamExt
53+
| ^^^^^^^^^^^^^^^^^^^
4254

4355
error: aborting due to 2 previous errors
4456

tests/ui/methods/method-call-err-msg.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ LL | | .take()
6666
note: the trait `Iterator` must be implemented
6767
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
6868
= help: items from traits can only be used if the trait is implemented and in scope
69-
= note: the following trait defines an item `take`, perhaps you need to implement it:
70-
candidate #1: `Iterator`
69+
= note: the following traits define an item `take`, perhaps you need to implement one of them:
70+
candidate #1: `std::io::Read`
71+
candidate #2: `Iterator`
7172

7273
error[E0061]: this method takes 3 arguments but 0 arguments were supplied
7374
--> $DIR/method-call-err-msg.rs:21:7

tests/ui/traits/method-on-unbounded-type-param.stderr

+16-6
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@ LL | (&a).cmp(&b)
2727
which is required by `&mut &T: Iterator`
2828
`T: Iterator`
2929
which is required by `&mut T: Iterator`
30-
help: consider restricting the type parameters to satisfy the trait bounds
30+
= help: items from traits can only be used if the type parameter is bounded by the trait
31+
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
3132
|
32-
LL | fn g<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
33-
| +++++++++++++++++++++++++
33+
LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
34+
| +++++
35+
LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
36+
| ++++++++++
3437

3538
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
3639
--> $DIR/method-on-unbounded-type-param.rs:8:7
@@ -45,10 +48,13 @@ LL | a.cmp(&b)
4548
which is required by `&mut &T: Iterator`
4649
`T: Iterator`
4750
which is required by `&mut T: Iterator`
48-
help: consider restricting the type parameters to satisfy the trait bounds
51+
= help: items from traits can only be used if the type parameter is bounded by the trait
52+
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
4953
|
50-
LL | fn h<T>(a: &T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
51-
| +++++++++++++++++++++++++
54+
LL | fn h<T: Ord>(a: &T, b: T) -> std::cmp::Ordering {
55+
| +++++
56+
LL | fn h<T: Iterator>(a: &T, b: T) -> std::cmp::Ordering {
57+
| ++++++++++
5258

5359
error[E0599]: the method `cmp` exists for struct `Box<dyn T>`, but its trait bounds were not satisfied
5460
--> $DIR/method-on-unbounded-type-param.rs:14:7
@@ -77,6 +83,10 @@ LL | x.cmp(&x);
7783
which is required by `&mut Box<dyn T>: Iterator`
7884
`dyn T: Iterator`
7985
which is required by `&mut dyn T: Iterator`
86+
= help: items from traits can only be used if the trait is implemented and in scope
87+
= note: the following traits define an item `cmp`, perhaps you need to implement one of them:
88+
candidate #1: `Ord`
89+
candidate #2: `Iterator`
8090

8191
error: aborting due to 4 previous errors
8292

0 commit comments

Comments
 (0)