Skip to content

Commit 2a6a423

Browse files
committed
Change E0369 diagnostic give note information for foreign items.
Make it easy for developers to understand why the binop cannot be applied. fixes #125631
1 parent 4e63822 commit 2a6a423

11 files changed

+219
-26
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+76-26
Original file line numberDiff line numberDiff line change
@@ -2788,69 +2788,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27882788
errors: Vec<FulfillmentError<'tcx>>,
27892789
suggest_derive: bool,
27902790
) {
2791-
let all_local_types_needing_impls =
2792-
errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
2791+
let preds: Vec<_> = errors
2792+
.iter()
2793+
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
27932794
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
27942795
match pred.self_ty().kind() {
2795-
ty::Adt(def, _) => def.did().is_local(),
2796-
_ => false,
2796+
ty::Adt(_, _) => Some(pred),
2797+
_ => None,
27972798
}
27982799
}
2799-
_ => false,
2800-
});
2801-
let mut preds: Vec<_> = errors
2802-
.iter()
2803-
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
2804-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred),
28052800
_ => None,
28062801
})
28072802
.collect();
2808-
preds.sort_by_key(|pred| pred.trait_ref.to_string());
2809-
let def_ids = preds
2803+
2804+
// Note for local items and foreign items respectively.
2805+
let (mut local_preds, mut foreign_preds): (Vec<_>, Vec<_>) =
2806+
preds.iter().partition(|&pred| {
2807+
if let ty::Adt(def, _) = pred.self_ty().kind() {
2808+
def.did().is_local()
2809+
} else {
2810+
false
2811+
}
2812+
});
2813+
2814+
local_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
2815+
let local_def_ids = local_preds
28102816
.iter()
28112817
.filter_map(|pred| match pred.self_ty().kind() {
28122818
ty::Adt(def, _) => Some(def.did()),
28132819
_ => None,
28142820
})
28152821
.collect::<FxIndexSet<_>>();
2816-
let mut spans: MultiSpan = def_ids
2822+
let mut local_spans: MultiSpan = local_def_ids
28172823
.iter()
28182824
.filter_map(|def_id| {
28192825
let span = self.tcx.def_span(*def_id);
28202826
if span.is_dummy() { None } else { Some(span) }
28212827
})
28222828
.collect::<Vec<_>>()
28232829
.into();
2824-
2825-
for pred in &preds {
2830+
for pred in &local_preds {
28262831
match pred.self_ty().kind() {
2827-
ty::Adt(def, _) if def.did().is_local() => {
2828-
spans.push_span_label(
2832+
ty::Adt(def, _) => {
2833+
local_spans.push_span_label(
28292834
self.tcx.def_span(def.did()),
28302835
format!("must implement `{}`", pred.trait_ref.print_trait_sugared()),
28312836
);
28322837
}
28332838
_ => {}
28342839
}
28352840
}
2836-
2837-
if all_local_types_needing_impls && spans.primary_span().is_some() {
2838-
let msg = if preds.len() == 1 {
2841+
if local_spans.primary_span().is_some() {
2842+
let msg = if local_preds.len() == 1 {
28392843
format!(
28402844
"an implementation of `{}` might be missing for `{}`",
2841-
preds[0].trait_ref.print_trait_sugared(),
2842-
preds[0].self_ty()
2845+
local_preds[0].trait_ref.print_trait_sugared(),
2846+
local_preds[0].self_ty()
28432847
)
28442848
} else {
28452849
format!(
28462850
"the following type{} would have to `impl` {} required trait{} for this \
28472851
operation to be valid",
2848-
pluralize!(def_ids.len()),
2849-
if def_ids.len() == 1 { "its" } else { "their" },
2850-
pluralize!(preds.len()),
2852+
pluralize!(local_def_ids.len()),
2853+
if local_def_ids.len() == 1 { "its" } else { "their" },
2854+
pluralize!(local_preds.len()),
2855+
)
2856+
};
2857+
err.span_note(local_spans, msg);
2858+
}
2859+
2860+
foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
2861+
let foreign_def_ids = foreign_preds
2862+
.iter()
2863+
.filter_map(|pred| match pred.self_ty().kind() {
2864+
ty::Adt(def, _) => Some(def.did()),
2865+
_ => None,
2866+
})
2867+
.collect::<FxIndexSet<_>>();
2868+
let mut foreign_spans: MultiSpan = foreign_def_ids
2869+
.iter()
2870+
.filter_map(|def_id| {
2871+
let span = self.tcx.def_span(*def_id);
2872+
if span.is_dummy() { None } else { Some(span) }
2873+
})
2874+
.collect::<Vec<_>>()
2875+
.into();
2876+
for pred in &foreign_preds {
2877+
match pred.self_ty().kind() {
2878+
ty::Adt(def, _) => {
2879+
foreign_spans.push_span_label(
2880+
self.tcx.def_span(def.did()),
2881+
format!("not implement `{}`", pred.trait_ref.print_trait_sugared()),
2882+
);
2883+
}
2884+
_ => {}
2885+
}
2886+
}
2887+
if foreign_spans.primary_span().is_some() {
2888+
let msg = if foreign_preds.len() == 1 {
2889+
format!(
2890+
"the foreign item type `{}` doesn't implement `{}`",
2891+
foreign_preds[0].self_ty(),
2892+
foreign_preds[0].trait_ref.print_trait_sugared()
2893+
)
2894+
} else {
2895+
format!(
2896+
"the foreign item type{} {} implement required trait{} for this \
2897+
operation to be valid",
2898+
pluralize!(foreign_def_ids.len()),
2899+
if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" },
2900+
pluralize!(foreign_preds.len()),
28512901
)
28522902
};
2853-
err.span_note(spans, msg);
2903+
err.span_note(foreign_spans, msg);
28542904
}
28552905

28562906
let preds: Vec<_> = errors

tests/ui/array-slice-vec/vec-res-add.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ LL | let k = i + j;
55
| - ^ - Vec<R>
66
| |
77
| Vec<R>
8+
|
9+
note: the foreign item type `Vec<R>` doesn't implement `Add`
10+
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
11+
|
12+
= note: not implement `Add`
813

914
error: aborting due to 1 previous error
1015

tests/ui/autoderef-full-lval.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | let z: isize = a.x + b.y;
55
| --- ^ --- Box<isize>
66
| |
77
| Box<isize>
8+
|
9+
note: the foreign item type `Box<isize>` doesn't implement `Add`
10+
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
11+
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
12+
|
13+
= note: not implement `Add`
814

915
error[E0369]: cannot add `Box<isize>` to `Box<isize>`
1016
--> $DIR/autoderef-full-lval.rs:21:33
@@ -13,6 +19,12 @@ LL | let answer: isize = forty.a + two.a;
1319
| ------- ^ ----- Box<isize>
1420
| |
1521
| Box<isize>
22+
|
23+
note: the foreign item type `Box<isize>` doesn't implement `Add`
24+
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
25+
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
26+
|
27+
= note: not implement `Add`
1628

1729
error: aborting due to 2 previous errors
1830

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use std::io::{Error, ErrorKind};
2+
use std::thread;
3+
4+
struct T1;
5+
struct T2;
6+
7+
fn main() {
8+
(Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
9+
//~^ERROR binary operation `==` cannot be applied to type
10+
(Error::new(ErrorKind::Other, "2"), thread::current())
11+
== (Error::new(ErrorKind::Other, "2"), thread::current());
12+
//~^ERROR binary operation `==` cannot be applied to type
13+
(Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
14+
== (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
15+
//~^ERROR binary operation `==` cannot be applied to type
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, T1, {integer})`
2+
--> $DIR/binary-op-not-allowed-issue-125631.rs:8:48
3+
|
4+
LL | (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
5+
| ------------------------------------------ ^^ ------------------------------------------ (std::io::Error, T1, {integer})
6+
| |
7+
| (std::io::Error, T1, {integer})
8+
|
9+
note: an implementation of `PartialEq` might be missing for `T1`
10+
--> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
11+
|
12+
LL | struct T1;
13+
| ^^^^^^^^^ must implement `PartialEq`
14+
note: the foreign item type `std::io::Error` doesn't implement `PartialEq`
15+
--> $SRC_DIR/std/src/io/error.rs:LL:COL
16+
|
17+
= note: not implement `PartialEq`
18+
help: consider annotating `T1` with `#[derive(PartialEq)]`
19+
|
20+
LL + #[derive(PartialEq)]
21+
LL | struct T1;
22+
|
23+
24+
error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread)`
25+
--> $DIR/binary-op-not-allowed-issue-125631.rs:11:9
26+
|
27+
LL | (Error::new(ErrorKind::Other, "2"), thread::current())
28+
| ------------------------------------------------------ (std::io::Error, Thread)
29+
LL | == (Error::new(ErrorKind::Other, "2"), thread::current());
30+
| ^^ ------------------------------------------------------ (std::io::Error, Thread)
31+
|
32+
note: the foreign item types don't implement required traits for this operation to be valid
33+
--> $SRC_DIR/std/src/io/error.rs:LL:COL
34+
|
35+
= note: not implement `PartialEq`
36+
--> $SRC_DIR/std/src/thread/mod.rs:LL:COL
37+
|
38+
= note: not implement `PartialEq`
39+
40+
error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread, T1, T2)`
41+
--> $DIR/binary-op-not-allowed-issue-125631.rs:14:9
42+
|
43+
LL | (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
44+
| -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
45+
LL | == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
46+
| ^^ -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
47+
|
48+
note: the following types would have to `impl` their required traits for this operation to be valid
49+
--> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
50+
|
51+
LL | struct T1;
52+
| ^^^^^^^^^ must implement `PartialEq`
53+
LL | struct T2;
54+
| ^^^^^^^^^ must implement `PartialEq`
55+
note: the foreign item types don't implement required traits for this operation to be valid
56+
--> $SRC_DIR/std/src/io/error.rs:LL:COL
57+
|
58+
= note: not implement `PartialEq`
59+
--> $SRC_DIR/std/src/thread/mod.rs:LL:COL
60+
|
61+
= note: not implement `PartialEq`
62+
help: consider annotating `T1` with `#[derive(PartialEq)]`
63+
|
64+
LL + #[derive(PartialEq)]
65+
LL | struct T1;
66+
|
67+
help: consider annotating `T2` with `#[derive(PartialEq)]`
68+
|
69+
LL + #[derive(PartialEq)]
70+
LL | struct T2;
71+
|
72+
73+
error: aborting due to 3 previous errors
74+
75+
For more information about this error, try `rustc --explain E0369`.

tests/ui/binop/binop-bitxor-str.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ LL | fn main() { let x = "a".to_string() ^ "b".to_string(); }
55
| --------------- ^ --------------- String
66
| |
77
| String
8+
|
9+
note: the foreign item type `String` doesn't implement `BitXor`
10+
--> $SRC_DIR/alloc/src/string.rs:LL:COL
11+
|
12+
= note: not implement `BitXor`
813

914
error: aborting due to 1 previous error
1015

tests/ui/error-codes/E0067.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | LinkedList::new() += 1;
55
| -----------------^^^^^
66
| |
77
| cannot use `+=` on type `LinkedList<_>`
8+
|
9+
note: the foreign item type `LinkedList<_>` doesn't implement `AddAssign<{integer}>`
10+
--> $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
11+
::: $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
12+
|
13+
= note: not implement `AddAssign<{integer}>`
814

915
error[E0067]: invalid left-hand side of assignment
1016
--> $DIR/E0067.rs:4:23

tests/ui/issues/issue-14915.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | println!("{}", x + 1);
55
| - ^ - {integer}
66
| |
77
| Box<isize>
8+
|
9+
note: the foreign item type `Box<isize>` doesn't implement `Add<{integer}>`
10+
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
11+
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
12+
|
13+
= note: not implement `Add<{integer}>`
814

915
error: aborting due to 1 previous error
1016

tests/ui/minus-string.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0600]: cannot apply unary operator `-` to type `String`
33
|
44
LL | fn main() { -"foo".to_string(); }
55
| ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-`
6+
|
7+
note: the foreign item type `String` doesn't implement `Neg`
8+
--> $SRC_DIR/alloc/src/string.rs:LL:COL
9+
|
10+
= note: not implement `Neg`
611

712
error: aborting due to 1 previous error
813

tests/ui/pattern/pattern-tyvar-2.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3;
55
| - ^ - {integer}
66
| |
77
| Vec<isize>
8+
|
9+
note: the foreign item type `Vec<isize>` doesn't implement `Mul<{integer}>`
10+
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
11+
|
12+
= note: not implement `Mul<{integer}>`
813

914
error: aborting due to 1 previous error
1015

tests/ui/typeck/assign-non-lval-derefmut.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ LL | x.lock().unwrap() += 1;
1919
| |
2020
| cannot use `+=` on type `MutexGuard<'_, usize>`
2121
|
22+
note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
23+
--> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
24+
|
25+
= note: not implement `AddAssign<{integer}>`
2226
help: `+=` can be used on `usize` if you dereference the left-hand side
2327
|
2428
LL | *x.lock().unwrap() += 1;
@@ -47,6 +51,10 @@ LL | y += 1;
4751
| |
4852
| cannot use `+=` on type `MutexGuard<'_, usize>`
4953
|
54+
note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
55+
--> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
56+
|
57+
= note: not implement `AddAssign<{integer}>`
5058
help: `+=` can be used on `usize` if you dereference the left-hand side
5159
|
5260
LL | *y += 1;

0 commit comments

Comments
 (0)