Skip to content

Commit 22bcd5c

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

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
@@ -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

+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::rfs::copy;
67
use run_make_support::{assert_contains, rust_lib_name, rustc};
@@ -19,19 +20,82 @@ fn main() {
1920
.extern_("dep_2_reexport", rust_lib_name("foo"))
2021
.run_fail()
2122
.assert_stderr_contains(
22-
"there are multiple different versions of crate `dependency` in the dependency graph",
23+
r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is 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 of crate `foo`
36+
2 | extern crate dependency;
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate"#,
2338
)
2439
.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",
40+
r#"
41+
3 | pub struct Type(pub i32);
42+
| ^^^^^^^^^^^^^^^ this type implements the required trait
43+
4 | pub trait Trait {
44+
| --------------- this is the required trait"#,
2745
)
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")
3146
.assert_stderr_contains(
32-
"`dependency` imported here doesn't correspond to the right crate version",
47+
r#"
48+
3 | pub struct Type;
49+
| ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#,
3350
)
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");
51+
.assert_stderr_contains(
52+
r#"
53+
error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope
54+
--> multiple-dep-versions.rs:8:10
55+
|
56+
8 | Type.foo();
57+
| ^^^ method not found in `Type`
58+
|
59+
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
60+
)
61+
.assert_stderr_contains(
62+
r#"
63+
4 | pub trait Trait {
64+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
65+
5 | fn foo(&self);
66+
| -------------- the method is available for `dep_2_reexport::Type` here
67+
|
68+
::: multiple-dep-versions.rs:4:32
69+
|
70+
4 | use dependency::{do_something, Trait};
71+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
72+
)
73+
.assert_stderr_contains(
74+
r#"
75+
4 | pub trait Trait {
76+
| --------------- this is the trait that was imported"#,
77+
)
78+
.assert_stderr_contains(
79+
r#"
80+
error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope
81+
--> multiple-dep-versions.rs:9:11
82+
|
83+
9 | Type::bar();
84+
| ^^^ function or associated item not found in `Type`
85+
|
86+
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
87+
)
88+
.assert_stderr_contains(
89+
r#"
90+
4 | pub trait Trait {
91+
| ^^^^^^^^^^^^^^^ this is the trait that is needed
92+
5 | fn foo(&self);
93+
6 | fn bar();
94+
| --------- the method is available for `dep_2_reexport::Type` here
95+
|
96+
::: multiple-dep-versions.rs:4:32
97+
|
98+
4 | use dependency::{do_something, Trait};
99+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
100+
);
37101
}

0 commit comments

Comments
 (0)