Skip to content

Commit 882f4a4

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

File tree

5 files changed

+115
-65
lines changed

5 files changed

+115
-65
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

+63-16
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,67 @@ fn main() {
1818
.extern_("dependency", rust_lib_name("dependency"))
1919
.extern_("dep_2_reexport", rust_lib_name("foo"))
2020
.run_fail()
21-
.assert_stderr_contains(
22-
"there are multiple different versions of crate `dependency` in the dependency graph",
23-
)
24-
.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",
27-
)
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")
31-
.assert_stderr_contains(
32-
"`dependency` imported here doesn't correspond to the right crate version",
33-
)
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");
21+
.assert_stderr_contains(r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied
22+
--> multiple-dep-versions.rs:7:18
23+
|
24+
7 | do_something(Type);
25+
| ------------ ^^^^ the trait `Trait` is not implemented for `dep_2_reexport::Type`
26+
| |
27+
| required by a bound introduced by this call
28+
|
29+
help: there are multiple different versions of crate `dependency` the your dependency graph
30+
--> multiple-dep-versions.rs:1:1
31+
|
32+
1 | extern crate dep_2_reexport;
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a dependency of crate `foo`
34+
2 | extern crate dependency;
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate"#)
36+
.assert_stderr_contains(r#"
37+
3 | pub struct Type(pub i32);
38+
| ^^^^^^^^^^^^^^^ this type implements the required trait
39+
4 | pub trait Trait {
40+
| --------------- this is the required trait"#)
41+
.assert_stderr_contains(r#"
42+
3 | pub struct Type;
43+
| ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#)
44+
.assert_stderr_contains(r#"
45+
error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope
46+
--> multiple-dep-versions.rs:8:10
47+
|
48+
8 | Type.foo();
49+
| ^^^ method not found in `Type`
50+
|
51+
note: there are multiple different versions of crate `dependency` in the dependency graph"#)
52+
.assert_stderr_contains(r#"
53+
4 | pub trait Trait {
54+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
55+
5 | fn foo(&self);
56+
| -------------- the method is available for `dep_2_reexport::Type` here
57+
|
58+
::: multiple-dep-versions.rs:4:32
59+
|
60+
4 | use dependency::{do_something, Trait};
61+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#)
62+
.assert_stderr_contains(r#"
63+
4 | pub trait Trait {
64+
| --------------- this is the trait that was imported"#)
65+
.assert_stderr_contains(r#"
66+
error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope
67+
--> multiple-dep-versions.rs:9:11
68+
|
69+
9 | Type::bar();
70+
| ^^^ function or associated item not found in `Type`
71+
|
72+
note: there are multiple different versions of crate `dependency` in the dependency graph"#)
73+
.assert_stderr_contains(r#"
74+
4 | pub trait Trait {
75+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
76+
5 | fn foo(&self);
77+
6 | fn bar();
78+
| --------- the method is available for `dep_2_reexport::Type` here
79+
|
80+
::: multiple-dep-versions.rs:4:32
81+
|
82+
4 | use dependency::{do_something, Trait};
83+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#);
3784
}

0 commit comments

Comments
 (0)