From dffdf378ee6950a7ecd8d5c235e6ad891b5bc4b8 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sun, 3 Nov 2019 23:06:48 +0100 Subject: [PATCH 1/3] Add version mismatch help message for unimplemented trait 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. --- src/librustc/traits/error_reporting.rs | 39 +++++++++++++++++++ src/test/ui/traits/auxiliary/crate_a1.rs | 8 +--- src/test/ui/traits/auxiliary/crate_a2.rs | 5 +++ .../ui/traits/trait-bounds-same-crate-name.rs | 21 ++++++++++ .../trait-bounds-same-crate-name.stderr | 21 ++++++++++ 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/traits/auxiliary/crate_a2.rs create mode 100644 src/test/ui/traits/trait-bounds-same-crate-name.rs create mode 100644 src/test/ui/traits/trait-bounds-same-crate-name.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0144d51a969..00b575adbce 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..724206860fe 100644 --- a/src/test/ui/traits/auxiliary/crate_a1.rs +++ b/src/test/ui/traits/auxiliary/crate_a1.rs @@ -2,10 +2,4 @@ pub struct Foo; pub trait Bar{} -pub fn bar() -> Box { - unimplemented!() -} - - -pub fn try_foo(x: Foo){} -pub fn try_bar(x: Box){} +pub fn try_foo(x: impl Bar){} 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..d8a178dc0d9 --- /dev/null +++ b/src/test/ui/traits/auxiliary/crate_a2.rs @@ -0,0 +1,5 @@ +pub struct Foo; + +pub trait Bar{} + +impl Bar for Foo {} 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..2ef1c989a56 --- /dev/null +++ b/src/test/ui/traits/trait-bounds-same-crate-name.rs @@ -0,0 +1,21 @@ +// 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 foo2 = { + extern crate crate_a2 as a; + a::Foo + }; + + { + extern crate crate_a1 as a; + a::try_foo(foo2); + //~^ ERROR E0277 + //~| Trait impl with same name found + //~| Perhaps two different versions of crate `crate_a2` + } +} 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..78ea9a849f4 --- /dev/null +++ b/src/test/ui/traits/trait-bounds-same-crate-name.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `main::a::Foo: main::a::Bar` is not satisfied + --> $DIR/trait-bounds-same-crate-name.rs:16:20 + | +LL | a::try_foo(foo2); + | ^^^^ the trait `main::a::Bar` is not implemented for `main::a::Foo` + | + ::: $DIR/auxiliary/crate_a1.rs:5: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: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 318fb9a4da8adc4451b1e44ee823576e32eb437f Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sun, 24 Nov 2019 13:01:13 +0100 Subject: [PATCH 2/3] fixup! Add negative tests where the diagnostic message would be wrong --- src/test/ui/traits/auxiliary/crate_a1.rs | 10 +++- src/test/ui/traits/auxiliary/crate_a2.rs | 10 +++- .../ui/traits/trait-bounds-same-crate-name.rs | 40 ++++++++++++- .../trait-bounds-same-crate-name.stderr | 57 ++++++++++++++++--- 4 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/test/ui/traits/auxiliary/crate_a1.rs b/src/test/ui/traits/auxiliary/crate_a1.rs index 724206860fe..6aa010258eb 100644 --- a/src/test/ui/traits/auxiliary/crate_a1.rs +++ b/src/test/ui/traits/auxiliary/crate_a1.rs @@ -1,5 +1,9 @@ -pub struct Foo; +pub trait Bar {} -pub trait Bar{} +pub fn try_foo(x: impl Bar) {} -pub fn try_foo(x: impl Bar){} +pub struct ImplementsTraitForUsize { + _marker: std::marker::PhantomData, +} + +impl Bar for ImplementsTraitForUsize {} diff --git a/src/test/ui/traits/auxiliary/crate_a2.rs b/src/test/ui/traits/auxiliary/crate_a2.rs index d8a178dc0d9..d6057db5e63 100644 --- a/src/test/ui/traits/auxiliary/crate_a2.rs +++ b/src/test/ui/traits/auxiliary/crate_a2.rs @@ -1,5 +1,13 @@ pub struct Foo; -pub trait Bar{} +pub trait Bar {} impl Bar for Foo {} + +pub struct DoesNotImplementTrait; + +pub struct ImplementsWrongTraitConditionally { + _marker: std::marker::PhantomData, +} + +impl Bar for ImplementsWrongTraitConditionally {} diff --git a/src/test/ui/traits/trait-bounds-same-crate-name.rs b/src/test/ui/traits/trait-bounds-same-crate-name.rs index 2ef1c989a56..af720ecfdc0 100644 --- a/src/test/ui/traits/trait-bounds-same-crate-name.rs +++ b/src/test/ui/traits/trait-bounds-same-crate-name.rs @@ -6,16 +6,50 @@ // is not met but the struct implements a trait with the same path. fn main() { - let foo2 = { + 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:: } + }; + + let other_variant_implements_correct_trait = { + extern crate crate_a1 as a; + a::ImplementsTraitForUsize { _marker: std::marker::PhantomData:: } + }; + { extern crate crate_a1 as a; - a::try_foo(foo2); + a::try_foo(foo); //~^ ERROR E0277 - //~| Trait impl with same name found + //~| 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 `` variant, + // only for its `` 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 index 78ea9a849f4..8fd0bd13e54 100644 --- a/src/test/ui/traits/trait-bounds-same-crate-name.stderr +++ b/src/test/ui/traits/trait-bounds-same-crate-name.stderr @@ -1,21 +1,64 @@ error[E0277]: the trait bound `main::a::Foo: main::a::Bar` is not satisfied - --> $DIR/trait-bounds-same-crate-name.rs:16:20 + --> $DIR/trait-bounds-same-crate-name.rs:31:20 | -LL | a::try_foo(foo2); - | ^^^^ the trait `main::a::Bar` is not implemented for `main::a::Foo` +LL | a::try_foo(foo); + | ^^^ the trait `main::a::Bar` is not implemented for `main::a::Foo` | - ::: $DIR/auxiliary/crate_a1.rs:5:24 + ::: $DIR/auxiliary/crate_a1.rs:3:24 | -LL | pub fn try_foo(x: impl Bar){} +LL | pub fn try_foo(x: impl Bar) {} | --- required by this bound in `main::a::try_foo` | -help: Trait impl with same name found +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: aborting due to previous error +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: 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` + | + ::: $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 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: Perhaps two different versions of crate `crate_a2` are being used? + +error[E0277]: the trait bound `main::a::ImplementsTraitForUsize: 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` + | + ::: $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: + as main::a::Bar> + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. From 2a0292f9aa96e32ca44ec7dc599c668fd735dbe8 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sun, 24 Nov 2019 13:01:24 +0100 Subject: [PATCH 3/3] fixup! Lowercase diagnostic message label --- src/librustc/traits/error_reporting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 00b575adbce..f8efa0b9de2 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1079,7 +1079,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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"); + 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?",