Skip to content

Commit 6105893

Browse files
committed
Rework suggestion method
Make checking slightly cheaper (by restricting to the right item only). Add tests.
1 parent 4e98553 commit 6105893

File tree

5 files changed

+126
-59
lines changed

5 files changed

+126
-59
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

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

41354133
/// 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

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

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

@@ -18,19 +19,82 @@ fn main() {
1819
.extern_("dep_2_reexport", rust_lib_name("foo"))
1920
.run_fail()
2021
.assert_stderr_contains(
21-
"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 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"#,
2237
)
2338
.assert_stderr_contains(
24-
"two types coming from two different versions of the same crate are different types \
25-
even if they look the same",
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"#,
2644
)
27-
.assert_stderr_contains("this type doesn't implement the required trait")
28-
.assert_stderr_contains("this type implements the required trait")
29-
.assert_stderr_contains("this is the required trait")
3045
.assert_stderr_contains(
31-
"`dependency` imported here doesn't correspond to the right crate version",
46+
r#"
47+
3 | pub struct Type;
48+
| ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#,
3249
)
33-
.assert_stderr_contains("this is the trait that was imported")
34-
.assert_stderr_contains("this is the trait that is needed")
35-
.assert_stderr_contains("the method is available for `dep_2_reexport::Type` here");
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`"#,
71+
)
72+
.assert_stderr_contains(
73+
r#"
74+
4 | pub trait Trait {
75+
| --------------- this is the trait that was imported"#,
76+
)
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 method 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+
);
36100
}

0 commit comments

Comments
 (0)