Untruthful multiple different versions of a crate in the dependency graph

Currently, If `expected_def_id` and `another_trait_def_id` have their
crate imported as ExternCrateSource::Path the method
get_extern_crate_renamed_symbol() will return None for both, resulting
in a false positive. This fixes the issue by using a slitly different
approach, we use a predicate instead and do the comparison of the item
names only when both crates are imported as ExternCrateSource::Extern and
are direct dependencies of the LOCAL_CRATE, otherwise false is returned.
This commit is contained in:
Romain Perier 2025-11-15 18:34:02 +01:00
parent 6840234806
commit b25336b010
5 changed files with 77 additions and 14 deletions

View file

@ -12,7 +12,7 @@ use std::{fmt, iter};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, AmbigArg};
use rustc_infer::traits::solve::Goal;
@ -22,7 +22,8 @@ use rustc_infer::traits::{
};
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _};
use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span, Symbol};
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span};
use tracing::{info, instrument};
pub use self::overflow::*;
@ -353,14 +354,37 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
fn get_extern_crate_renamed_symbol(&self, trait_def_id: DefId) -> Option<Symbol> {
if !trait_def_id.is_local()
&& let Some(data) = self.tcx.extern_crate(trait_def_id.krate)
&& let rustc_session::cstore::ExternCrateSource::Extern(def_id) = data.src
{
self.tcx.opt_item_name(def_id)
} else {
None
/// If the crates of `expected_def_id` and `trait_def_id` are imported as extern crate
/// under the same name (`extern crate foo as a` and `extern crate bar as a`) returns true,
/// otherwise returns false.
fn extern_crates_with_the_same_name(
&self,
expected_def_id: DefId,
trait_def_id: DefId,
) -> bool {
if expected_def_id.is_local() || trait_def_id.is_local() {
return false;
}
// We only compare direct dependencies of the current crate, so it avoids unnecessary
// processing and excludes indirect dependencies, like `std` or `core`. In such a case
// both would be imported under the same name `std`.
match (
self.tcx.extern_crate(expected_def_id.krate),
self.tcx.extern_crate(trait_def_id.krate),
) {
(
Some(ExternCrate {
src: ExternCrateSource::Extern(expected_def_id),
dependency_of: LOCAL_CRATE,
..
}),
Some(ExternCrate {
src: ExternCrateSource::Extern(trait_def_id),
dependency_of: LOCAL_CRATE,
..
}),
) => self.tcx.item_name(expected_def_id) == self.tcx.item_name(trait_def_id),
_ => false,
}
}
@ -377,13 +401,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
{
let krate = self.tcx.crate_name(expected_did.krate);
let name = self.tcx.item_name(expected_did);
let locally_renamed_krate = self
.get_extern_crate_renamed_symbol(expected_did)
.map_or(None, |s| if s != krate { Some(s) } else { None });
let definitions_with_same_path: UnordSet<_> = found_dids
.filter(|def_id| {
def_id.krate != expected_did.krate
&& (locally_renamed_krate == self.get_extern_crate_renamed_symbol(*def_id)
&& (self.extern_crates_with_the_same_name(expected_did, *def_id)
|| self.tcx.crate_name(def_id.krate) == krate)
&& self.tcx.item_name(def_id) == name
})

View file

@ -0,0 +1,7 @@
//@ aux-crate:crate2=crate2.rs
pub trait Trait {}
pub fn foo(_arg: impl Trait) {}
pub fn bar(_arg: impl crate2::Trait) {}

View file

@ -0,0 +1 @@
pub trait Trait {}

View file

@ -0,0 +1,12 @@
// Test that we do not report false positives of a crate as being found multiple times in the
// dependency graph with different versions, when this crate is only present once. In this test,
// this was happening for `crate1` when two different crates in the dependencies were imported
// as ExternCrateSource::Path.
// Issue #148892.
//@ aux-crate:crate1=crate1.rs
struct MyStruct; //~ HELP the trait `Trait` is not implemented for `MyStruct`
fn main() {
crate1::foo(MyStruct); //~ ERROR the trait bound `MyStruct: Trait` is not satisfied
}

View file

@ -0,0 +1,22 @@
error[E0277]: the trait bound `MyStruct: Trait` is not satisfied
--> $DIR/wrong-multiple-different-versions-of-a-crate.rs:11:17
|
LL | crate1::foo(MyStruct);
| ----------- ^^^^^^^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
help: the trait `Trait` is not implemented for `MyStruct`
--> $DIR/wrong-multiple-different-versions-of-a-crate.rs:8:1
|
LL | struct MyStruct;
| ^^^^^^^^^^^^^^^
note: required by a bound in `foo`
--> $DIR/auxiliary/crate1.rs:5:23
|
LL | pub fn foo(_arg: impl Trait) {}
| ^^^^^ required by this bound in `foo`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.