From 2a6a42329f79d3c2a42f23ca9fb68aba511a2bc1 Mon Sep 17 00:00:00 2001 From: surechen Date: Tue, 25 Jun 2024 10:00:30 +0800 Subject: [PATCH] Change E0369 diagnostic give note information for foreign items. Make it easy for developers to understand why the binop cannot be applied. fixes #125631 --- .../rustc_hir_typeck/src/method/suggest.rs | 102 +++++++++++++----- tests/ui/array-slice-vec/vec-res-add.stderr | 5 + tests/ui/autoderef-full-lval.stderr | 12 +++ .../binary-op-not-allowed-issue-125631.rs | 16 +++ .../binary-op-not-allowed-issue-125631.stderr | 75 +++++++++++++ tests/ui/binop/binop-bitxor-str.stderr | 5 + tests/ui/error-codes/E0067.stderr | 6 ++ tests/ui/issues/issue-14915.stderr | 6 ++ tests/ui/minus-string.stderr | 5 + tests/ui/pattern/pattern-tyvar-2.stderr | 5 + .../ui/typeck/assign-non-lval-derefmut.stderr | 8 ++ 11 files changed, 219 insertions(+), 26 deletions(-) create mode 100644 tests/ui/binop/binary-op-not-allowed-issue-125631.rs create mode 100644 tests/ui/binop/binary-op-not-allowed-issue-125631.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6a44d2afc15ca..ff066bb9edeb9 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2788,32 +2788,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors: Vec>, 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::>(); - 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); @@ -2821,11 +2827,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect::>() .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()), ); @@ -2833,24 +2838,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } } - - 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::>(); + 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::>() + .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 diff --git a/tests/ui/array-slice-vec/vec-res-add.stderr b/tests/ui/array-slice-vec/vec-res-add.stderr index cf5796f7e4a4a..34fd69426a8bb 100644 --- a/tests/ui/array-slice-vec/vec-res-add.stderr +++ b/tests/ui/array-slice-vec/vec-res-add.stderr @@ -5,6 +5,11 @@ LL | let k = i + j; | - ^ - Vec | | | Vec + | +note: the foreign item type `Vec` doesn't implement `Add` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: not implement `Add` error: aborting due to 1 previous error diff --git a/tests/ui/autoderef-full-lval.stderr b/tests/ui/autoderef-full-lval.stderr index 9921ce7c15440..d90238a7fb210 100644 --- a/tests/ui/autoderef-full-lval.stderr +++ b/tests/ui/autoderef-full-lval.stderr @@ -5,6 +5,12 @@ LL | let z: isize = a.x + b.y; | --- ^ --- Box | | | Box + | +note: the foreign item type `Box` 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` to `Box` --> $DIR/autoderef-full-lval.rs:21:33 @@ -13,6 +19,12 @@ LL | let answer: isize = forty.a + two.a; | ------- ^ ----- Box | | | Box + | +note: the foreign item type `Box` 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 diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.rs b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs new file mode 100644 index 0000000000000..8827bbb003b14 --- /dev/null +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs @@ -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 +} diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr new file mode 100644 index 0000000000000..1cf75bbc1a570 --- /dev/null +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -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`. diff --git a/tests/ui/binop/binop-bitxor-str.stderr b/tests/ui/binop/binop-bitxor-str.stderr index 20b1ecc5a939e..9d9ec6c5af6be 100644 --- a/tests/ui/binop/binop-bitxor-str.stderr +++ b/tests/ui/binop/binop-bitxor-str.stderr @@ -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 diff --git a/tests/ui/error-codes/E0067.stderr b/tests/ui/error-codes/E0067.stderr index ec0358cb7dfe6..71b7208054423 100644 --- a/tests/ui/error-codes/E0067.stderr +++ b/tests/ui/error-codes/E0067.stderr @@ -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 diff --git a/tests/ui/issues/issue-14915.stderr b/tests/ui/issues/issue-14915.stderr index 279f5772d21d3..3558bd651c62a 100644 --- a/tests/ui/issues/issue-14915.stderr +++ b/tests/ui/issues/issue-14915.stderr @@ -5,6 +5,12 @@ LL | println!("{}", x + 1); | - ^ - {integer} | | | Box + | +note: the foreign item type `Box` 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 diff --git a/tests/ui/minus-string.stderr b/tests/ui/minus-string.stderr index 105274ee7d0e8..cf63ec2441670 100644 --- a/tests/ui/minus-string.stderr +++ b/tests/ui/minus-string.stderr @@ -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 diff --git a/tests/ui/pattern/pattern-tyvar-2.stderr b/tests/ui/pattern/pattern-tyvar-2.stderr index c6540e795589f..be52fa8b23943 100644 --- a/tests/ui/pattern/pattern-tyvar-2.stderr +++ b/tests/ui/pattern/pattern-tyvar-2.stderr @@ -5,6 +5,11 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; | - ^ - {integer} | | | Vec + | +note: the foreign item type `Vec` 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 diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr index b26d16da01521..ce0ff1d957b53 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.stderr +++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr @@ -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; @@ -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;