Skip to content

Commit 7c7dc22

Browse files
committed
Add a note when a type implements a trait with the same name as the required one
This is useful when you have two dependencies that use different trait for the same thing and with the same name. The user can accidentally implement the bad one which might be confusing. This commits refactorizes existing diagnostics about multiple different crates with the same version and adds a note when similarly named traits are found. Both diagnostics are merged into a single function.
1 parent 4fd3181 commit 7c7dc22

File tree

11 files changed

+263
-246
lines changed

11 files changed

+263
-246
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,7 +4089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
40894089
err: &mut Diag<'_>,
40904090
item_def_id: DefId,
40914091
hir_id: hir::HirId,
4092-
rcvr_ty: Option<Ty<'_>>,
4092+
rcvr_ty: Option<Ty<'tcx>>,
40934093
) -> bool {
40944094
let hir_id = self.tcx.parent_hir_id(hir_id);
40954095
let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
@@ -4100,49 +4100,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
41004100
if !self.tcx.is_trait(trait_def_id) {
41014101
return false;
41024102
}
4103-
let krate = self.tcx.crate_name(trait_def_id.krate);
4104-
let name = self.tcx.item_name(trait_def_id);
4105-
let candidates: Vec<_> = traits
4106-
.iter()
4107-
.filter(|c| {
4108-
c.def_id.krate != trait_def_id.krate
4109-
&& self.tcx.crate_name(c.def_id.krate) == krate
4110-
&& self.tcx.item_name(c.def_id) == name
4111-
})
4112-
.map(|c| (c.def_id, c.import_ids.get(0).cloned()))
4113-
.collect();
4114-
if candidates.is_empty() {
4103+
let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else {
41154104
return false;
4116-
}
4117-
let item_span = self.tcx.def_span(item_def_id);
4118-
let msg = format!(
4119-
"there are multiple different versions of crate `{krate}` in the dependency graph",
4120-
);
4121-
let trait_span = self.tcx.def_span(trait_def_id);
4122-
let mut multi_span: MultiSpan = trait_span.into();
4123-
multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string());
4124-
let descr = self.tcx.associated_item(item_def_id).descr();
4125-
let rcvr_ty =
4126-
rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string());
4127-
multi_span
4128-
.push_span_label(item_span, format!("the {descr} is available for {rcvr_ty} here"));
4129-
for (def_id, import_def_id) in candidates {
4130-
if let Some(import_def_id) = import_def_id {
4131-
multi_span.push_span_label(
4132-
self.tcx.def_span(import_def_id),
4133-
format!(
4134-
"`{name}` imported here doesn't correspond to the right version of crate \
4135-
`{krate}`",
4136-
),
4137-
);
4138-
}
4139-
multi_span.push_span_label(
4140-
self.tcx.def_span(def_id),
4141-
"this is the trait that was imported".to_string(),
4142-
);
4143-
}
4144-
err.span_note(multi_span, msg);
4145-
true
4105+
};
4106+
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter());
4107+
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
4108+
trait_ref,
4109+
polarity: ty::PredicatePolarity::Positive,
4110+
});
4111+
let obligation = Obligation::new(self.tcx, self.misc(rcvr.span), self.param_env, trait_ref);
4112+
self.err_ctxt().note_different_trait_with_same_name(err, &obligation, trait_pred)
41464113
}
41474114

41484115
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`

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

Lines changed: 117 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
467467
span,
468468
leaf_trait_predicate,
469469
);
470-
self.note_trait_version_mismatch(&mut err, leaf_trait_predicate);
470+
self.note_different_trait_with_same_name(&mut err, &obligation, leaf_trait_predicate);
471471
self.note_adt_version_mismatch(&mut err, leaf_trait_predicate);
472472
self.suggest_remove_await(&obligation, &mut err);
473473
self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);
@@ -1949,115 +1949,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19491949
impl_candidates
19501950
};
19511951

1952-
// We'll check for the case where the reason for the mismatch is that the trait comes from
1953-
// one crate version and the type comes from another crate version, even though they both
1954-
// are from the same crate.
1955-
let trait_def_id = trait_pred.def_id();
1956-
let trait_name = self.tcx.item_name(trait_def_id);
1957-
let crate_name = self.tcx.crate_name(trait_def_id.krate);
1958-
if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
1959-
trait_name == self.tcx.item_name(trait_def_id)
1960-
&& trait_def_id.krate != def_id.krate
1961-
&& crate_name == self.tcx.crate_name(def_id.krate)
1962-
}) {
1963-
// We've found two different traits with the same name, same crate name, but
1964-
// different crate `DefId`. We highlight the traits.
1965-
1966-
let found_type =
1967-
if let ty::Adt(def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() {
1968-
Some(def.did())
1969-
} else {
1970-
None
1971-
};
1972-
let candidates = if impl_candidates.is_empty() {
1973-
alternative_candidates(trait_def_id)
1974-
} else {
1975-
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
1976-
};
1977-
let mut span: MultiSpan = self.tcx.def_span(trait_def_id).into();
1978-
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
1979-
for (sp, label) in [trait_def_id, other_trait_def_id]
1980-
.iter()
1981-
// The current crate-version might depend on another version of the same crate
1982-
// (Think "semver-trick"). Do not call `extern_crate` in that case for the local
1983-
// crate as that doesn't make sense and ICEs (#133563).
1984-
.filter(|def_id| !def_id.is_local())
1985-
.filter_map(|def_id| self.tcx.extern_crate(def_id.krate))
1986-
.map(|data| {
1987-
let dependency = if data.dependency_of == LOCAL_CRATE {
1988-
"direct dependency of the current crate".to_string()
1989-
} else {
1990-
let dep = self.tcx.crate_name(data.dependency_of);
1991-
format!("dependency of crate `{dep}`")
1992-
};
1993-
(
1994-
data.span,
1995-
format!("one version of crate `{crate_name}` used here, as a {dependency}"),
1996-
)
1997-
})
1998-
{
1999-
span.push_span_label(sp, label);
2000-
}
2001-
let mut points_at_type = false;
2002-
if let Some(found_type) = found_type {
2003-
span.push_span_label(
2004-
self.tcx.def_span(found_type),
2005-
"this type doesn't implement the required trait",
2006-
);
2007-
for trait_ref in candidates {
2008-
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
2009-
&& let candidate_def_id = def.did()
2010-
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
2011-
&& let Some(found) = self.tcx.opt_item_name(found_type)
2012-
&& name == found
2013-
&& candidate_def_id.krate != found_type.krate
2014-
&& self.tcx.crate_name(candidate_def_id.krate)
2015-
== self.tcx.crate_name(found_type.krate)
2016-
{
2017-
// A candidate was found of an item with the same name, from two separate
2018-
// versions of the same crate, let's clarify.
2019-
let candidate_span = self.tcx.def_span(candidate_def_id);
2020-
span.push_span_label(
2021-
candidate_span,
2022-
"this type implements the required trait",
2023-
);
2024-
points_at_type = true;
2025-
}
2026-
}
2027-
}
2028-
span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait");
2029-
err.highlighted_span_note(
2030-
span,
2031-
vec![
2032-
StringPart::normal("there are ".to_string()),
2033-
StringPart::highlighted("multiple different versions".to_string()),
2034-
StringPart::normal(" of crate `".to_string()),
2035-
StringPart::highlighted(format!("{crate_name}")),
2036-
StringPart::normal("` in the dependency graph".to_string()),
2037-
],
2038-
);
2039-
if points_at_type {
2040-
// We only clarify that the same type from different crate versions are not the
2041-
// same when we *find* the same type coming from different crate versions, otherwise
2042-
// it could be that it was a type provided by a different crate than the one that
2043-
// provides the trait, and mentioning this adds verbosity without clarification.
2044-
err.highlighted_note(vec![
2045-
StringPart::normal(
2046-
"two types coming from two different versions of the same crate are \
2047-
different types "
2048-
.to_string(),
2049-
),
2050-
StringPart::highlighted("even if they look the same".to_string()),
2051-
]);
2052-
}
2053-
err.highlighted_help(vec![
2054-
StringPart::normal("you can use `".to_string()),
2055-
StringPart::highlighted("cargo tree".to_string()),
2056-
StringPart::normal("` to explore your dependency tree".to_string()),
2057-
]);
2058-
return true;
2059-
}
2060-
20611952
if let [single] = &impl_candidates {
20621953
// If we have a single implementation, try to unify it with the trait ref
20631954
// that failed. This should uncover a better hint for what *is* implemented.
@@ -2422,10 +2313,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24222313
}
24232314
}
24242315

2425-
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
2426-
/// with the same path as `trait_ref`, a help message about
2427-
/// a probable version mismatch is added to `err`
2428-
fn note_trait_version_mismatch(
2316+
fn check_same_trait_different_version(
24292317
&self,
24302318
err: &mut Diag<'_>,
24312319
trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -2442,40 +2330,69 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24422330
trait_impls
24432331
};
24442332

2333+
let krate = self.tcx.crate_name(trait_pred.def_id().krate);
2334+
let name = self.tcx.item_name(trait_pred.def_id());
24452335
let required_trait_path = self.tcx.def_path_str(trait_pred.def_id());
24462336
let traits_with_same_path: UnordSet<_> = self
24472337
.tcx
24482338
.visible_traits()
2449-
.filter(|trait_def_id| *trait_def_id != trait_pred.def_id())
2339+
.filter(|trait_def_id| {
2340+
trait_def_id.krate != trait_pred.def_id().krate
2341+
&& (self.tcx.def_path_str(trait_def_id) == required_trait_path
2342+
|| self.tcx.crate_name(trait_def_id.krate) == krate
2343+
&& self.tcx.item_name(trait_def_id) == name)
2344+
})
24502345
.map(|trait_def_id| (self.tcx.def_path_str(trait_def_id), trait_def_id))
2451-
.filter(|(p, _)| *p == required_trait_path)
24522346
.collect();
24532347

24542348
let traits_with_same_path =
24552349
traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p);
24562350
let mut suggested = false;
2457-
for (_, trait_with_same_path) in traits_with_same_path {
2458-
let trait_impls = get_trait_impls(trait_with_same_path);
2459-
if trait_impls.is_empty() {
2460-
continue;
2461-
}
2462-
let impl_spans: Vec<_> =
2463-
trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect();
2464-
err.span_help(
2465-
impl_spans,
2466-
format!("trait impl{} with same name found", pluralize!(trait_impls.len())),
2351+
let mut trait_is_impl = false;
2352+
2353+
if !traits_with_same_path.is_empty() {
2354+
let mut span: MultiSpan = self.tcx.def_span(trait_pred.def_id()).into();
2355+
span.push_span_label(
2356+
self.tcx.def_span(trait_pred.def_id()),
2357+
"this is the required trait",
24672358
);
2468-
self.note_two_crate_versions(trait_with_same_path, err);
24692359
suggested = true;
2360+
for (_, trait_with_same_path) in &traits_with_same_path {
2361+
let trait_impls = get_trait_impls(*trait_with_same_path);
2362+
if trait_impls.is_empty() {
2363+
continue;
2364+
}
2365+
2366+
for candidate_def_id in trait_impls {
2367+
let Some(impl_trait_header) = self.tcx.impl_trait_header(candidate_def_id)
2368+
else {
2369+
continue;
2370+
};
2371+
let candidate_span =
2372+
self.tcx.def_span(impl_trait_header.trait_ref.skip_binder().def_id);
2373+
span.push_span_label(candidate_span, "this is the implemented trait");
2374+
trait_is_impl = true;
2375+
}
2376+
}
2377+
if !trait_is_impl {
2378+
for (_, def_id) in traits_with_same_path {
2379+
span.push_span_label(
2380+
self.tcx.def_span(def_id),
2381+
"this is the trait that was imported",
2382+
);
2383+
}
2384+
}
2385+
self.note_two_crate_versions(trait_pred.def_id(), span, err);
24702386
}
24712387
suggested
24722388
}
24732389

2474-
fn note_two_crate_versions(&self, did: DefId, err: &mut Diag<'_>) {
2390+
fn note_two_crate_versions(&self, did: DefId, sp: impl Into<MultiSpan>, err: &mut Diag<'_>) {
24752391
let crate_name = self.tcx.crate_name(did.krate);
2476-
let crate_msg =
2477-
format!("perhaps two different versions of crate `{crate_name}` are being used?");
2478-
err.note(crate_msg);
2392+
let crate_msg = format!(
2393+
"there are multiple different versions of crate `{crate_name}` in the dependency graph"
2394+
);
2395+
err.span_note(sp, crate_msg);
24792396
}
24802397

24812398
fn note_adt_version_mismatch(
@@ -2536,8 +2453,76 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
25362453

25372454
for (similar_item, _) in similar_items {
25382455
err.span_help(self.tcx.def_span(similar_item), "item with same name found");
2539-
self.note_two_crate_versions(similar_item, err);
2456+
self.note_two_crate_versions(similar_item, MultiSpan::new(), err);
2457+
}
2458+
}
2459+
2460+
fn check_same_name_different_path(
2461+
&self,
2462+
err: &mut Diag<'_>,
2463+
obligation: &PredicateObligation<'tcx>,
2464+
trait_pred: ty::PolyTraitPredicate<'tcx>,
2465+
) -> bool {
2466+
let mut suggested = false;
2467+
let trait_def_id = trait_pred.def_id();
2468+
let trait_has_same_params = |other_trait_def_id: DefId| -> bool {
2469+
let trait_generics = self.tcx.generics_of(trait_def_id);
2470+
let other_trait_generics = self.tcx.generics_of(other_trait_def_id);
2471+
2472+
if trait_generics.count() != other_trait_generics.count() {
2473+
return false;
2474+
}
2475+
trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all(
2476+
|(a, b)| {
2477+
(matches!(a.kind, ty::GenericParamDefKind::Type { .. })
2478+
&& matches!(b.kind, ty::GenericParamDefKind::Type { .. }))
2479+
|| (matches!(a.kind, ty::GenericParamDefKind::Lifetime,)
2480+
&& matches!(b.kind, ty::GenericParamDefKind::Lifetime))
2481+
|| (matches!(a.kind, ty::GenericParamDefKind::Const { .. })
2482+
&& matches!(b.kind, ty::GenericParamDefKind::Const { .. }))
2483+
},
2484+
)
2485+
};
2486+
let trait_name = self.tcx.item_name(trait_def_id);
2487+
if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
2488+
trait_def_id != *def_id
2489+
&& trait_name == self.tcx.item_name(def_id)
2490+
&& trait_has_same_params(*def_id)
2491+
&& self.predicate_must_hold_modulo_regions(&Obligation::new(
2492+
self.tcx,
2493+
obligation.cause.clone(),
2494+
obligation.param_env,
2495+
trait_pred.map_bound(|tr| ty::TraitPredicate {
2496+
trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args),
2497+
..tr
2498+
}),
2499+
))
2500+
}) {
2501+
err.note(format!(
2502+
"`{}` implements similarly named `{}`, but not `{}`",
2503+
trait_pred.self_ty(),
2504+
self.tcx.def_path_str(other_trait_def_id),
2505+
trait_pred.print_modifiers_and_trait_path()
2506+
));
2507+
suggested = true;
2508+
}
2509+
suggested
2510+
}
2511+
2512+
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
2513+
/// with the same path as `trait_ref`, a help message about a multiple different
2514+
/// versions of the same crate is added to `err`. Otherwise if it implements another
2515+
/// trait with the same name, a note message about a similarly named trait is added to `err`.
2516+
pub fn note_different_trait_with_same_name(
2517+
&self,
2518+
err: &mut Diag<'_>,
2519+
obligation: &PredicateObligation<'tcx>,
2520+
trait_pred: ty::PolyTraitPredicate<'tcx>,
2521+
) -> bool {
2522+
if self.check_same_trait_different_version(err, trait_pred) {
2523+
return true;
25402524
}
2525+
self.check_same_name_different_path(err, obligation, trait_pred)
25412526
}
25422527

25432528
/// Add a `::` prefix when comparing paths so that paths with just one item

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub mod ambiguity;
22
pub mod call_kind;
3-
mod fulfillment_errors;
3+
pub mod fulfillment_errors;
44
pub mod on_unimplemented;
55
pub mod on_unimplemented_condition;
66
pub mod on_unimplemented_format;

0 commit comments

Comments
 (0)