Skip to content

Commit 5b20c45

Browse files
committed
Auto merge of #128849 - estebank:issue-89143, r=jackh726
Tweak detection of multiple crate versions to be more encompassing Previously, we only emitted the additional context if the type was in the same crate as the trait that appeared multiple times in the dependency tree. Now, we look at all traits looking for two with the same name in different crates with the same crate number, and we are more flexible looking for the types involved. This will work even if the type that implements the wrong trait version is from a different crate entirely. ``` error[E0277]: the trait bound `CustomErrorHandler: ErrorHandler` is not satisfied because the trait comes from a different crate version --> src/main.rs:5:17 | 5 | cnb_runtime(CustomErrorHandler {}); | ^^^^^^^^^^^^^^^^^^^^^ the trait `ErrorHandler` is not implemented for `CustomErrorHandler` | note: there are multiple different versions of crate `c` in the dependency graph --> /home/gh-estebank/testcase-rustc-crate-version-mismatch/c-v0.2/src/lib.rs:1:1 | 1 | pub trait ErrorHandler {} | ^^^^^^^^^^^^^^^^^^^^^^ this is the required trait | ::: src/main.rs:1:5 | 1 | use b::CustomErrorHandler; | - one version of crate `c` is used here, as a dependency of crate `b` 2 | use c::cnb_runtime; | - one version of crate `c` is used here, as a direct dependency of the current crate | ::: /home/gh-estebank/testcase-rustc-crate-version-mismatch/b/src/lib.rs:1:1 | 1 | pub struct CustomErrorHandler {} | ----------------------------- this type doesn't implement the required trait | ::: /home/gh-estebank/testcase-rustc-crate-version-mismatch/c-v0.1/src/lib.rs:1:1 | 1 | pub trait ErrorHandler {} | ---------------------- this is the found trait = note: two types coming from two different versions of the same crate are different types even if they look the same = help: you can use `cargo tree` to explore your dependency tree ``` Fix #89143.
2 parents b91a3a0 + 76c5989 commit 5b20c45

File tree

5 files changed

+137
-102
lines changed

5 files changed

+137
-102
lines changed

Diff for: compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+90-45
Original file line numberDiff line numberDiff line change
@@ -1707,15 +1707,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17071707
// one crate version and the type comes from another crate version, even though they both
17081708
// are from the same crate.
17091709
let trait_def_id = trait_ref.def_id();
1710-
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind()
1711-
&& let found_type = def.did()
1712-
&& trait_def_id.krate != found_type.krate
1713-
&& self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate)
1714-
{
1715-
let name = self.tcx.crate_name(trait_def_id.krate);
1716-
let spans: Vec<_> = [trait_def_id, found_type]
1717-
.into_iter()
1718-
.filter(|def_id| def_id.krate != LOCAL_CRATE)
1710+
let trait_name = self.tcx.item_name(trait_def_id);
1711+
let crate_name = self.tcx.crate_name(trait_def_id.krate);
1712+
if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| {
1713+
trait_name == self.tcx.item_name(trait_def_id)
1714+
&& trait_def_id.krate != def_id.krate
1715+
&& crate_name == self.tcx.crate_name(def_id.krate)
1716+
}) {
1717+
// We've found two different traits with the same name, same crate name, but
1718+
// different crate `DefId`. We highlight the traits.
1719+
1720+
let found_type =
1721+
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() {
1722+
Some(def.did())
1723+
} else {
1724+
None
1725+
};
1726+
let candidates = if impl_candidates.is_empty() {
1727+
alternative_candidates(trait_def_id)
1728+
} else {
1729+
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
1730+
};
1731+
let mut span: MultiSpan = self.tcx.def_span(trait_def_id).into();
1732+
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
1733+
for (sp, label) in [trait_def_id, other_trait_def_id]
1734+
.iter()
17191735
.filter_map(|def_id| self.tcx.extern_crate(def_id.krate))
17201736
.map(|data| {
17211737
let dependency = if data.dependency_of == LOCAL_CRATE {
@@ -1726,57 +1742,86 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17261742
};
17271743
(
17281744
data.span,
1729-
format!("one version of crate `{name}` is used here, as a {dependency}"),
1745+
format!(
1746+
"one version of crate `{crate_name}` is used here, as a {dependency}"
1747+
),
17301748
)
17311749
})
1732-
.collect();
1733-
let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
1734-
for (sp, label) in spans.into_iter() {
1750+
{
17351751
span.push_span_label(sp, label);
17361752
}
1737-
err.highlighted_span_help(span, vec![
1753+
let mut points_at_type = false;
1754+
if let Some(found_type) = found_type {
1755+
span.push_span_label(
1756+
self.tcx.def_span(found_type),
1757+
"this type doesn't implement the required trait",
1758+
);
1759+
for trait_ref in candidates {
1760+
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
1761+
&& let candidate_def_id = def.did()
1762+
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
1763+
&& let Some(found) = self.tcx.opt_item_name(found_type)
1764+
&& name == found
1765+
&& candidate_def_id.krate != found_type.krate
1766+
&& self.tcx.crate_name(candidate_def_id.krate)
1767+
== self.tcx.crate_name(found_type.krate)
1768+
{
1769+
// A candidate was found of an item with the same name, from two separate
1770+
// versions of the same crate, let's clarify.
1771+
let candidate_span = self.tcx.def_span(candidate_def_id);
1772+
span.push_span_label(
1773+
candidate_span,
1774+
"this type implements the required trait",
1775+
);
1776+
points_at_type = true;
1777+
}
1778+
}
1779+
}
1780+
span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait");
1781+
err.highlighted_span_note(span, vec![
17381782
StringPart::normal("there are ".to_string()),
17391783
StringPart::highlighted("multiple different versions".to_string()),
17401784
StringPart::normal(" of crate `".to_string()),
1741-
StringPart::highlighted(format!("{name}")),
1742-
StringPart::normal("` in the dependency graph".to_string()),
1785+
StringPart::highlighted(format!("{crate_name}")),
1786+
StringPart::normal("` in the dependency graph\n".to_string()),
17431787
]);
1744-
let candidates = if impl_candidates.is_empty() {
1745-
alternative_candidates(trait_def_id)
1746-
} else {
1747-
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
1748-
};
1749-
if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| {
1750-
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
1751-
&& let candidate_def_id = def.did()
1752-
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
1753-
&& let Some(found) = self.tcx.opt_item_name(found_type)
1754-
&& name == found
1755-
&& candidate_def_id.krate != found_type.krate
1756-
&& self.tcx.crate_name(candidate_def_id.krate)
1757-
== self.tcx.crate_name(found_type.krate)
1758-
{
1759-
// A candidate was found of an item with the same name, from two separate
1760-
// versions of the same crate, let's clarify.
1761-
Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type)))
1762-
} else {
1763-
None
1764-
}
1765-
}) {
1766-
let mut span: MultiSpan = vec![sp_candidate, sp_found].into();
1767-
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
1768-
span.push_span_label(sp_candidate, "this type implements the required trait");
1769-
span.push_span_label(sp_found, "this type doesn't implement the required trait");
1770-
err.highlighted_span_note(span, vec![
1788+
if points_at_type {
1789+
// We only clarify that the same type from different crate versions are not the
1790+
// same when we *find* the same type coming from different crate versions, otherwise
1791+
// it could be that it was a type provided by a different crate than the one that
1792+
// provides the trait, and mentioning this adds verbosity without clarification.
1793+
err.highlighted_note(vec![
17711794
StringPart::normal(
17721795
"two types coming from two different versions of the same crate are \
1773-
different types "
1796+
different types "
17741797
.to_string(),
17751798
),
17761799
StringPart::highlighted("even if they look the same".to_string()),
17771800
]);
17781801
}
1779-
err.help("you can use `cargo tree` to explore your dependency tree");
1802+
err.highlighted_help(vec![
1803+
StringPart::normal("you can use `".to_string()),
1804+
StringPart::highlighted("cargo tree".to_string()),
1805+
StringPart::normal("` to explore your dependency tree".to_string()),
1806+
]);
1807+
1808+
// FIXME: this is a giant hack for the benefit of this specific diagnostic. Because
1809+
// we're so nested in method calls before the error gets emitted, bubbling a single bit
1810+
// flag informing the top level caller to stop adding extra detail to the diagnostic,
1811+
// would actually be harder to follow. So we do something naughty here: we consume the
1812+
// diagnostic, emit it and leave in its place a "delayed bug" that will continue being
1813+
// modified but won't actually be printed to end users. This *is not ideal*, but allows
1814+
// us to reduce the verbosity of an error that is already quite verbose and increase its
1815+
// specificity. Below we modify the main message as well, in a way that *could* break if
1816+
// the implementation of Diagnostics change significantly, but that would be caught with
1817+
// a make test failure when this diagnostic is tested.
1818+
err.primary_message(format!(
1819+
"{} because the trait comes from a different crate version",
1820+
err.messages[0].0.as_str().unwrap(),
1821+
));
1822+
let diag = err.clone();
1823+
err.downgrade_to_delayed_bug();
1824+
self.tcx.dcx().emit_diagnostic(diag);
17801825
return true;
17811826
}
17821827

Diff for: tests/run-make/crate-loading/multiple-dep-versions-3.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@
33

44
extern crate dependency;
55
pub use dependency::Type;
6+
pub struct OtherType;
7+
impl dependency::Trait for OtherType {
8+
fn foo(&self) {}
9+
fn bar() {}
10+
}
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
extern crate dep_2_reexport;
22
extern crate dependency;
3-
use dep_2_reexport::Type;
3+
use dep_2_reexport::{OtherType, Type};
44
use dependency::{Trait, do_something};
55

66
fn main() {
77
do_something(Type);
88
Type.foo();
99
Type::bar();
10+
do_something(OtherType);
1011
}

Diff for: tests/run-make/crate-loading/rmake.rs

+39-50
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,39 @@ 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-
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` in the 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
21+
.assert_stderr_contains(r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied because the trait comes from a different crate version
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+
note: there are multiple different versions of crate `dependency` in the dependency graph"#)
28+
.assert_stderr_contains(r#"
29+
3 | pub struct Type(pub i32);
30+
| --------------- this type implements the required trait
31+
4 | pub trait Trait {
32+
| ^^^^^^^^^^^^^^^ this is the required trait
33+
"#)
34+
.assert_stderr_contains(r#"
35+
1 | extern crate dep_2_reexport;
36+
| ---------------------------- one version of crate `dependency` is used here, as a dependency of crate `foo`
37+
2 | extern crate dependency;
38+
| ------------------------ one version of crate `dependency` is used here, as a direct dependency of the current crate"#)
39+
.assert_stderr_contains(r#"
40+
3 | pub struct Type;
41+
| --------------- this type doesn't implement the required trait
42+
4 | pub trait Trait {
43+
| --------------- this is the found trait
44+
= note: two types coming from two different versions of the same crate are different types even if they look the same
45+
= help: you can use `cargo tree` to explore your dependency tree"#)
46+
.assert_stderr_contains(r#"error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope
5347
--> multiple-dep-versions.rs:8:10
5448
|
5549
8 | Type.foo();
5650
| ^^^ method not found in `Type`
5751
|
58-
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
59-
)
60-
.assert_stderr_contains(
61-
r#"
52+
note: there are multiple different versions of crate `dependency` in the dependency graph"#)
53+
.assert_stderr_contains(r#"
6254
4 | pub trait Trait {
6355
| ^^^^^^^^^^^^^^^ this is the trait that is needed
6456
5 | fn foo(&self);
@@ -67,25 +59,19 @@ note: there are multiple different versions of crate `dependency` in the depende
6759
::: multiple-dep-versions.rs:4:18
6860
|
6961
4 | use dependency::{Trait, do_something};
70-
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
71-
)
72-
.assert_stderr_contains(
73-
r#"
62+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#)
63+
.assert_stderr_contains(r#"
7464
4 | pub trait Trait {
75-
| --------------- this is the trait that was imported"#,
76-
)
77-
.assert_stderr_contains(
78-
r#"
65+
| --------------- this is the trait that was imported"#)
66+
.assert_stderr_contains(r#"
7967
error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope
8068
--> multiple-dep-versions.rs:9:11
8169
|
8270
9 | Type::bar();
8371
| ^^^ function or associated item not found in `Type`
8472
|
85-
note: there are multiple different versions of crate `dependency` in the dependency graph"#,
86-
)
87-
.assert_stderr_contains(
88-
r#"
73+
note: there are multiple different versions of crate `dependency` in the dependency graph"#)
74+
.assert_stderr_contains(r#"
8975
4 | pub trait Trait {
9076
| ^^^^^^^^^^^^^^^ this is the trait that is needed
9177
5 | fn foo(&self);
@@ -95,6 +81,9 @@ note: there are multiple different versions of crate `dependency` in the depende
9581
::: multiple-dep-versions.rs:4:18
9682
|
9783
4 | use dependency::{Trait, do_something};
98-
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
99-
);
84+
| ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#)
85+
.assert_stderr_contains(
86+
r#"
87+
6 | pub struct OtherType;
88+
| -------------------- this type doesn't implement the required trait"#);
10089
}

Diff for: tests/ui/typeck/foreign_struct_trait_unimplemented.stderr

+1-6
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ LL | needs_test(foreign_struct_trait_unimplemented::B);
66
| |
77
| required by a bound introduced by this call
88
|
9-
help: there are multiple different versions of crate `foreign_struct_trait_unimplemented` in the dependency graph
10-
--> $DIR/foreign_struct_trait_unimplemented.rs:3:1
11-
|
12-
LL | extern crate foreign_struct_trait_unimplemented;
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `foreign_struct_trait_unimplemented` is used here, as a direct dependency of the current crate
14-
= help: you can use `cargo tree` to explore your dependency tree
9+
= help: the trait `Test` is implemented for `A`
1510
note: required by a bound in `needs_test`
1611
--> $DIR/foreign_struct_trait_unimplemented.rs:10:23
1712
|

0 commit comments

Comments
 (0)