Skip to content

Commit 515a88b

Browse files
committed
Rework suggestion method
Make checking slightly cheaper (by restricting to the right item only). Add tests.
1 parent 5c537df commit 515a88b

File tree

5 files changed

+131
-59
lines changed

5 files changed

+131
-59
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+47-49
Original file line numberDiff line numberDiff line change
@@ -3498,8 +3498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34983498
.detect_and_explain_multiple_crate_versions(
34993499
err,
35003500
pick.item.def_id,
3501-
pick.item.ident(self.tcx).span,
3502-
rcvr.hir_id.owner,
3501+
rcvr.hir_id,
35033502
*rcvr_ty,
35043503
);
35053504
if pick.autoderefs == 0 && !trait_in_other_version_found {
@@ -3706,8 +3705,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
37063705
trait_in_other_version_found = self.detect_and_explain_multiple_crate_versions(
37073706
err,
37083707
assoc.def_id,
3709-
self.tcx.def_span(assoc.def_id),
3710-
ty.hir_id.owner,
3708+
ty.hir_id,
37113709
rcvr_ty,
37123710
);
37133711
}
@@ -4082,55 +4080,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
40824080
&self,
40834081
err: &mut Diag<'_>,
40844082
item_def_id: DefId,
4085-
item_span: Span,
4086-
owner: hir::OwnerId,
4083+
hir_id: hir::HirId,
40874084
rcvr_ty: Ty<'_>,
40884085
) -> bool {
4089-
let pick_name = self.tcx.crate_name(item_def_id.krate);
4090-
let trait_did = self.tcx.parent(item_def_id);
4091-
let trait_name = self.tcx.item_name(trait_did);
4092-
if let Some(map) = self.tcx.in_scope_traits_map(owner) {
4093-
for trait_candidate in map.to_sorted_stable_ord().into_iter().flat_map(|v| v.1.iter()) {
4094-
let crate_name = self.tcx.crate_name(trait_candidate.def_id.krate);
4095-
if trait_candidate.def_id.krate != item_def_id.krate && crate_name == pick_name {
4096-
let msg = format!(
4097-
"there are multiple different versions of crate `{crate_name}` in the \
4098-
dependency graph",
4099-
);
4100-
let candidate_name = self.tcx.item_name(trait_candidate.def_id);
4101-
if candidate_name == trait_name
4102-
&& let Some(def_id) = trait_candidate.import_ids.get(0)
4103-
{
4104-
let span = self.tcx.def_span(*def_id);
4105-
let mut multi_span: MultiSpan = span.into();
4106-
multi_span.push_span_label(
4107-
span,
4108-
format!(
4109-
"`{crate_name}` imported here doesn't correspond to the right \
4110-
crate version",
4111-
),
4112-
);
4113-
multi_span.push_span_label(
4114-
self.tcx.def_span(trait_candidate.def_id),
4115-
format!("this is the trait that was imported"),
4116-
);
4117-
multi_span.push_span_label(
4118-
self.tcx.def_span(trait_did),
4119-
format!("this is the trait that is needed"),
4120-
);
4121-
multi_span.push_span_label(
4122-
item_span,
4123-
format!("the method is available for `{rcvr_ty}` here"),
4124-
);
4125-
err.span_note(multi_span, msg);
4126-
} else {
4127-
err.note(msg);
4128-
}
4129-
return true;
4130-
}
4086+
let hir_id = self.tcx.parent_hir_id(hir_id);
4087+
let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
4088+
if traits.is_empty() {
4089+
return false;
4090+
}
4091+
let trait_def_id = self.tcx.parent(item_def_id);
4092+
let krate = self.tcx.crate_name(trait_def_id.krate);
4093+
let name = self.tcx.item_name(trait_def_id);
4094+
let candidates: Vec<_> = traits
4095+
.iter()
4096+
.filter(|c| {
4097+
c.def_id.krate != trait_def_id.krate
4098+
&& self.tcx.crate_name(c.def_id.krate) == krate
4099+
&& self.tcx.item_name(c.def_id) == name
4100+
})
4101+
.map(|c| (c.def_id, c.import_ids.get(0).cloned()))
4102+
.collect();
4103+
if candidates.is_empty() {
4104+
return false;
4105+
}
4106+
let item_span = self.tcx.def_span(item_def_id);
4107+
let msg = format!(
4108+
"there are multiple different versions of crate `{krate}` in the dependency graph",
4109+
);
4110+
let trait_span = self.tcx.def_span(trait_def_id);
4111+
let mut multi_span: MultiSpan = trait_span.into();
4112+
multi_span.push_span_label(trait_span, format!("this is the trait that is needed"));
4113+
multi_span
4114+
.push_span_label(item_span, format!("the method 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+
);
41314124
}
4125+
multi_span.push_span_label(
4126+
self.tcx.def_span(def_id),
4127+
format!("this is the trait that was imported"),
4128+
);
41324129
}
4133-
false
4130+
err.span_note(multi_span, msg);
4131+
true
41344132
}
41354133

41364134
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`

tests/run-make/crate-loading/multiple-dep-versions-1.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
pub struct Type(pub i32);
44
pub trait Trait {
55
fn foo(&self);
6+
fn bar();
67
}
78
impl Trait for Type {
89
fn foo(&self) {}
10+
fn bar() {}
911
}
1012
pub fn do_something<X: Trait>(_: X) {}

tests/run-make/crate-loading/multiple-dep-versions-2.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
pub struct Type;
44
pub trait Trait {
55
fn foo(&self);
6+
fn bar();
67
}
78
impl Trait for Type {
89
fn foo(&self) {}
10+
fn bar() {}
911
}
1012
pub fn do_something<X: Trait>(_: X) {}

tests/run-make/crate-loading/multiple-dep-versions.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ use dependency::{do_something, Trait};
66
fn main() {
77
do_something(Type);
88
Type.foo();
9+
Type::bar();
910
}

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

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

0 commit comments

Comments
 (0)