Skip to content

Commit c7c8914

Browse files
authored
Rollup merge of #113785 - GuillaumeGomez:tests/rustdoc/issue-105735-fix, r=notriddle,aDotInTheVoid
Fix invalid display of inlined re-export when both local and foreign items are inlined Fixes #105735. The bug is actually quite interesting: at the `clean` pass, local inlined items have their `use` item removed, however foreign items don't have their `use` item removed because it's in the `clean` pass that we handle them. So when a `use` inlines both a local and a foreign item, it will work as expected for the foreign one, but not for the local as its `use` should not be around anymore. To prevent this, I created a new `inlined_foreigns` field into the `Module` struct to allow to remove the `use` item early on for foreign items as well. Then we iterate it in the `clean` pass directly. r? ``@notriddle``
2 parents 444ac1a + afec6d2 commit c7c8914

File tree

4 files changed

+87
-17
lines changed

4 files changed

+87
-17
lines changed

src/librustdoc/clean/mod.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
9090
}
9191
v
9292
}));
93+
items.extend(doc.inlined_foreigns.iter().flat_map(|((_, renamed), (res, local_import_id))| {
94+
let Some(def_id) = res.opt_def_id() else { return Vec::new() };
95+
let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id));
96+
let import = cx.tcx.hir().expect_item(*local_import_id);
97+
match import.kind {
98+
hir::ItemKind::Use(path, kind) => {
99+
let hir::UsePath { segments, span, .. } = *path;
100+
let path = hir::Path { segments, res: *res, span };
101+
clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default())
102+
}
103+
_ => unreachable!(),
104+
}
105+
}));
93106
items.extend(doc.items.values().flat_map(|(item, renamed, _)| {
94107
// Now we actually lower the imports, skipping everything else.
95108
if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
@@ -2652,9 +2665,6 @@ fn clean_use_statement<'tcx>(
26522665
let mut items = Vec::new();
26532666
let hir::UsePath { segments, ref res, span } = *path;
26542667
for &res in res {
2655-
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
2656-
continue;
2657-
}
26582668
let path = hir::Path { segments, res, span };
26592669
items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
26602670
}
@@ -2669,6 +2679,9 @@ fn clean_use_statement_inner<'tcx>(
26692679
cx: &mut DocContext<'tcx>,
26702680
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
26712681
) -> Vec<Item> {
2682+
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
2683+
return Vec::new();
2684+
}
26722685
// We need this comparison because some imports (for std types for example)
26732686
// are "inserted" as well but directly by the compiler and they should not be
26742687
// taken into account.

src/librustdoc/visit_ast.rs

+25-14
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub(crate) struct Module<'hir> {
3535
(LocalDefId, Option<Symbol>),
3636
(&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>),
3737
>,
38+
/// Same as for `items`.
39+
pub(crate) inlined_foreigns: FxIndexMap<(DefId, Option<Symbol>), (Res, LocalDefId)>,
3840
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
3941
}
4042

@@ -54,6 +56,7 @@ impl Module<'_> {
5456
import_id,
5557
mods: Vec::new(),
5658
items: FxIndexMap::default(),
59+
inlined_foreigns: FxIndexMap::default(),
5760
foreigns: Vec::new(),
5861
}
5962
}
@@ -272,21 +275,30 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
272275
return false;
273276
}
274277

275-
// For cross-crate impl inlining we need to know whether items are
276-
// reachable in documentation -- a previously unreachable item can be
277-
// made reachable by cross-crate inlining which we're checking here.
278-
// (this is done here because we need to know this upfront).
279-
if !ori_res_did.is_local() && !is_no_inline {
280-
crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
281-
return false;
282-
}
283-
278+
let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did);
284279
let Some(res_did) = ori_res_did.as_local() else {
285-
return false;
280+
// For cross-crate impl inlining we need to know whether items are
281+
// reachable in documentation -- a previously unreachable item can be
282+
// made reachable by cross-crate inlining which we're checking here.
283+
// (this is done here because we need to know this upfront).
284+
crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
285+
if is_hidden {
286+
return false;
287+
}
288+
// We store inlined foreign items otherwise, it'd mean that the `use` item would be kept
289+
// around. It's not a problem unless this `use` imports both a local AND a foreign item.
290+
// If a local item is inlined, its `use` is not supposed to still be around in `clean`,
291+
// which would make appear the `use` in the generated documentation like the local item
292+
// was not inlined even though it actually was.
293+
self.modules
294+
.last_mut()
295+
.unwrap()
296+
.inlined_foreigns
297+
.insert((ori_res_did, renamed), (res, def_id));
298+
return true;
286299
};
287300

288301
let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did);
289-
let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did);
290302
let item = tcx.hir().get_by_def_id(res_did);
291303

292304
if !please_inline {
@@ -314,7 +326,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
314326
return false;
315327
}
316328

317-
let inlined = match tcx.hir().get_by_def_id(res_did) {
329+
let inlined = match item {
318330
// Bang macros are handled a bit on their because of how they are handled by the
319331
// compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
320332
// `#[doc(inline)]`, then we don't inline it.
@@ -346,7 +358,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
346358
};
347359
self.view_item_stack.remove(&res_did);
348360
if inlined {
349-
self.cx.cache.inlined_items.insert(res_did.to_def_id());
361+
self.cx.cache.inlined_items.insert(ori_res_did);
350362
}
351363
inlined
352364
}
@@ -483,7 +495,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
483495
continue;
484496
}
485497
}
486-
487498
self.add_to_current_mod(item, renamed, import_id);
488499
}
489500
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export.
2+
3+
#![crate_name = "foo"]
4+
#![no_std]
5+
6+
// @has 'foo/index.html'
7+
// @has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8'
8+
// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
9+
// We also ensure we don't have another item displayed.
10+
// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2
11+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions'
12+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants'
13+
14+
mod other {
15+
pub type AtomicU8 = ();
16+
}
17+
18+
mod thing {
19+
pub use crate::other::AtomicU8;
20+
21+
#[allow(non_upper_case_globals)]
22+
pub const AtomicU8: () = ();
23+
}
24+
25+
pub use crate::thing::AtomicU8;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export.
2+
3+
#![crate_name = "foo"]
4+
#![no_std]
5+
6+
// @has 'foo/index.html'
7+
// @has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8'
8+
// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
9+
// We also ensure we don't have another item displayed.
10+
// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2
11+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs'
12+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants'
13+
14+
mod thing {
15+
pub use core::sync::atomic::AtomicU8;
16+
17+
#[allow(non_upper_case_globals)]
18+
pub const AtomicU8: () = ();
19+
}
20+
21+
pub use crate::thing::AtomicU8;

0 commit comments

Comments
 (0)