Detect multiple crate versions on method not found
When a type comes indirectly from one crate version but the imported trait comes from a separate crate version, the called method won't be found. We now show additional context: ``` error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope --> multiple-dep-versions.rs:8:10 | 8 | Type.foo(); | ^^^ method not found in `Type` | note: you have multiple different versions of crate `dependency` in your dependency graph --> multiple-dep-versions.rs:4:32 | 4 | use dependency::{do_something, Trait}; | ^^^^^ `dependency` imported here doesn't correspond to the right crate version | ::: ~/rust/build/x86_64-unknown-linux-gnu/test/run-make/crate-loading/rmake_out/multiple-dep-versions-1.rs:4:1 | 4 | pub trait Trait { | --------------- this is the trait that was imported | ::: ~/rust/build/x86_64-unknown-linux-gnu/test/run-make/crate-loading/rmake_out/multiple-dep-versions-2.rs:4:1 | 4 | pub trait Trait { | --------------- this is the trait that is needed 5 | fn foo(&self); | --- the method is available for `dep_2_reexport::Type` here ```
This commit is contained in:
parent
91376f4162
commit
b2e7ae1f65
@ -3448,6 +3448,7 @@ fn suggest_traits_to_import(
|
|||||||
trait_missing_method: bool,
|
trait_missing_method: bool,
|
||||||
) {
|
) {
|
||||||
let mut alt_rcvr_sugg = false;
|
let mut alt_rcvr_sugg = false;
|
||||||
|
let mut suggest = true;
|
||||||
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
|
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
|
||||||
debug!(
|
debug!(
|
||||||
"suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
|
"suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
|
||||||
@ -3491,11 +3492,19 @@ fn suggest_traits_to_import(
|
|||||||
let did = Some(pick.item.container_id(self.tcx));
|
let did = Some(pick.item.container_id(self.tcx));
|
||||||
let skip = skippable.contains(&did);
|
let skip = skippable.contains(&did);
|
||||||
if pick.autoderefs == 0 && !skip {
|
if pick.autoderefs == 0 && !skip {
|
||||||
|
suggest = self.detect_and_explain_multiple_crate_versions(
|
||||||
|
err,
|
||||||
|
&pick.item,
|
||||||
|
rcvr.hir_id.owner,
|
||||||
|
*rcvr_ty,
|
||||||
|
);
|
||||||
|
if suggest {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
pick.item.ident(self.tcx).span,
|
pick.item.ident(self.tcx).span,
|
||||||
format!("the method is available for `{rcvr_ty}` here"),
|
format!("the method is available for `{rcvr_ty}` here"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(MethodError::Ambiguity(_)) => {
|
Err(MethodError::Ambiguity(_)) => {
|
||||||
@ -3675,7 +3684,7 @@ fn suggest_traits_to_import(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) {
|
if suggest && self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4040,6 +4049,58 @@ enum Introducer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detect_and_explain_multiple_crate_versions(
|
||||||
|
&self,
|
||||||
|
err: &mut Diag<'_>,
|
||||||
|
item: &ty::AssocItem,
|
||||||
|
owner: hir::OwnerId,
|
||||||
|
rcvr_ty: Ty<'_>,
|
||||||
|
) -> bool {
|
||||||
|
let pick_name = self.tcx.crate_name(item.def_id.krate);
|
||||||
|
if let Some(map) = self.tcx.in_scope_traits_map(owner) {
|
||||||
|
for trait_candidate in map.to_sorted_stable_ord().into_iter().flat_map(|v| v.1.iter()) {
|
||||||
|
let name = self.tcx.crate_name(trait_candidate.def_id.krate);
|
||||||
|
if trait_candidate.def_id.krate != item.def_id.krate && name == pick_name {
|
||||||
|
let msg = format!(
|
||||||
|
"you have multiple different versions of crate `{name}` in your \
|
||||||
|
dependency graph",
|
||||||
|
);
|
||||||
|
let tdid = self.tcx.parent(item.def_id);
|
||||||
|
if self.tcx.item_name(trait_candidate.def_id) == self.tcx.item_name(tdid)
|
||||||
|
&& let Some(def_id) = trait_candidate.import_ids.get(0)
|
||||||
|
{
|
||||||
|
let span = self.tcx.def_span(*def_id);
|
||||||
|
let mut multi_span: MultiSpan = span.into();
|
||||||
|
multi_span.push_span_label(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"`{name}` imported here doesn't correspond to the right crate \
|
||||||
|
version",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
multi_span.push_span_label(
|
||||||
|
self.tcx.def_span(trait_candidate.def_id),
|
||||||
|
format!("this is the trait that was imported"),
|
||||||
|
);
|
||||||
|
multi_span.push_span_label(
|
||||||
|
self.tcx.def_span(tdid),
|
||||||
|
format!("this is the trait that is needed"),
|
||||||
|
);
|
||||||
|
multi_span.push_span_label(
|
||||||
|
item.ident(self.tcx).span,
|
||||||
|
format!("the method is available for `{rcvr_ty}` here"),
|
||||||
|
);
|
||||||
|
err.span_note(multi_span, msg);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
err.note(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
|
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
|
||||||
/// FIXME: currently not working for suggesting `map_or_else`, see #102408
|
/// FIXME: currently not working for suggesting `map_or_else`, see #102408
|
||||||
pub(crate) fn suggest_else_fn_with_closure(
|
pub(crate) fn suggest_else_fn_with_closure(
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#![crate_name = "dependency"]
|
#![crate_name = "dependency"]
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
pub struct Type;
|
pub struct Type(pub i32);
|
||||||
pub trait Trait {}
|
pub trait Trait {
|
||||||
impl Trait for Type {}
|
fn foo(&self);
|
||||||
|
}
|
||||||
|
impl Trait for Type {
|
||||||
|
fn foo(&self) {}
|
||||||
|
}
|
||||||
pub fn do_something<X: Trait>(_: X) {}
|
pub fn do_something<X: Trait>(_: X) {}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#![crate_name = "dependency"]
|
#![crate_name = "dependency"]
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
pub struct Type(pub i32);
|
pub struct Type;
|
||||||
pub trait Trait {}
|
pub trait Trait {
|
||||||
impl Trait for Type {}
|
fn foo(&self);
|
||||||
|
}
|
||||||
|
impl Trait for Type {
|
||||||
|
fn foo(&self) {}
|
||||||
|
}
|
||||||
pub fn do_something<X: Trait>(_: X) {}
|
pub fn do_something<X: Trait>(_: X) {}
|
||||||
|
5
tests/run-make/crate-loading/multiple-dep-versions-3.rs
Normal file
5
tests/run-make/crate-loading/multiple-dep-versions-3.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#![crate_name = "foo"]
|
||||||
|
#![crate_type = "rlib"]
|
||||||
|
|
||||||
|
extern crate dependency;
|
||||||
|
pub use dependency::Type;
|
@ -1,8 +1,9 @@
|
|||||||
extern crate dep_2_reexport;
|
extern crate dep_2_reexport;
|
||||||
extern crate dependency;
|
extern crate dependency;
|
||||||
use dep_2_reexport::do_something;
|
use dep_2_reexport::Type;
|
||||||
use dependency::Type;
|
use dependency::{do_something, Trait};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
do_something(Type);
|
do_something(Type);
|
||||||
|
Type.foo();
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,15 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
rustc().input("multiple-dep-versions-1.rs").run();
|
rustc().input("multiple-dep-versions-1.rs").run();
|
||||||
rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run();
|
rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run();
|
||||||
|
rustc()
|
||||||
|
.input("multiple-dep-versions-3.rs")
|
||||||
|
.extern_("dependency", rust_lib_name("dependency2"))
|
||||||
|
.run();
|
||||||
|
|
||||||
rustc()
|
rustc()
|
||||||
.input("multiple-dep-versions.rs")
|
.input("multiple-dep-versions.rs")
|
||||||
.extern_("dependency", rust_lib_name("dependency"))
|
.extern_("dependency", rust_lib_name("dependency"))
|
||||||
.extern_("dep_2_reexport", rust_lib_name("dependency2"))
|
.extern_("dep_2_reexport", rust_lib_name("foo"))
|
||||||
.run_fail()
|
.run_fail()
|
||||||
.assert_stderr_contains(
|
.assert_stderr_contains(
|
||||||
"you have multiple different versions of crate `dependency` in your dependency graph",
|
"you have multiple different versions of crate `dependency` in your dependency graph",
|
||||||
@ -22,5 +26,11 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.assert_stderr_contains("this type doesn't implement the required trait")
|
.assert_stderr_contains("this type doesn't implement the required trait")
|
||||||
.assert_stderr_contains("this type implements the required trait")
|
.assert_stderr_contains("this type implements the required trait")
|
||||||
.assert_stderr_contains("this is the required trait");
|
.assert_stderr_contains("this is the required trait")
|
||||||
|
.assert_stderr_contains(
|
||||||
|
"`dependency` imported here doesn't correspond to the right crate version",
|
||||||
|
)
|
||||||
|
.assert_stderr_contains("this is the trait that was imported")
|
||||||
|
.assert_stderr_contains("this is the trait that is needed")
|
||||||
|
.assert_stderr_contains("the method is available for `dep_2_reexport::Type` here");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user