Skip to content

Commit a71f4d1

Browse files
committed
Tweak detection of multiple crate versions to be more ecompassing
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 --> src/main.rs:5:17 | 5 | cnb_runtime(CustomErrorHandler {}); | ----------- ^^^^^^^^^^^^^^^^^^^^^ the trait `ErrorHandler` is not implemented for `CustomErrorHandler` | | | required by a bound introduced by this call | help: you have multiple different versions of crate `ErrorHandler` in your dependency graph --> src/main.rs:1:5 | 1 | use b::CustomErrorHandler; | ^ one version of crate `ErrorHandler` is used here, as a dependency of crate `b` 2 | use c::cnb_runtime; | ^ one version of crate `ErrorHandler` is used here, as a direct dependency of the current crate note: two types coming from two different versions of the same crate are different types even if they look the same --> /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 | ::: /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 = help: you can use `cargo tree` to explore your dependency tree note: required by a bound in `cnb_runtime` --> /home/gh-estebank/testcase-rustc-crate-version-mismatch/c-v0.2/src/lib.rs:3:41 | 3 | pub fn cnb_runtime(_error_handler: impl ErrorHandler) {} | ^^^^^^^^^^^^ required by this bound in `cnb_runtime` ``` Fix #89143.
1 parent d3a3939 commit a71f4d1

File tree

1 file changed

+60
-42
lines changed

1 file changed

+60
-42
lines changed

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+60-42
Original file line numberDiff line numberDiff line change
@@ -1669,15 +1669,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16691669
// one crate version and the type comes from another crate version, even though they both
16701670
// are from the same crate.
16711671
let trait_def_id = trait_ref.def_id();
1672-
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind()
1673-
&& let found_type = def.did()
1674-
&& trait_def_id.krate != found_type.krate
1675-
&& self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate)
1676-
{
1677-
let name = self.tcx.crate_name(trait_def_id.krate);
1678-
let spans: Vec<_> = [trait_def_id, found_type]
1679-
.into_iter()
1680-
.filter_map(|def_id| self.tcx.extern_crate(def_id))
1672+
let trait_name = self.tcx.item_name(trait_def_id);
1673+
let crate_name = self.tcx.crate_name(trait_def_id.krate);
1674+
if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| {
1675+
trait_name == self.tcx.item_name(trait_def_id)
1676+
&& trait_def_id.krate != def_id.krate
1677+
&& crate_name == self.tcx.crate_name(def_id.krate)
1678+
}) {
1679+
// We've found two different traits with the same name, same crate name, but
1680+
// different crate `DefId`. We highlight the traits.
1681+
1682+
let found_type =
1683+
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() {
1684+
Some(def.did())
1685+
} else {
1686+
None
1687+
};
1688+
let spans: Vec<_> = [trait_def_id, other_trait_def_id]
1689+
.iter()
1690+
.filter_map(|def_id| self.tcx.extern_crate(*def_id))
16811691
.map(|data| {
16821692
let dependency = if data.dependency_of == LOCAL_CRATE {
16831693
"direct dependency of the current crate".to_string()
@@ -1687,7 +1697,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16871697
};
16881698
(
16891699
data.span,
1690-
format!("one version of crate `{name}` is used here, as a {dependency}"),
1700+
format!(
1701+
"one version of crate `{trait_name}` is used here, as a {dependency}"
1702+
),
16911703
)
16921704
})
16931705
.collect();
@@ -1701,7 +1713,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17011713
StringPart::normal("you have ".to_string()),
17021714
StringPart::highlighted("multiple different versions".to_string()),
17031715
StringPart::normal(" of crate `".to_string()),
1704-
StringPart::highlighted(format!("{name}")),
1716+
StringPart::highlighted(format!("{crate_name}")),
17051717
StringPart::normal("` in your dependency graph".to_string()),
17061718
],
17071719
);
@@ -1710,39 +1722,45 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17101722
} else {
17111723
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
17121724
};
1713-
if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| {
1714-
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
1715-
&& let candidate_def_id = def.did()
1716-
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
1717-
&& let Some(found) = self.tcx.opt_item_name(found_type)
1718-
&& name == found
1719-
&& candidate_def_id.krate != found_type.krate
1720-
&& self.tcx.crate_name(candidate_def_id.krate)
1721-
== self.tcx.crate_name(found_type.krate)
1722-
{
1723-
// A candidate was found of an item with the same name, from two separate
1724-
// versions of the same crate, let's clarify.
1725-
Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type)))
1726-
} else {
1727-
None
1728-
}
1729-
}) {
1730-
let mut span: MultiSpan = vec![sp_candidate, sp_found].into();
1731-
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
1732-
span.push_span_label(sp_candidate, "this type implements the required trait");
1733-
span.push_span_label(sp_found, "this type doesn't implement the required trait");
1734-
err.highlighted_span_note(
1735-
span,
1736-
vec![
1737-
StringPart::normal(
1738-
"two types coming from two different versions of the same crate are \
1739-
different types "
1740-
.to_string(),
1741-
),
1742-
StringPart::highlighted("even if they look the same".to_string()),
1743-
],
1725+
let mut span: MultiSpan = self.tcx.def_span(trait_def_id).into();
1726+
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
1727+
if let Some(found_type) = found_type {
1728+
span.push_span_label(
1729+
self.tcx.def_span(found_type),
1730+
"this type doesn't implement the required trait",
17441731
);
1732+
for trait_ref in candidates {
1733+
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
1734+
&& let candidate_def_id = def.did()
1735+
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
1736+
&& let Some(found) = self.tcx.opt_item_name(found_type)
1737+
&& name == found
1738+
&& candidate_def_id.krate != found_type.krate
1739+
&& self.tcx.crate_name(candidate_def_id.krate)
1740+
== self.tcx.crate_name(found_type.krate)
1741+
{
1742+
// A candidate was found of an item with the same name, from two separate
1743+
// versions of the same crate, let's clarify.
1744+
let candidate_span = self.tcx.def_span(candidate_def_id);
1745+
span.push_span_label(
1746+
candidate_span,
1747+
"this type implements the required trait",
1748+
);
1749+
}
1750+
}
17451751
}
1752+
span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait");
1753+
err.highlighted_span_note(
1754+
span,
1755+
vec![
1756+
StringPart::normal(
1757+
"two types coming from two different versions of the same crate are \
1758+
different types "
1759+
.to_string(),
1760+
),
1761+
StringPart::highlighted("even if they look the same".to_string()),
1762+
],
1763+
);
17461764
err.help("you can use `cargo tree` to explore your dependency tree");
17471765
return true;
17481766
}

0 commit comments

Comments
 (0)