Skip to content

Commit 9af31ea

Browse files
Correctly handle re-exports of #[doc(hidden)] items
1 parent c2ccc85 commit 9af31ea

File tree

2 files changed

+66
-44
lines changed

2 files changed

+66
-44
lines changed

src/librustdoc/clean/mod.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use thin_vec::ThinVec;
4141

4242
use crate::core::{self, DocContext, ImplTraitParam};
4343
use crate::formats::item_type::ItemType;
44-
use crate::visit_ast::Module as DocModule;
44+
use crate::visit_ast::{ItemEntry, Module as DocModule};
4545

4646
use utils::*;
4747

@@ -77,7 +77,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
7777
// This covers the case where somebody does an import which should pull in an item,
7878
// but there's already an item with the same namespace and same name. Rust gives
7979
// priority to the not-imported one, so we should, too.
80-
items.extend(doc.items.values().flat_map(|(item, renamed, import_id)| {
80+
items.extend(doc.items.values().flat_map(|ItemEntry { item, renamed, import_id }| {
8181
// First, lower everything other than imports.
8282
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
8383
return Vec::new();
@@ -90,7 +90,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
9090
}
9191
v
9292
}));
93-
items.extend(doc.items.values().flat_map(|(item, renamed, _)| {
93+
items.extend(doc.items.values().flat_map(|ItemEntry { item, renamed, .. }| {
9494
// Now we actually lower the imports, skipping everything else.
9595
if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
9696
let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id()));
@@ -2236,16 +2236,8 @@ fn filter_tokens_from_list(
22362236
fn add_without_unwanted_attributes<'hir>(
22372237
attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)>,
22382238
new_attrs: &'hir [ast::Attribute],
2239-
is_inline: bool,
22402239
import_parent: Option<DefId>,
22412240
) {
2242-
// If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
2243-
if !is_inline {
2244-
for attr in new_attrs {
2245-
attrs.push((Cow::Borrowed(attr), import_parent));
2246-
}
2247-
return;
2248-
}
22492241
for attr in new_attrs {
22502242
if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
22512243
attrs.push((Cow::Borrowed(attr), import_parent));

src/librustdoc/visit_ast.rs

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ use std::{iter, mem};
1919
use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt};
2020
use crate::core;
2121

22+
#[derive(Debug)]
23+
pub(crate) struct ItemEntry<'hir> {
24+
pub(crate) item: &'hir hir::Item<'hir>,
25+
pub(crate) renamed: Option<Symbol>,
26+
pub(crate) import_id: Option<LocalDefId>,
27+
}
28+
2229
/// This module is used to store stuff from Rust's AST in a more convenient
2330
/// manner (and with prettier names) before cleaning.
2431
#[derive(Debug)]
@@ -31,10 +38,7 @@ pub(crate) struct Module<'hir> {
3138
pub(crate) import_id: Option<LocalDefId>,
3239
/// The key is the item `ItemId` and the value is: (item, renamed, import_id).
3340
/// We use `FxIndexMap` to keep the insert order.
34-
pub(crate) items: FxIndexMap<
35-
(LocalDefId, Option<Symbol>),
36-
(&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>),
37-
>,
41+
pub(crate) items: FxIndexMap<(LocalDefId, Option<Symbol>), ItemEntry<'hir>>,
3842
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
3943
}
4044

@@ -107,6 +111,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> {
107111
modules: Vec<Module<'tcx>>,
108112
is_importable_from_parent: bool,
109113
inside_body: bool,
114+
ignored_items: usize,
110115
}
111116

112117
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
@@ -131,6 +136,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
131136
modules: vec![om],
132137
is_importable_from_parent: true,
133138
inside_body: false,
139+
ignored_items: 0,
134140
}
135141
}
136142

@@ -165,7 +171,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
165171
inserted.insert(def_id)
166172
{
167173
let item = self.cx.tcx.hir().expect_item(local_def_id);
168-
top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None));
174+
top_level_module.items.insert((local_def_id, Some(item.ident.name)), ItemEntry {
175+
item,
176+
renamed: None,
177+
import_id: None,
178+
});
169179
}
170180
}
171181

@@ -263,9 +273,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
263273
};
264274

265275
let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
266-
// Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
267-
let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
268-
|| use_attrs.lists(sym::doc).has_word(sym::hidden);
276+
let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline);
269277

270278
// For cross-crate impl inlining we need to know whether items are
271279
// reachable in documentation -- a previously unreachable item can be
@@ -280,12 +288,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
280288
return false;
281289
};
282290

283-
let is_private =
284-
!self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, ori_res_did);
285-
let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did, None);
291+
let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did);
292+
let is_hidden = tcx.is_doc_hidden(res_did) || inherits_doc_hidden(tcx, res_did, None);
286293

287294
// Only inline if requested or if the item would otherwise be stripped.
288-
if (!please_inline && !is_private && !is_hidden) || is_no_inline {
295+
if is_no_inline || (!please_inline && !is_private && !is_hidden) {
289296
return false;
290297
}
291298

@@ -298,8 +305,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
298305
.cx
299306
.cache
300307
.effective_visibilities
301-
.is_directly_public(self.cx.tcx, item_def_id.to_def_id()) &&
302-
!inherits_doc_hidden(self.cx.tcx, item_def_id, None)
308+
.is_directly_public(tcx, item_def_id.to_def_id()) &&
309+
!tcx.is_doc_hidden(item_def_id) &&
310+
!inherits_doc_hidden(tcx, item_def_id, None)
303311
{
304312
// The imported item is public and not `doc(hidden)` so no need to inline it.
305313
return false;
@@ -312,22 +320,25 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
312320
let ret = match tcx.hir().get_by_def_id(res_did) {
313321
Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
314322
let prev = mem::replace(&mut self.inlining, true);
323+
let prev_ignored = self.ignored_items;
315324
for &i in m.item_ids {
316-
let i = self.cx.tcx.hir().item(i);
317-
self.visit_item_inner(i, None, Some(def_id));
325+
let i = tcx.hir().item(i);
326+
self.visit_item_inner(i, None, Some(def_id), true);
318327
}
319328
self.inlining = prev;
320-
true
329+
let inline = self.ignored_items - prev_ignored == 0;
330+
self.ignored_items = prev_ignored;
331+
inline
321332
}
322333
Node::Item(it) if !glob => {
323334
let prev = mem::replace(&mut self.inlining, true);
324-
self.visit_item_inner(it, renamed, Some(def_id));
335+
self.visit_item_inner(it, renamed, Some(def_id), false);
325336
self.inlining = prev;
326337
true
327338
}
328339
Node::ForeignItem(it) if !glob => {
329340
let prev = mem::replace(&mut self.inlining, true);
330-
self.visit_foreign_item_inner(it, renamed);
341+
self.visit_foreign_item_inner(it, renamed, false);
331342
self.inlining = prev;
332343
true
333344
}
@@ -343,6 +354,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
343354
item: &'tcx hir::Item<'_>,
344355
renamed: Option<Symbol>,
345356
parent_id: Option<LocalDefId>,
357+
from_glob_reexport: bool,
346358
) {
347359
if self.is_importable_from_parent
348360
// If we're inside an item, only impl blocks and `macro_rules!` with the `macro_export`
@@ -355,11 +367,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
355367
_ => false,
356368
}
357369
{
358-
self.modules
359-
.last_mut()
360-
.unwrap()
361-
.items
362-
.insert((item.owner_id.def_id, renamed), (item, renamed, parent_id));
370+
if !from_glob_reexport || !self.cx.tcx.is_doc_hidden(item.owner_id.to_def_id()) {
371+
self.modules.last_mut().unwrap().items.insert(
372+
(item.owner_id.def_id, renamed),
373+
ItemEntry { item, renamed, import_id: parent_id },
374+
);
375+
} else {
376+
self.ignored_items += 1;
377+
}
363378
}
364379
}
365380

@@ -368,6 +383,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
368383
item: &'tcx hir::Item<'_>,
369384
renamed: Option<Symbol>,
370385
import_id: Option<LocalDefId>,
386+
from_glob_reexport: bool,
371387
) {
372388
debug!("visiting item {:?}", item);
373389
if self.inside_body {
@@ -386,7 +402,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
386402
// them up regardless of where they're located.
387403
impl_.of_trait.is_none()
388404
{
389-
self.add_to_current_mod(item, None, None);
405+
self.add_to_current_mod(item, None, None, false);
390406
}
391407
return;
392408
}
@@ -404,7 +420,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
404420
hir::ItemKind::ForeignMod { items, .. } => {
405421
for item in items {
406422
let item = tcx.hir().foreign_item(item.id);
407-
self.visit_foreign_item_inner(item, None);
423+
self.visit_foreign_item_inner(item, None, from_glob_reexport);
408424
}
409425
}
410426
// If we're inlining, skip private items.
@@ -419,6 +435,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
419435
continue;
420436
}
421437

438+
if from_glob_reexport &&
439+
tcx.is_doc_hidden(def_id) &&
440+
let Some(res_def_id) = res.opt_def_id() &&
441+
tcx.is_doc_hidden(res_def_id)
442+
{
443+
self.ignored_items += 1;
444+
continue;
445+
}
446+
422447
let attrs =
423448
tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(item.owner_id.def_id));
424449

@@ -444,7 +469,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
444469
}
445470
}
446471

447-
self.add_to_current_mod(item, renamed, import_id);
472+
self.add_to_current_mod(item, renamed, import_id, false);
448473
}
449474
}
450475
hir::ItemKind::Macro(ref macro_def, _) => {
@@ -464,7 +489,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
464489
let nonexported = !tcx.has_attr(def_id, sym::macro_export);
465490

466491
if is_macro_2_0 || nonexported || self.inlining {
467-
self.add_to_current_mod(item, renamed, import_id);
492+
self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
468493
}
469494
}
470495
hir::ItemKind::Mod(ref m) => {
@@ -483,7 +508,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
483508
| hir::ItemKind::Static(..)
484509
| hir::ItemKind::Trait(..)
485510
| hir::ItemKind::TraitAlias(..) => {
486-
self.add_to_current_mod(item, renamed, import_id);
511+
self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
487512
}
488513
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
489514
origin: hir::OpaqueTyOrigin::AsyncFn(_) | hir::OpaqueTyOrigin::FnReturn(_),
@@ -495,14 +520,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
495520
// Underscore constants do not correspond to a nameable item and
496521
// so are never useful in documentation.
497522
if name != kw::Underscore {
498-
self.add_to_current_mod(item, renamed, import_id);
523+
self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
499524
}
500525
}
501526
hir::ItemKind::Impl(impl_) => {
502527
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
503528
// them up regardless of where they're located.
504529
if !self.inlining && impl_.of_trait.is_none() {
505-
self.add_to_current_mod(item, None, None);
530+
self.add_to_current_mod(item, None, None, false);
506531
}
507532
}
508533
}
@@ -512,10 +537,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
512537
&mut self,
513538
item: &'tcx hir::ForeignItem<'_>,
514539
renamed: Option<Symbol>,
540+
from_glob_reexport: bool,
515541
) {
516542
// If inlining we only want to include public functions.
517543
if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() {
518-
self.modules.last_mut().unwrap().foreigns.push((item, renamed));
544+
if !from_glob_reexport || !self.cx.tcx.is_doc_hidden(item.owner_id.to_def_id()) {
545+
self.modules.last_mut().unwrap().foreigns.push((item, renamed));
546+
} else {
547+
self.ignored_items += 1;
548+
}
519549
}
520550
}
521551

@@ -549,7 +579,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
549579
}
550580

551581
fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
552-
self.visit_item_inner(i, None, None);
582+
self.visit_item_inner(i, None, None, false);
553583
let new_value = self.is_importable_from_parent
554584
&& matches!(
555585
i.kind,

0 commit comments

Comments
 (0)