Skip to content

Commit

Permalink
Change E0369 diagnostic give note information for foreign items.
Browse files Browse the repository at this point in the history
Make it easy for developers to understand why the binop cannot be applied.

fixes rust-lang#125631
  • Loading branch information
surechen committed Jun 25, 2024
1 parent 4e63822 commit 2a6a423
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 26 deletions.
102 changes: 76 additions & 26 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2788,69 +2788,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
errors: Vec<FulfillmentError<'tcx>>,
suggest_derive: bool,
) {
let all_local_types_needing_impls =
errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
let preds: Vec<_> = errors
.iter()
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
match pred.self_ty().kind() {
ty::Adt(def, _) => def.did().is_local(),
_ => false,
ty::Adt(_, _) => Some(pred),
_ => None,
}
}
_ => false,
});
let mut preds: Vec<_> = errors
.iter()
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred),
_ => None,
})
.collect();
preds.sort_by_key(|pred| pred.trait_ref.to_string());
let def_ids = preds

// Note for local items and foreign items respectively.
let (mut local_preds, mut foreign_preds): (Vec<_>, Vec<_>) =
preds.iter().partition(|&pred| {
if let ty::Adt(def, _) = pred.self_ty().kind() {
def.did().is_local()
} else {
false
}
});

local_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
let local_def_ids = local_preds
.iter()
.filter_map(|pred| match pred.self_ty().kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
})
.collect::<FxIndexSet<_>>();
let mut spans: MultiSpan = def_ids
let mut local_spans: MultiSpan = local_def_ids
.iter()
.filter_map(|def_id| {
let span = self.tcx.def_span(*def_id);
if span.is_dummy() { None } else { Some(span) }
})
.collect::<Vec<_>>()
.into();

for pred in &preds {
for pred in &local_preds {
match pred.self_ty().kind() {
ty::Adt(def, _) if def.did().is_local() => {
spans.push_span_label(
ty::Adt(def, _) => {
local_spans.push_span_label(
self.tcx.def_span(def.did()),
format!("must implement `{}`", pred.trait_ref.print_trait_sugared()),
);
}
_ => {}
}
}

if all_local_types_needing_impls && spans.primary_span().is_some() {
let msg = if preds.len() == 1 {
if local_spans.primary_span().is_some() {
let msg = if local_preds.len() == 1 {
format!(
"an implementation of `{}` might be missing for `{}`",
preds[0].trait_ref.print_trait_sugared(),
preds[0].self_ty()
local_preds[0].trait_ref.print_trait_sugared(),
local_preds[0].self_ty()
)
} else {
format!(
"the following type{} would have to `impl` {} required trait{} for this \
operation to be valid",
pluralize!(def_ids.len()),
if def_ids.len() == 1 { "its" } else { "their" },
pluralize!(preds.len()),
pluralize!(local_def_ids.len()),
if local_def_ids.len() == 1 { "its" } else { "their" },
pluralize!(local_preds.len()),
)
};
err.span_note(local_spans, msg);
}

foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
let foreign_def_ids = foreign_preds
.iter()
.filter_map(|pred| match pred.self_ty().kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
})
.collect::<FxIndexSet<_>>();
let mut foreign_spans: MultiSpan = foreign_def_ids
.iter()
.filter_map(|def_id| {
let span = self.tcx.def_span(*def_id);
if span.is_dummy() { None } else { Some(span) }
})
.collect::<Vec<_>>()
.into();
for pred in &foreign_preds {
match pred.self_ty().kind() {
ty::Adt(def, _) => {
foreign_spans.push_span_label(
self.tcx.def_span(def.did()),
format!("not implement `{}`", pred.trait_ref.print_trait_sugared()),
);
}
_ => {}
}
}
if foreign_spans.primary_span().is_some() {
let msg = if foreign_preds.len() == 1 {
format!(
"the foreign item type `{}` doesn't implement `{}`",
foreign_preds[0].self_ty(),
foreign_preds[0].trait_ref.print_trait_sugared()
)
} else {
format!(
"the foreign item type{} {} implement required trait{} for this \
operation to be valid",
pluralize!(foreign_def_ids.len()),
if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" },
pluralize!(foreign_preds.len()),
)
};
err.span_note(spans, msg);
err.span_note(foreign_spans, msg);
}

let preds: Vec<_> = errors
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/array-slice-vec/vec-res-add.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ LL | let k = i + j;
| - ^ - Vec<R>
| |
| Vec<R>
|
note: the foreign item type `Vec<R>` doesn't implement `Add`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
= note: not implement `Add`

error: aborting due to 1 previous error

Expand Down
12 changes: 12 additions & 0 deletions tests/ui/autoderef-full-lval.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ LL | let z: isize = a.x + b.y;
| --- ^ --- Box<isize>
| |
| Box<isize>
|
note: the foreign item type `Box<isize>` doesn't implement `Add`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
|
= note: not implement `Add`

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

error: aborting due to 2 previous errors

Expand Down
16 changes: 16 additions & 0 deletions tests/ui/binop/binary-op-not-allowed-issue-125631.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::io::{Error, ErrorKind};
use std::thread;

struct T1;
struct T2;

fn main() {
(Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
//~^ERROR binary operation `==` cannot be applied to type
(Error::new(ErrorKind::Other, "2"), thread::current())
== (Error::new(ErrorKind::Other, "2"), thread::current());
//~^ERROR binary operation `==` cannot be applied to type
(Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
== (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
//~^ERROR binary operation `==` cannot be applied to type
}
75 changes: 75 additions & 0 deletions tests/ui/binop/binary-op-not-allowed-issue-125631.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, T1, {integer})`
--> $DIR/binary-op-not-allowed-issue-125631.rs:8:48
|
LL | (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
| ------------------------------------------ ^^ ------------------------------------------ (std::io::Error, T1, {integer})
| |
| (std::io::Error, T1, {integer})
|
note: an implementation of `PartialEq` might be missing for `T1`
--> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
|
LL | struct T1;
| ^^^^^^^^^ must implement `PartialEq`
note: the foreign item type `std::io::Error` doesn't implement `PartialEq`
--> $SRC_DIR/std/src/io/error.rs:LL:COL
|
= note: not implement `PartialEq`
help: consider annotating `T1` with `#[derive(PartialEq)]`
|
LL + #[derive(PartialEq)]
LL | struct T1;
|

error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread)`
--> $DIR/binary-op-not-allowed-issue-125631.rs:11:9
|
LL | (Error::new(ErrorKind::Other, "2"), thread::current())
| ------------------------------------------------------ (std::io::Error, Thread)
LL | == (Error::new(ErrorKind::Other, "2"), thread::current());
| ^^ ------------------------------------------------------ (std::io::Error, Thread)
|
note: the foreign item types don't implement required traits for this operation to be valid
--> $SRC_DIR/std/src/io/error.rs:LL:COL
|
= note: not implement `PartialEq`
--> $SRC_DIR/std/src/thread/mod.rs:LL:COL
|
= note: not implement `PartialEq`

error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread, T1, T2)`
--> $DIR/binary-op-not-allowed-issue-125631.rs:14:9
|
LL | (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
| -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
LL | == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
| ^^ -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
|
note: the following types would have to `impl` their required traits for this operation to be valid
--> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
|
LL | struct T1;
| ^^^^^^^^^ must implement `PartialEq`
LL | struct T2;
| ^^^^^^^^^ must implement `PartialEq`
note: the foreign item types don't implement required traits for this operation to be valid
--> $SRC_DIR/std/src/io/error.rs:LL:COL
|
= note: not implement `PartialEq`
--> $SRC_DIR/std/src/thread/mod.rs:LL:COL
|
= note: not implement `PartialEq`
help: consider annotating `T1` with `#[derive(PartialEq)]`
|
LL + #[derive(PartialEq)]
LL | struct T1;
|
help: consider annotating `T2` with `#[derive(PartialEq)]`
|
LL + #[derive(PartialEq)]
LL | struct T2;
|

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0369`.
5 changes: 5 additions & 0 deletions tests/ui/binop/binop-bitxor-str.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ LL | fn main() { let x = "a".to_string() ^ "b".to_string(); }
| --------------- ^ --------------- String
| |
| String
|
note: the foreign item type `String` doesn't implement `BitXor`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
|
= note: not implement `BitXor`

error: aborting due to 1 previous error

Expand Down
6 changes: 6 additions & 0 deletions tests/ui/error-codes/E0067.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ LL | LinkedList::new() += 1;
| -----------------^^^^^
| |
| cannot use `+=` on type `LinkedList<_>`
|
note: the foreign item type `LinkedList<_>` doesn't implement `AddAssign<{integer}>`
--> $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
::: $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
|
= note: not implement `AddAssign<{integer}>`

error[E0067]: invalid left-hand side of assignment
--> $DIR/E0067.rs:4:23
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/issues/issue-14915.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ LL | println!("{}", x + 1);
| - ^ - {integer}
| |
| Box<isize>
|
note: the foreign item type `Box<isize>` doesn't implement `Add<{integer}>`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
|
= note: not implement `Add<{integer}>`

error: aborting due to 1 previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/minus-string.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ error[E0600]: cannot apply unary operator `-` to type `String`
|
LL | fn main() { -"foo".to_string(); }
| ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-`
|
note: the foreign item type `String` doesn't implement `Neg`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
|
= note: not implement `Neg`

error: aborting due to 1 previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/pattern/pattern-tyvar-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3;
| - ^ - {integer}
| |
| Vec<isize>
|
note: the foreign item type `Vec<isize>` doesn't implement `Mul<{integer}>`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
|
= note: not implement `Mul<{integer}>`

error: aborting due to 1 previous error

Expand Down
8 changes: 8 additions & 0 deletions tests/ui/typeck/assign-non-lval-derefmut.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ LL | x.lock().unwrap() += 1;
| |
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
--> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
|
= note: not implement `AddAssign<{integer}>`
help: `+=` can be used on `usize` if you dereference the left-hand side
|
LL | *x.lock().unwrap() += 1;
Expand Down Expand Up @@ -47,6 +51,10 @@ LL | y += 1;
| |
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
--> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
|
= note: not implement `AddAssign<{integer}>`
help: `+=` can be used on `usize` if you dereference the left-hand side
|
LL | *y += 1;
Expand Down

0 comments on commit 2a6a423

Please sign in to comment.