From 73b1560bfeb58747fa97c9481506459c10890cab Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 27 Aug 2026 16:48:52 +0300 Subject: [PATCH 1/2] resolve: Concatenate reexport chains from all crates --- .../rustc_resolve/src/build_reduced_graph.rs | 7 +++- .../rustc_resolve/src/diagnostics/impls.rs | 8 ++--- compiler/rustc_resolve/src/imports.rs | 6 ++-- compiler/rustc_resolve/src/lib.rs | 35 ++++++++++++------- compiler/rustc_resolve/src/macros.rs | 2 +- src/librustdoc/clean/mod.rs | 5 +++ 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 88f057c3a6d6d..e72761b87167a 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -398,8 +398,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Record primary definitions. let mut define_extern = |ns| { let orig_ident_span = orig_ident.span; + let reexport_chain: &[Reexport] = if !reexport_chain.is_empty() { + self.arenas.dropless.alloc_slice(reexport_chain) + } else { + &[] + }; let decl = self.arenas.alloc_decl(DeclData { - kind: DeclKind::Def(res), + kind: DeclKind::Def(res, reexport_chain), ambiguity: CmCell::new(ambig), initial_vis: vis, ambiguity_vis_max: CmCell::new(None), diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index a005824e5dbfa..3e5fe13ccf094 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -358,7 +358,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(ambiguity_warning) = ambiguity_error.warning { let node_id = match ambiguity_error.b1.0.kind { DeclKind::Import { import, .. } => import.root_id, - DeclKind::Def(_) => CRATE_NODE_ID, + DeclKind::Def(..) => CRATE_NODE_ID, }; let lint = match ambiguity_warning { @@ -2465,7 +2465,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// If the binding refers to a tuple struct constructor with fields, /// returns the span of its fields. fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { - let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = + let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), _) = decl.kind else { return None; @@ -2735,7 +2735,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match binding.kind { DeclKind::Import { source_decl, import, .. } => { - let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); + let through_reexport = !matches!(source_decl.kind, DeclKind::Def(..)); let uses_relative_path = import .module_path .first() @@ -2810,7 +2810,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { sugg_paths.push((path, through_reexport)); } } - DeclKind::Def(_) => {} + DeclKind::Def(..) => {} } let first = binding == first_binding; let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 1cda9b9139028..9246322d5df1d 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1073,7 +1073,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && !binding.vis().is_public() { let binding_id = match binding.kind { - DeclKind::Def(res) => { + DeclKind::Def(res, ..) => { Some(self.def_id_to_node_id(res.def_id().expect_local())) } DeclKind::Import { import, .. } => import.id(), @@ -1513,7 +1513,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match source_decl.kind { // Never suggest names that previously could not // be resolved. - DeclKind::Def(Res::Err) => None, + DeclKind::Def(Res::Err, ..) => None, _ => Some(i.name), } } @@ -1697,7 +1697,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match decl.kind { // exclude decl_macro - DeclKind::Def(Res::Def(DefKind::Macro(_), def_id)) + DeclKind::Def(Res::Def(DefKind::Macro(_), def_id), _) if let SyntaxExtensionKind::MacroRules(mr) = &self.get_macro_by_def_id(def_id).kind && mr.is_macro_rules() => diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index baadbc644582b..d3dbfb0b9b0d0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1040,7 +1040,9 @@ type Decl<'ra> = Interned<'ra, DeclData<'ra>>; enum DeclKind<'ra> { /// The name declaration is a definition (possibly without a `DefId`), /// can be provided by source code or built into the language. - Def(Res), + /// + /// The reexports are only added for declarations in external modules. + Def(Res, &'ra [Reexport]), /// The name declaration is a link to another name declaration. Import { source_decl: Decl<'ra>, import: Import<'ra> }, } @@ -1152,7 +1154,7 @@ impl<'ra> DeclData<'ra> { fn res(&self) -> Res { match self.kind { - DeclKind::Def(res) => res, + DeclKind::Def(res, ..) => res, DeclKind::Import { source_decl, .. } => source_decl.res(), } } @@ -1185,9 +1187,10 @@ impl<'ra> DeclData<'ra> { fn is_possibly_imported_variant(&self) -> bool { match self.kind { DeclKind::Import { source_decl, .. } => source_decl.is_possibly_imported_variant(), - DeclKind::Def(Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _)) => { - true - } + DeclKind::Def( + Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), + _, + ) => true, DeclKind::Def(..) => false, } } @@ -1197,7 +1200,7 @@ impl<'ra> DeclData<'ra> { DeclKind::Import { import, .. } => { matches!(import.kind, ImportKind::ExternCrate { .. }) } - DeclKind::Def(Res::Def(_, def_id)) => def_id.is_crate_root(), + DeclKind::Def(Res::Def(_, def_id), _) => def_id.is_crate_root(), _ => false, } } @@ -1232,13 +1235,21 @@ impl<'ra> DeclData<'ra> { } fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { - let mut reexport_chain = SmallVec::new(); + let mut full_reexport_chain: SmallVec<[Reexport; 2]> = SmallVec::new(); let mut next_binding = self; - while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify()); - next_binding = source_decl; + loop { + match next_binding.kind { + DeclKind::Import { source_decl, import, .. } => { + full_reexport_chain.push(import.simplify()); + next_binding = source_decl; + } + DeclKind::Def(_, reexport_chain) => { + full_reexport_chain.extend(reexport_chain.iter().copied()); + break; + } + } } - reexport_chain + full_reexport_chain } // Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding` @@ -1585,7 +1596,7 @@ impl<'ra> ResolverArenas<'ra> { parent_module: Option>, ) -> Decl<'ra> { self.alloc_decl(DeclData { - kind: DeclKind::Def(res), + kind: DeclKind::Def(res, &[]), ambiguity: CmCell::new(None), initial_vis: vis, ambiguity_vis_max: CmCell::new(None), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d57f831f3d204..0a518f694db5b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1143,7 +1143,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some((mod_def_id, node_id)) = invoc_in_mod_inert_attr && let Some(decl) = decl // This is a `macro_rules` itself, not some import. - && let DeclKind::Def(res) = decl.kind + && let DeclKind::Def(res, _) = decl.kind && let Res::Def(DefKind::Macro(kinds), def_id) = res && kinds.contains(MacroKinds::BANG) // And the `macro_rules` is defined inside the attribute's module, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2b1a37cbcda30..67a0b7ff91976 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2772,6 +2772,11 @@ fn get_all_import_attributes<'hir>( .iter() .flat_map(|reexport| reexport.id()) { + if cx.tcx.def_kind(def_id) == DefKind::ExternCrate { + // There is no attribute to query from an `DefKind::ExternCrate` item, only + // from `DefKind::Use`. + continue; + } let import_attrs = inline::load_attrs(cx.tcx, def_id); if first { // This is the "original" reexport so we get all its attributes without filtering them. From cb55fc760e4515aef0216ee1d9ae2ad38cae88e6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 9 Sep 2026 17:23:47 +0200 Subject: [PATCH 2/2] Add regression test for reexport chain in transient crates --- .../auxiliary/transient-reexports-dep.rs | 4 ++++ .../reexport/auxiliary/transient-reexports.rs | 8 +++++++ .../reexport/transient-reexports.rs | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs create mode 100644 tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs create mode 100644 tests/rustdoc-html/reexport/transient-reexports.rs diff --git a/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs b/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs new file mode 100644 index 0000000000000..55d58fab729a5 --- /dev/null +++ b/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs @@ -0,0 +1,4 @@ +#![crate_name = "baz"] + +/// baz +pub struct Type; diff --git a/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs b/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs new file mode 100644 index 0000000000000..e078d12c0e78e --- /dev/null +++ b/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs @@ -0,0 +1,8 @@ +//@ aux-build: transient-reexports-dep.rs + +#![crate_name = "bar"] + +extern crate baz; + +/// bar +pub use baz::Type; diff --git a/tests/rustdoc-html/reexport/transient-reexports.rs b/tests/rustdoc-html/reexport/transient-reexports.rs new file mode 100644 index 0000000000000..1ce6b67aa3ccf --- /dev/null +++ b/tests/rustdoc-html/reexport/transient-reexports.rs @@ -0,0 +1,21 @@ +// If there is more than one dependency level, the transient crates reexports used to be +// skipped. This test ensures it's not the case anymore. +// Regression test . + +//@ aux-build: transient-reexports.rs + +#![crate_name = "foo"] + +extern crate bar; + +//@ has 'foo/index.html' + +// We ensure that there is only one item, so it's not possibly another item we're gonna check. +//@ count - '//dl/[@class="item-table"]/dt' 1 +//@ count - '//dl/[@class="item-table"]/dd' 1 +//@ has - '//dl/[@class="item-table"]/dt/a[@href="struct.Type.html"]' 'Type' +// We should have "foo", "bar" and "baz" (one fragment in each crate reexport). +//@ has - '//dl/[@class="item-table"]/dd' 'foo bar baz' + +/// foo +pub use bar::Type;