When referencing items in transitive dependency crates, the current diagnostic behavior prefers paths through a direct dependency crate, even if the path involves a #[doc(hidden)] re-export.
In the examples below, direct_dep::__private is a #[doc(hidden)] module re-exporting items from transitive_dep, which is not an extern crate of the current crate. See the full code of the three crates at the bottom of the issue description.
error[E0308]: mismatched types
--> $DIR/hidden-reexport-of-transitive-dep-item.rs:10:44
|
LL | let _: direct_dep::__private::Struct = Struct;
| ----------------------------- ^^^^^^ expected `direct_dep::__private::Struct`, found `Struct`
| |
| expected due to this
|
= note: `Struct` and `direct_dep::__private::Struct` have similar names, but are actually distinct types
note: `Struct` is defined in the current crate
--> $DIR/hidden-reexport-of-transitive-dep-item.rs:7:1
|
LL | struct Struct;
| ^^^^^^^^^^^^^
note: `direct_dep::__private::Struct` is defined in crate `transitive_dep`
--> $DIR/auxiliary/transitive-dep.rs:3:1
|
LL | pub struct Struct;
| ^^^^^^^^^^^^^^^^^
Based on this, for consistent behavior, the same preference would be expected if the re-exported item is nested in a module in the re-exporting direct dependency crate. However, currently, rustc instead shows an inaccessible path through the transitive dependency crate, falling back to the bare definition path of the item. See the examples below (diagnostic notes removed for brevity).
Actual:
error[E0308]: mismatched types
--> $DIR/hidden-reexport-of-nested-transitive-dep-item.rs:10:51
|
LL | let _: direct_dep::__private::inner::Struct = Struct;
| ------------------------------------ ^^^^^^ expected `transitive_dep::Struct`, found `Struct`
| |
| expected due to this
Expected:
error[E0308]: mismatched types
--> $DIR/hidden-reexport-of-nested-transitive-dep-item.rs:10:51
|
LL | let _: direct_dep::__private::inner::Struct = Struct;
| ------------------------------------ ^^^^^^ expected `direct_dep::__private::inner::Struct`, found `Struct`
| |
| expected due to this
Since the #[doc(hidden)] (but actually accessible) path is already preferred in the more basic case, it should be the same in the more complicated cases. Note that the issue disappears entirely and the accessible path through the direct dependency crate is used in both cases if the #[doc(hidden)] attribute is removed from the re-exporting module.
Related PRs/issues:
Code to reproduce the diagnostics:
I tried to reduce this as much as possible to the point that changing anything about the example does not trigger the inconsistent diagnostic anymore.
transitive-dep.rs:
#![crate_type = "lib"]
pub struct Struct;
direct-dep.rs:
#![crate_type = "lib"]
extern crate transitive_dep;
mod private {
// NOTE: Wrapping the re-export in a module is required to trigger the def path fallback,
// and removing it results in the intended diagnostic in the first example
// with the accessible `#[doc(hidden)]` path through `direct_dep`.
pub mod inner {
pub use crate::transitive_dep::Struct;
}
}
#[doc(hidden)]
pub mod __private {
pub use crate::private::*;
}
test.rs:
extern crate direct_dep;
struct Struct;
fn main() {
let _: direct_dep::__private::inner::Struct = Struct;
}
Meta
rustc --version --verbose:
rustc 1.99.0-dev
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-07-24
host: x86_64-unknown-linux-gnu
release: 1.99.0-dev
LLVM version: 22.1.8
Backtrace
When referencing items in transitive dependency crates, the current diagnostic behavior prefers paths through a direct dependency crate, even if the path involves a
#[doc(hidden)]re-export.In the examples below,
direct_dep::__privateis a#[doc(hidden)]module re-exporting items fromtransitive_dep, which is not an extern crate of the current crate. See the full code of the three crates at the bottom of the issue description.Based on this, for consistent behavior, the same preference would be expected if the re-exported item is nested in a module in the re-exporting direct dependency crate. However, currently, rustc instead shows an inaccessible path through the transitive dependency crate, falling back to the bare definition path of the item. See the examples below (diagnostic notes removed for brevity).
Actual:
Expected:
Since the
#[doc(hidden)](but actually accessible) path is already preferred in the more basic case, it should be the same in the more complicated cases. Note that the issue disappears entirely and the accessible path through the direct dependency crate is used in both cases if the#[doc(hidden)]attribute is removed from the re-exporting module.Related PRs/issues:
Code to reproduce the diagnostics:
I tried to reduce this as much as possible to the point that changing anything about the example does not trigger the inconsistent diagnostic anymore.
transitive-dep.rs:
direct-dep.rs:
test.rs:
Meta
rustc --version --verbose:Backtrace