Skip to content

Commit

Permalink
Add version mismatch help message for unimplemented trait
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
TimoFreiberg committed Nov 24, 2019
1 parent b56b239 commit dffdf37
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 7 deletions.
39 changes: 39 additions & 0 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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() &&
Expand Down Expand Up @@ -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

This comment has been minimized.

Copy link
@technic

technic Aug 23, 2021

What's the reason to collecting in BTreeSet here? I can't see where it is used later, having just iterator seems to be enough.

This comment has been minimized.

Copy link
@TimoFreiberg

TimoFreiberg Aug 24, 2021

Author Contributor

I don't remember what I was thinking at the time, but I usually use sets for uniqueness (and BTreeSets in particular also for ordering).
I'm not sure if all_traits can contain duplicates, but it's still not wrong to make extra sure no duplicate diagnostics are rendered.

This comment has been minimized.

Copy link
@technic

technic Aug 24, 2021

Yep, make sense. thanks for reply :)

.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<'_>,
Expand Down
8 changes: 1 addition & 7 deletions src/test/ui/traits/auxiliary/crate_a1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,4 @@ pub struct Foo;

pub trait Bar{}

pub fn bar() -> Box<Bar> {
unimplemented!()
}


pub fn try_foo(x: Foo){}
pub fn try_bar(x: Box<Bar>){}
pub fn try_foo(x: impl Bar){}
5 changes: 5 additions & 0 deletions src/test/ui/traits/auxiliary/crate_a2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub struct Foo;

pub trait Bar{}

impl Bar for Foo {}
21 changes: 21 additions & 0 deletions src/test/ui/traits/trait-bounds-same-crate-name.rs
Original file line number Diff line number Diff line change
@@ -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`
}
}
21 changes: 21 additions & 0 deletions src/test/ui/traits/trait-bounds-same-crate-name.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit dffdf37

Please sign in to comment.