about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-26 05:07:02 +0000
committerbors <bors@rust-lang.org>2019-11-26 05:07:02 +0000
commit0f6f66fcdc4abf110171ee06b1a72bdd2883b74f (patch)
treee36f93d606f1cd8d914a723c68d9c8b6ab73e6b6
parent2626f3d3d5c3007745176aa0fe22781b9ec2bb06 (diff)
parent2a0292f9aa96e32ca44ec7dc599c668fd735dbe8 (diff)
downloadrust-0f6f66fcdc4abf110171ee06b1a72bdd2883b74f.tar.gz
rust-0f6f66fcdc4abf110171ee06b1a72bdd2883b74f.zip
Auto merge of #66561 - TimoFreiberg:trait-name-report, r=estebank
Add version mismatch help message for unimplemented trait

Improves issue #22750

The error reporting for E0277 (the trait `X` is not implemented for `Foo`)
now checks whether `Foo` implements a trait with the same path as `X`,
which probably means that the programmer wanted to actually use only one
version of the trait `X` instead of the two.

Still open:
* the same diagnostic should be added for [the trait method case](https://github.com/rust-lang/rust/issues/22750#issuecomment-372077056)
* Showing the real crate versions would be nice, but rustc currently doesn't have that information [according to Esteban](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/diagnostics.20for.20crate.20version.20mismatch/near/180572989)
-rw-r--r--src/librustc/traits/error_reporting.rs39
-rw-r--r--src/test/ui/traits/auxiliary/crate_a1.rs12
-rw-r--r--src/test/ui/traits/auxiliary/crate_a2.rs13
-rw-r--r--src/test/ui/traits/trait-bounds-same-crate-name.rs55
-rw-r--r--src/test/ui/traits/trait-bounds-same-crate-name.stderr64
5 files changed, 176 insertions, 7 deletions
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 0144d51a969..f8efa0b9de2 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -38,6 +38,7 @@ use std::fmt;
 use syntax::ast;
 use syntax::symbol::{sym, kw};
 use syntax_pos::{DUMMY_SP, Span, ExpnKind, MultiSpan};
+use rustc::hir::def_id::LOCAL_CRATE;
 
 use rustc_error_codes::*;
 
@@ -799,6 +800,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg);
                         self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
                         self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
+                        self.note_version_mismatch(&mut err, &trait_ref);
 
                         // Try to report a help message
                         if !trait_ref.has_infer_types() &&
@@ -1050,6 +1052,43 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         err.emit();
     }
 
+    /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
+    /// with the same path as `trait_ref`, a help message about
+    /// a probable version mismatch is added to `err`
+    fn note_version_mismatch(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) {
+        let get_trait_impl = |trait_def_id| {
+            let mut trait_impl = None;
+            self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| {
+                if trait_impl.is_none() {
+                    trait_impl = Some(impl_def_id);
+                }
+            });
+            trait_impl
+        };
+        let required_trait_path = self.tcx.def_path_str(trait_ref.def_id());
+        let all_traits = self.tcx.all_traits(LOCAL_CRATE);
+        let traits_with_same_path: std::collections::BTreeSet<_> = all_traits
+            .iter()
+            .filter(|trait_def_id| **trait_def_id != trait_ref.def_id())
+            .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path)
+            .collect();
+        for trait_with_same_path in traits_with_same_path {
+            if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) {
+                let impl_span = self.tcx.def_span(impl_def_id);
+                err.span_help(impl_span, "trait impl with same name found");
+                let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
+                let crate_msg = format!(
+                    "Perhaps two different versions of crate `{}` are being used?",
+                    trait_crate
+                );
+                err.note(&crate_msg);
+            }
+        }
+    }
     fn suggest_restricting_param_bound(
         &self,
         err: &mut DiagnosticBuilder<'_>,
diff --git a/src/test/ui/traits/auxiliary/crate_a1.rs b/src/test/ui/traits/auxiliary/crate_a1.rs
index e2e18500541..6aa010258eb 100644
--- a/src/test/ui/traits/auxiliary/crate_a1.rs
+++ b/src/test/ui/traits/auxiliary/crate_a1.rs
@@ -1,11 +1,9 @@
-pub struct Foo;
+pub trait Bar {}
 
-pub trait Bar{}
+pub fn try_foo(x: impl Bar) {}
 
-pub fn bar() -> Box<Bar> {
-    unimplemented!()
+pub struct ImplementsTraitForUsize<T> {
+    _marker: std::marker::PhantomData<T>,
 }
 
-
-pub fn try_foo(x: Foo){}
-pub fn try_bar(x: Box<Bar>){}
+impl Bar for ImplementsTraitForUsize<usize> {}
diff --git a/src/test/ui/traits/auxiliary/crate_a2.rs b/src/test/ui/traits/auxiliary/crate_a2.rs
new file mode 100644
index 00000000000..d6057db5e63
--- /dev/null
+++ b/src/test/ui/traits/auxiliary/crate_a2.rs
@@ -0,0 +1,13 @@
+pub struct Foo;
+
+pub trait Bar {}
+
+impl Bar for Foo {}
+
+pub struct DoesNotImplementTrait;
+
+pub struct ImplementsWrongTraitConditionally<T> {
+    _marker: std::marker::PhantomData<T>,
+}
+
+impl Bar for ImplementsWrongTraitConditionally<isize> {}
diff --git a/src/test/ui/traits/trait-bounds-same-crate-name.rs b/src/test/ui/traits/trait-bounds-same-crate-name.rs
new file mode 100644
index 00000000000..af720ecfdc0
--- /dev/null
+++ b/src/test/ui/traits/trait-bounds-same-crate-name.rs
@@ -0,0 +1,55 @@
+// aux-build:crate_a1.rs
+// aux-build:crate_a2.rs
+
+// Issue 22750
+// This tests the extra help message reported when a trait bound
+// is not met but the struct implements a trait with the same path.
+
+fn main() {
+    let foo = {
+        extern crate crate_a2 as a;
+        a::Foo
+    };
+
+    let implements_no_traits = {
+        extern crate crate_a2 as a;
+        a::DoesNotImplementTrait
+    };
+
+    let other_variant_implements_mismatched_trait = {
+        extern crate crate_a2 as a;
+        a::ImplementsWrongTraitConditionally { _marker: std::marker::PhantomData::<isize> }
+    };
+
+    let other_variant_implements_correct_trait = {
+        extern crate crate_a1 as a;
+        a::ImplementsTraitForUsize { _marker: std::marker::PhantomData::<isize> }
+    };
+
+    {
+        extern crate crate_a1 as a;
+        a::try_foo(foo);
+        //~^ ERROR E0277
+        //~| trait impl with same name found
+        //~| Perhaps two different versions of crate `crate_a2`
+
+        // We don't want to see the "version mismatch" help message here
+        // because `implements_no_traits` has no impl for `Foo`
+        a::try_foo(implements_no_traits);
+        //~^ ERROR E0277
+
+        // We don't want to see the "version mismatch" help message here
+        // because `other_variant_implements_mismatched_trait`
+        // does not have an impl for its `<isize>` variant,
+        // only for its `<usize>` variant.
+        a::try_foo(other_variant_implements_mismatched_trait);
+        //~^ ERROR E0277
+
+        // We don't want to see the "version mismatch" help message here
+        // because `ImplementsTraitForUsize` only has
+        // impls for the correct trait where the path is not misleading.
+        a::try_foo(other_variant_implements_correct_trait);
+        //~^ ERROR E0277
+        //~| the following implementations were found:
+    }
+}
diff --git a/src/test/ui/traits/trait-bounds-same-crate-name.stderr b/src/test/ui/traits/trait-bounds-same-crate-name.stderr
new file mode 100644
index 00000000000..8fd0bd13e54
--- /dev/null
+++ b/src/test/ui/traits/trait-bounds-same-crate-name.stderr
@@ -0,0 +1,64 @@
+error[E0277]: the trait bound `main::a::Foo: main::a::Bar` is not satisfied
+  --> $DIR/trait-bounds-same-crate-name.rs:31:20
+   |
+LL |         a::try_foo(foo);
+   |                    ^^^ the trait `main::a::Bar` is not implemented for `main::a::Foo`
+   | 
+  ::: $DIR/auxiliary/crate_a1.rs:3:24
+   |
+LL | pub fn try_foo(x: impl Bar) {}
+   |                        --- required by this bound in `main::a::try_foo`
+   |
+help: trait impl with same name found
+  --> $DIR/auxiliary/crate_a2.rs:5:1
+   |
+LL | impl Bar for Foo {}
+   | ^^^^^^^^^^^^^^^^^^^
+   = note: Perhaps two different versions of crate `crate_a2` are being used?
+
+error[E0277]: the trait bound `main::a::DoesNotImplementTrait: main::a::Bar` is not satisfied
+  --> $DIR/trait-bounds-same-crate-name.rs:38:20
+   |
+LL |         a::try_foo(implements_no_traits);
+   |                    ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::DoesNotImplementTrait`
+   | 
+  ::: $DIR/auxiliary/crate_a1.rs:3:24
+   |
+LL | pub fn try_foo(x: impl Bar) {}
+   |                        --- required by this bound in `main::a::try_foo`
+
+error[E0277]: the trait bound `main::a::ImplementsWrongTraitConditionally<isize>: main::a::Bar` is not satisfied
+  --> $DIR/trait-bounds-same-crate-name.rs:45:20
+   |
+LL |         a::try_foo(other_variant_implements_mismatched_trait);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::ImplementsWrongTraitConditionally<isize>`
+   | 
+  ::: $DIR/auxiliary/crate_a1.rs:3:24
+   |
+LL | pub fn try_foo(x: impl Bar) {}
+   |                        --- required by this bound in `main::a::try_foo`
+   |
+help: trait impl with same name found
+  --> $DIR/auxiliary/crate_a2.rs:13:1
+   |
+LL | impl Bar for ImplementsWrongTraitConditionally<isize> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: Perhaps two different versions of crate `crate_a2` are being used?
+
+error[E0277]: the trait bound `main::a::ImplementsTraitForUsize<isize>: main::a::Bar` is not satisfied
+  --> $DIR/trait-bounds-same-crate-name.rs:51:20
+   |
+LL |         a::try_foo(other_variant_implements_correct_trait);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::ImplementsTraitForUsize<isize>`
+   | 
+  ::: $DIR/auxiliary/crate_a1.rs:3:24
+   |
+LL | pub fn try_foo(x: impl Bar) {}
+   |                        --- required by this bound in `main::a::try_foo`
+   |
+   = help: the following implementations were found:
+             <main::a::ImplementsTraitForUsize<usize> as main::a::Bar>
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.