Skip to content

Commit 9b318d2

Browse files
committed
Auto merge of #128786 - estebank:multiple-crate-versions, r=fee1-dead
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: there are multiple different versions of crate `dependency` in the 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 ``` Fix #128569, fix #110926, fix #109161, fix #81659, fix #51458, fix #32611. Follow up to #124944.
2 parents c6f81a4 + 110b19b commit 9b318d2

File tree

7 files changed

+204
-20
lines changed

7 files changed

+204
-20
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+94-3
Original file line numberDiff line numberDiff line change
@@ -3448,6 +3448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34483448
trait_missing_method: bool,
34493449
) {
34503450
let mut alt_rcvr_sugg = false;
3451+
let mut trait_in_other_version_found = false;
34513452
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
34523453
debug!(
34533454
"suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
@@ -3489,8 +3490,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34893490
// self types and rely on the suggestion to `use` the trait from
34903491
// `suggest_valid_traits`.
34913492
let did = Some(pick.item.container_id(self.tcx));
3492-
let skip = skippable.contains(&did);
3493-
if pick.autoderefs == 0 && !skip {
3493+
if skippable.contains(&did) {
3494+
continue;
3495+
}
3496+
trait_in_other_version_found = self
3497+
.detect_and_explain_multiple_crate_versions(
3498+
err,
3499+
pick.item.def_id,
3500+
rcvr.hir_id,
3501+
*rcvr_ty,
3502+
);
3503+
if pick.autoderefs == 0 && !trait_in_other_version_found {
34943504
err.span_label(
34953505
pick.item.ident(self.tcx).span,
34963506
format!("the method is available for `{rcvr_ty}` here"),
@@ -3675,7 +3685,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
36753685
}
36763686
}
36773687
}
3678-
if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) {
3688+
3689+
if let SelfSource::QPath(ty) = source
3690+
&& !valid_out_of_scope_traits.is_empty()
3691+
&& let hir::TyKind::Path(path) = ty.kind
3692+
&& let hir::QPath::Resolved(_, path) = path
3693+
&& let Some(def_id) = path.res.opt_def_id()
3694+
&& let Some(assoc) = self
3695+
.tcx
3696+
.associated_items(valid_out_of_scope_traits[0])
3697+
.filter_by_name_unhygienic(item_name.name)
3698+
.next()
3699+
{
3700+
// See if the `Type::function(val)` where `function` wasn't found corresponds to a
3701+
// `Trait` that is imported directly, but `Type` came from a different version of the
3702+
// same crate.
3703+
let rcvr_ty = self.tcx.type_of(def_id).instantiate_identity();
3704+
trait_in_other_version_found = self.detect_and_explain_multiple_crate_versions(
3705+
err,
3706+
assoc.def_id,
3707+
ty.hir_id,
3708+
rcvr_ty,
3709+
);
3710+
}
3711+
if !trait_in_other_version_found
3712+
&& self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true)
3713+
{
36793714
return;
36803715
}
36813716

@@ -4040,6 +4075,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
40404075
}
40414076
}
40424077

4078+
fn detect_and_explain_multiple_crate_versions(
4079+
&self,
4080+
err: &mut Diag<'_>,
4081+
item_def_id: DefId,
4082+
hir_id: hir::HirId,
4083+
rcvr_ty: Ty<'_>,
4084+
) -> bool {
4085+
let hir_id = self.tcx.parent_hir_id(hir_id);
4086+
let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
4087+
if traits.is_empty() {
4088+
return false;
4089+
}
4090+
let trait_def_id = self.tcx.parent(item_def_id);
4091+
let krate = self.tcx.crate_name(trait_def_id.krate);
4092+
let name = self.tcx.item_name(trait_def_id);
4093+
let candidates: Vec<_> = traits
4094+
.iter()
4095+
.filter(|c| {
4096+
c.def_id.krate != trait_def_id.krate
4097+
&& self.tcx.crate_name(c.def_id.krate) == krate
4098+
&& self.tcx.item_name(c.def_id) == name
4099+
})
4100+
.map(|c| (c.def_id, c.import_ids.get(0).cloned()))
4101+
.collect();
4102+
if candidates.is_empty() {
4103+
return false;
4104+
}
4105+
let item_span = self.tcx.def_span(item_def_id);
4106+
let msg = format!(
4107+
"there are multiple different versions of crate `{krate}` in the dependency graph",
4108+
);
4109+
let trait_span = self.tcx.def_span(trait_def_id);
4110+
let mut multi_span: MultiSpan = trait_span.into();
4111+
multi_span.push_span_label(trait_span, format!("this is the trait that is needed"));
4112+
let descr = self.tcx.associated_item(item_def_id).descr();
4113+
multi_span
4114+
.push_span_label(item_span, format!("the {descr} is available for `{rcvr_ty}` here"));
4115+
for (def_id, import_def_id) in candidates {
4116+
if let Some(import_def_id) = import_def_id {
4117+
multi_span.push_span_label(
4118+
self.tcx.def_span(import_def_id),
4119+
format!(
4120+
"`{name}` imported here doesn't correspond to the right version of crate \
4121+
`{krate}`",
4122+
),
4123+
);
4124+
}
4125+
multi_span.push_span_label(
4126+
self.tcx.def_span(def_id),
4127+
format!("this is the trait that was imported"),
4128+
);
4129+
}
4130+
err.span_note(multi_span, msg);
4131+
true
4132+
}
4133+
40434134
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
40444135
/// FIXME: currently not working for suggesting `map_or_else`, see #102408
40454136
pub(crate) fn suggest_else_fn_with_closure(

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1689,11 +1689,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16891689
err.highlighted_span_help(
16901690
span,
16911691
vec![
1692-
StringPart::normal("you have ".to_string()),
1692+
StringPart::normal("there are ".to_string()),
16931693
StringPart::highlighted("multiple different versions".to_string()),
16941694
StringPart::normal(" of crate `".to_string()),
16951695
StringPart::highlighted(format!("{name}")),
1696-
StringPart::normal("` in your dependency graph".to_string()),
1696+
StringPart::normal("` the your dependency graph".to_string()),
16971697
],
16981698
);
16991699
let candidates = if impl_candidates.is_empty() {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#![crate_name = "dependency"]
22
#![crate_type = "rlib"]
3-
pub struct Type;
4-
pub trait Trait {}
5-
impl Trait for Type {}
3+
pub struct Type(pub i32);
4+
pub trait Trait {
5+
fn foo(&self);
6+
fn bar();
7+
}
8+
impl Trait for Type {
9+
fn foo(&self) {}
10+
fn bar() {}
11+
}
612
pub fn do_something<X: Trait>(_: X) {}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#![crate_name = "dependency"]
22
#![crate_type = "rlib"]
3-
pub struct Type(pub i32);
4-
pub trait Trait {}
5-
impl Trait for Type {}
3+
pub struct Type;
4+
pub trait Trait {
5+
fn foo(&self);
6+
fn bar();
7+
}
8+
impl Trait for Type {
9+
fn foo(&self) {}
10+
fn bar() {}
11+
}
612
pub fn do_something<X: Trait>(_: X) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![crate_name = "foo"]
2+
#![crate_type = "rlib"]
3+
4+
extern crate dependency;
5+
pub use dependency::Type;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
extern crate dep_2_reexport;
22
extern crate dependency;
3-
use dep_2_reexport::do_something;
4-
use dependency::Type;
3+
use dep_2_reexport::Type;
4+
use dependency::{do_something, Trait};
55

66
fn main() {
77
do_something(Type);
8+
Type.foo();
9+
Type::bar();
810
}

tests/run-make/crate-loading/rmake.rs

+81-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,100 @@
11
//@ only-linux
22
//@ ignore-wasm32
33
//@ ignore-wasm64
4+
// ignore-tidy-linelength
45

56
use run_make_support::{rust_lib_name, rustc};
67

78
fn main() {
89
rustc().input("multiple-dep-versions-1.rs").run();
910
rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run();
11+
rustc()
12+
.input("multiple-dep-versions-3.rs")
13+
.extern_("dependency", rust_lib_name("dependency2"))
14+
.run();
1015

1116
rustc()
1217
.input("multiple-dep-versions.rs")
1318
.extern_("dependency", rust_lib_name("dependency"))
14-
.extern_("dep_2_reexport", rust_lib_name("dependency2"))
19+
.extern_("dep_2_reexport", rust_lib_name("foo"))
1520
.run_fail()
1621
.assert_stderr_contains(
17-
"you have multiple different versions of crate `dependency` in your dependency graph",
22+
r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied
23+
--> multiple-dep-versions.rs:7:18
24+
|
25+
7 | do_something(Type);
26+
| ------------ ^^^^ the trait `Trait` is not implemented for `dep_2_reexport::Type`
27+
| |
28+
| required by a bound introduced by this call
29+
|
30+
help: there are multiple different versions of crate `dependency` the your dependency graph
31+
--> multiple-dep-versions.rs:1:1
32+
|
33+
1 | extern crate dep_2_reexport;
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a dependency of crate `foo`
35+
2 | extern crate dependency;
36+
| ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate"#,
37+
)
38+
.assert_stderr_contains(
39+
r#"
40+
3 | pub struct Type(pub i32);
41+
| ^^^^^^^^^^^^^^^ this type implements the required trait
42+
4 | pub trait Trait {
43+
| --------------- this is the required trait"#,
44+
)
45+
.assert_stderr_contains(
46+
r#"
47+
3 | pub struct Type;
48+
| ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#,
49+
)
50+
.assert_stderr_contains(
51+
r#"
52+
error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope
53+
--> multiple-dep-versions.rs:8:10
54+
|
55+
8 | Type.foo();
56+
| ^^^ method not found in `Type`
57+
|
58+
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
59+
)
60+
.assert_stderr_contains(
61+
r#"
62+
4 | pub trait Trait {
63+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
64+
5 | fn foo(&self);
65+
| -------------- the method is available for `dep_2_reexport::Type` here
66+
|
67+
::: multiple-dep-versions.rs:4:32
68+
|
69+
4 | use dependency::{do_something, Trait};
70+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
1871
)
1972
.assert_stderr_contains(
20-
"two types coming from two different versions of the same crate are different types \
21-
even if they look the same",
73+
r#"
74+
4 | pub trait Trait {
75+
| --------------- this is the trait that was imported"#,
2276
)
23-
.assert_stderr_contains("this type doesn't implement the required trait")
24-
.assert_stderr_contains("this type implements the required trait")
25-
.assert_stderr_contains("this is the required trait");
77+
.assert_stderr_contains(
78+
r#"
79+
error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope
80+
--> multiple-dep-versions.rs:9:11
81+
|
82+
9 | Type::bar();
83+
| ^^^ function or associated item not found in `Type`
84+
|
85+
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
86+
)
87+
.assert_stderr_contains(
88+
r#"
89+
4 | pub trait Trait {
90+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
91+
5 | fn foo(&self);
92+
6 | fn bar();
93+
| --------- the associated function is available for `dep_2_reexport::Type` here
94+
|
95+
::: multiple-dep-versions.rs:4:32
96+
|
97+
4 | use dependency::{do_something, Trait};
98+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
99+
);
26100
}

0 commit comments

Comments
 (0)