Change:
Consider three crates High, Mid and Low, where: Mid depends on Low; High depends on both Mid and Low. This is a quite common case. Now, it sometimes happens that the dependency of Mid is bumped to a more recent version of Low. If, however, the developer fails to bump the dependency of High to a more recent version of Low, surprising error messages arise. In particular, may arise, without any change to the code of either or . It would be great if the compiler could detect the error and somehow print something along the liens of
Triage: I feel like this has gotten better in certain cases, but not all. It would be nice to make a way to reproduce this more easily, but it's non-trivial.

Discussion:
trait_missing_method: bool, ) { let mut alt_rcvr_sugg = false; <ins> let mut trait_in_other_version_found = false; </ins> if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { debug!( "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",

// self types and rely on the suggestion to `use` the trait from // `suggest_valid_traits`. let did = Some(pick.item.container_id(self.tcx)); <del> let skip = skippable.contains(&did); if pick.autoderefs == 0 && !skip { </del> <ins> if skippable.contains(&did) { continue; } trait_in_other_version_found = self .detect_and_explain_multiple_crate_versions( err, pick.item.def_id, rcvr.hir_id, *rcvr_ty, ); if pick.autoderefs == 0 && !trait_in_other_version_found { </ins> err.span_label( pick.item.ident(self.tcx).span, format!("the method is available for `{rcvr_ty}` here"),

} } } <del> if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) { </del> <ins> if let SelfSource::QPath(ty) = source && !valid_out_of_scope_traits.is_empty() && let hir::TyKind::Path(path) = ty.kind && let hir::QPath::Resolved(_, path) = path && let Some(def_id) = path.res.opt_def_id() && let Some(assoc) = self .tcx .associated_items(valid_out_of_scope_traits[0]) .filter_by_name_unhygienic(item_name.name) .next() { // See if the `Type::function(val)` where `function` wasn't found corresponds to a // `Trait` that is imported directly, but `Type` came from a different version of the // same crate. let rcvr_ty = self.tcx.type_of(def_id).instantiate_identity(); trait_in_other_version_found = self.detect_and_explain_multiple_crate_versions( err, assoc.def_id, ty.hir_id, rcvr_ty, ); } if !trait_in_other_version_found && self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) { </ins> return; }

} } <ins> fn detect_and_explain_multiple_crate_versions( &self, err: &mut Diag<'_>, item_def_id: DefId, hir_id: hir::HirId, rcvr_ty: Ty<'_>, ) -> bool { let hir_id = self.tcx.parent_hir_id(hir_id); let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false }; if traits.is_empty() { return false; } let trait_def_id = self.tcx.parent(item_def_id); let krate = self.tcx.crate_name(trait_def_id.krate); let name = self.tcx.item_name(trait_def_id); let candidates: Vec<_> = traits .iter() .filter(|c| { c.def_id.krate != trait_def_id.krate && self.tcx.crate_name(c.def_id.krate) == krate && self.tcx.item_name(c.def_id) == name }) .map(|c| (c.def_id, c.import_ids.get(0).cloned())) .collect(); if candidates.is_empty() { return false; } let item_span = self.tcx.def_span(item_def_id); let msg = format!( "there are multiple different versions of crate `{krate}` in the dependency graph", ); let trait_span = self.tcx.def_span(trait_def_id); let mut multi_span: MultiSpan = trait_span.into(); multi_span.push_span_label(trait_span, format!("this is the trait that is needed")); let descr = self.tcx.associated_item(item_def_id).descr(); multi_span .push_span_label(item_span, format!("the {descr} is available for `{rcvr_ty}` here")); for (def_id, import_def_id) in candidates { if let Some(import_def_id) = import_def_id { multi_span.push_span_label( self.tcx.def_span(import_def_id), format!( "`{name}` imported here doesn't correspond to the right version of crate  `{krate}`", ), ); } multi_span.push_span_label( self.tcx.def_span(def_id), format!("this is the trait that was imported"), ); } err.span_note(multi_span, msg); true } </ins> /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` /// FIXME: currently not working for suggesting `map_or_else`, see #102408 pub(crate) fn suggest_else_fn_with_closure(

err.highlighted_span_help( span, vec![ <del> StringPart::normal("you have ".to_string()), </del> <ins> StringPart::normal("there are ".to_string()), </ins> StringPart::highlighted("multiple different versions".to_string()), StringPart::normal(" of crate `".to_string()), StringPart::highlighted(format!("{name}")), <del> StringPart::normal("` in your dependency graph".to_string()), </del> <ins> StringPart::normal("` the your dependency graph".to_string()), </ins> ], ); let candidates = if impl_candidates.is_empty() {

#![crate_name = "dependency"] #![crate_type = "rlib"] <del> pub struct Type; pub trait Trait {} impl Trait for Type {} </del> <ins> pub struct Type(pub i32); pub trait Trait { fn foo(&self); fn bar(); } impl Trait for Type { fn foo(&self) {} fn bar() {} } </ins> pub fn do_something<X: Trait>(_: X) {}

#![crate_name = "dependency"] #![crate_type = "rlib"] <del> pub struct Type(pub i32); pub trait Trait {} impl Trait for Type {} </del> <ins> pub struct Type; pub trait Trait { fn foo(&self); fn bar(); } impl Trait for Type { fn foo(&self) {} fn bar() {} } </ins> pub fn do_something<X: Trait>(_: X) {}

<ins> #![crate_name = "foo"] #![crate_type = "rlib"] extern crate dependency; pub use dependency::Type; </ins>

extern crate dep_2_reexport; extern crate dependency; <del> use dep_2_reexport::do_something; use dependency::Type; </del> <ins> use dep_2_reexport::Type; use dependency::{do_something, Trait}; </ins> fn main() { do_something(Type); <ins> Type.foo(); Type::bar(); </ins> }

//@ only-linux //@ ignore-wasm32 //@ ignore-wasm64 <ins> // ignore-tidy-linelength </ins> use run_make_support::{rust_lib_name, rustc}; fn main() { rustc().input("multiple-dep-versions-1.rs").run(); rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run(); <ins> rustc() .input("multiple-dep-versions-3.rs") .extern_("dependency", rust_lib_name("dependency2")) .run(); </ins> rustc() .input("multiple-dep-versions.rs") .extern_("dependency", rust_lib_name("dependency")) <del> .extern_("dep_2_reexport", rust_lib_name("dependency2")) </del> <ins> .extern_("dep_2_reexport", rust_lib_name("foo")) </ins> .run_fail() .assert_stderr_contains( <del> "you have multiple different versions of crate `dependency` in your dependency graph", </del> <ins> r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied --> multiple-dep-versions.rs:7:18 | 7  |     do_something(Type); |     ------------ ^^^^ the trait `Trait` is not implemented for `dep_2_reexport::Type` |     | |     required by a bound introduced by this call | help: there are multiple different versions of crate `dependency` the your dependency graph --> multiple-dep-versions.rs:1:1 | 1  | extern crate dep_2_reexport; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a dependency of crate `foo` 2  | extern crate dependency; | ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate"#, ) .assert_stderr_contains( r#" 3  | pub struct Type(pub i32); | ^^^^^^^^^^^^^^^ this type implements the required trait 4  | pub trait Trait { | --------------- this is the required trait"#, ) .assert_stderr_contains( r#" 3  | pub struct Type; | ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#, ) .assert_stderr_contains( r#" error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope --> multiple-dep-versions.rs:8:10 | 8 |     Type.foo(); |          ^^^ method not found in `Type` | note: there are multiple different versions of crate `dependency` in the dependency graph"#, ) .assert_stderr_contains( r#" 4 | pub trait Trait { | ^^^^^^^^^^^^^^^ this is the trait that is needed 5 |     fn foo(&self); |     -------------- the method is available for `dep_2_reexport::Type` here | ::: multiple-dep-versions.rs:4:32 | 4 | use dependency::{do_something, Trait}; |                                ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#, </ins> ) .assert_stderr_contains( <del> "two types coming from two different versions of the same crate are different types  even if they look the same", </del> <ins> r#" 4 | pub trait Trait { | --------------- this is the trait that was imported"#, </ins> ) <del> .assert_stderr_contains("this type doesn't implement the required trait") .assert_stderr_contains("this type implements the required trait") .assert_stderr_contains("this is the required trait"); </del> <ins> .assert_stderr_contains( r#" error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope --> multiple-dep-versions.rs:9:11 | 9 |     Type::bar(); |           ^^^ function or associated item not found in `Type` | note: there are multiple different versions of crate `dependency` in the dependency graph"#, ) .assert_stderr_contains( r#" 4 | pub trait Trait { | ^^^^^^^^^^^^^^^ this is the trait that is needed 5 |     fn foo(&self); 6 |     fn bar(); |     --------- the associated function is available for `dep_2_reexport::Type` here | ::: multiple-dep-versions.rs:4:32 | 4 | use dependency::{do_something, Trait}; |                                ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#, ); </ins> }