Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

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 rust-lang#125631
1 parent 4e63822 commit 2a6a423

File tree

11 files changed

+219
-26
lines changed

11 files changed

+219
-26
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 76 additions & 26 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 16 additions & 0 deletions
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+
}
Lines changed: 75 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 8 additions & 0 deletions
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)