diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 8197ccf4be7..7ec39ac8515 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -78,6 +78,7 @@ use rustc_front::hir; use rustc_front::print::pprust; use middle::def; +use middle::def_id::DefId; use middle::infer; use middle::region; use middle::subst; @@ -226,6 +227,8 @@ pub trait ErrorReporting<'tcx> { fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &ty::TypeError<'tcx>); + fn check_and_note_conflicting_crates(&self, terr: &ty::TypeError<'tcx>, sp: Span); + fn report_and_explain_type_error(&self, trace: TypeTrace<'tcx>, terr: &ty::TypeError<'tcx>); @@ -484,6 +487,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { expected_found_str, terr); + self.check_and_note_conflicting_crates(terr, trace.origin.span()); + match trace.origin { infer::MatchExpressionArm(_, arm_span) => self.tcx.sess.span_note(arm_span, "match arm with an incompatible type"), @@ -491,6 +496,51 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { } } + /// Adds a note if the types come from similarly named crates + fn check_and_note_conflicting_crates(&self, terr: &ty::TypeError<'tcx>, sp: Span) { + let report_path_match = |did1: DefId, did2: DefId| { + // Only external crates, if either is from a local + // module we could have false positives + if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate { + let exp_path = self.tcx.with_path(did1, + |p| p.map(|x| x.to_string()) + .collect::>()); + let found_path = self.tcx.with_path(did2, + |p| p.map(|x| x.to_string()) + .collect::>()); + // We compare strings because PathMod and PathName can be different + // for imported and non-imported crates + if exp_path == found_path { + let crate_name = self.tcx.sess.cstore + .get_crate_data(did1.krate).name(); + self.tcx.sess.span_note(sp, &format!("Perhaps two different versions \ + of crate `{}` are being used?", + crate_name)); + } + } + }; + match *terr { + ty::TypeError::Sorts(ref exp_found) => { + // if they are both "path types", there's a chance of ambiguity + // due to different versions of the same crate + match (&exp_found.expected.sty, &exp_found.found.sty) { + (&ty::TyEnum(ref exp_adt, _), &ty::TyEnum(ref found_adt, _)) | + (&ty::TyStruct(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) | + (&ty::TyEnum(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) | + (&ty::TyStruct(ref exp_adt, _), &ty::TyEnum(ref found_adt, _)) => { + report_path_match(exp_adt.did, found_adt.did); + }, + _ => () + } + }, + ty::TypeError::Traits(ref exp_found) => { + self.tcx.sess.note("errrr0"); + report_path_match(exp_found.expected, exp_found.found); + }, + _ => () // FIXME(#22750) handle traits and stuff + } + } + fn report_and_explain_type_error(&self, trace: TypeTrace<'tcx>, terr: &ty::TypeError<'tcx>) { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 9f2c87b1a0a..99d8c5c9301 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5311,6 +5311,16 @@ impl<'tcx> TyS<'tcx> { impl<'tcx> fmt::Display for TypeError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeError::*; + fn report_maybe_different(f: &mut fmt::Formatter, + expected: String, found: String) -> fmt::Result { + // A naive approach to making sure that we're not reporting silly errors such as: + // (expected closure, found closure). + if expected == found { + write!(f, "expected {}, found a different {}", expected, found) + } else { + write!(f, "expected {}, found {}", expected, found) + } + } match *self { CyclicTy => write!(f, "cyclic type of infinite size"), @@ -5371,20 +5381,15 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { found bound lifetime parameter {}", br) } Sorts(values) => tls::with(|tcx| { - // A naive approach to making sure that we're not reporting silly errors such as: - // (expected closure, found closure). - let expected_str = values.expected.sort_string(tcx); - let found_str = values.found.sort_string(tcx); - if expected_str == found_str { - write!(f, "expected {}, found a different {}", expected_str, found_str) - } else { - write!(f, "expected {}, found {}", expected_str, found_str) - } + report_maybe_different(f, values.expected.sort_string(tcx), + values.found.sort_string(tcx)) }), Traits(values) => tls::with(|tcx| { - write!(f, "expected trait `{}`, found trait `{}`", - tcx.item_path_str(values.expected), - tcx.item_path_str(values.found)) + report_maybe_different(f, + format!("trait `{}`", + tcx.item_path_str(values.expected)), + format!("trait `{}`", + tcx.item_path_str(values.found))) }), BuiltinBoundsMismatch(values) => { if values.expected.is_empty() { diff --git a/src/test/auxiliary/crate_a1.rs b/src/test/auxiliary/crate_a1.rs new file mode 100644 index 00000000000..70f7cac94de --- /dev/null +++ b/src/test/auxiliary/crate_a1.rs @@ -0,0 +1,21 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Foo; + +pub trait Bar{} + +pub fn bar() -> Box { + unimplemented!() +} + + +pub fn try_foo(x: Foo){} +pub fn try_bar(x: Box){} diff --git a/src/test/auxiliary/crate_a2.rs b/src/test/auxiliary/crate_a2.rs new file mode 100644 index 00000000000..d801f25ba2e --- /dev/null +++ b/src/test/auxiliary/crate_a2.rs @@ -0,0 +1,17 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Foo; + +pub trait Bar{} + +pub fn bar() -> Box { + unimplemented!() +} diff --git a/src/test/compile-fail/type-mismatch-same-crate-name.rs b/src/test/compile-fail/type-mismatch-same-crate-name.rs new file mode 100644 index 00000000000..014fa35c309 --- /dev/null +++ b/src/test/compile-fail/type-mismatch-same-crate-name.rs @@ -0,0 +1,33 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:crate_a1.rs +// aux-build:crate_a2.rs + +// This tests the extra note reported when a type error deals with +// seemingly identical types. +// The main use case of this error is when there are two crates +// (generally different versions of the same crate) with the same name +// causing a type mismatch. Here, we simulate that error using block-scoped +// aliased `extern crate` declarations. + +fn main() { + let foo2 = {extern crate crate_a2 as a; a::Foo}; + let bar2 = {extern crate crate_a2 as a; a::bar()}; + { + extern crate crate_a1 as a; + a::try_foo(foo2); //~ ERROR mismatched types + //~^ HELP run + //~^^ NOTE Perhaps two different versions of crate `crate_a1` + a::try_bar(bar2); //~ ERROR mismatched types + //~^ HELP run + //~^^ NOTE Perhaps two different versions of crate `crate_a1` + } +}