about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsurechen <chenshuo17@huawei.com>2024-06-25 10:00:30 +0800
committersurechen <chenshuo17@huawei.com>2024-06-25 10:00:30 +0800
commit2a6a42329f79d3c2a42f23ca9fb68aba511a2bc1 (patch)
treebbb5056488b24270c4f12f2b8653c4eb39b16edf
parent4e63822fc4e2ca675dd28bb3f332f7fd4f3f8126 (diff)
downloadrust-2a6a42329f79d3c2a42f23ca9fb68aba511a2bc1.tar.gz
rust-2a6a42329f79d3c2a42f23ca9fb68aba511a2bc1.zip
Change E0369 diagnostic give note information for foreign items.
Make it easy for developers to understand why the binop cannot be applied.

fixes #125631
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs102
-rw-r--r--tests/ui/array-slice-vec/vec-res-add.stderr5
-rw-r--r--tests/ui/autoderef-full-lval.stderr12
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.rs16
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.stderr75
-rw-r--r--tests/ui/binop/binop-bitxor-str.stderr5
-rw-r--r--tests/ui/error-codes/E0067.stderr6
-rw-r--r--tests/ui/issues/issue-14915.stderr6
-rw-r--r--tests/ui/minus-string.stderr5
-rw-r--r--tests/ui/pattern/pattern-tyvar-2.stderr5
-rw-r--r--tests/ui/typeck/assign-non-lval-derefmut.stderr8
11 files changed, 219 insertions, 26 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 6a44d2afc15..ff066bb9ede 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<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);
@@ -2821,11 +2827,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             })
             .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()),
                     );
@@ -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::<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
diff --git a/tests/ui/array-slice-vec/vec-res-add.stderr b/tests/ui/array-slice-vec/vec-res-add.stderr
index cf5796f7e4a..34fd69426a8 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<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
 
diff --git a/tests/ui/autoderef-full-lval.stderr b/tests/ui/autoderef-full-lval.stderr
index 9921ce7c154..d90238a7fb2 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<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
@@ -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
 
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 00000000000..8827bbb003b
--- /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 00000000000..1cf75bbc1a5
--- /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 20b1ecc5a93..9d9ec6c5af6 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 ec0358cb7df..71b72080544 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 279f5772d21..3558bd651c6 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<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
 
diff --git a/tests/ui/minus-string.stderr b/tests/ui/minus-string.stderr
index 105274ee7d0..cf63ec24416 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 c6540e79558..be52fa8b239 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<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
 
diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr
index b26d16da015..ce0ff1d957b 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;