Skip to content

Commit 02b9701

Browse files
authored
Rollup merge of #99287 - GuillaumeGomez:rustdoc-json-double-export, r=notriddle
[rustdoc-json] JSON no longer inlines Fixes #98007. Fixes #96161. Fixes #83057. Fixes #83720. I took over #93518 and applied the comments and added more tests. There was one thing missing (which is in the second commit): if a non-exported item was used in a public API but not reexported, it was still missing. cc `@CraftSpider` `@Urgau` `@Enselic` r? `@notriddle`
2 parents fa298be + b95b138 commit 02b9701

20 files changed

+194
-35
lines changed

Diff for: src/librustdoc/clean/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>(
21202120
// forcefully don't inline if this is not public or if the
21212121
// #[doc(no_inline)] attribute is present.
21222122
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
2123-
let mut denied = !(visibility.is_public()
2124-
|| (cx.render_options.document_private && is_visible_from_parent_mod))
2123+
let mut denied = cx.output_format.is_json()
2124+
|| !(visibility.is_public()
2125+
|| (cx.render_options.document_private && is_visible_from_parent_mod))
21252126
|| pub_underscore
21262127
|| attrs.iter().any(|a| {
21272128
a.has_name(sym::doc)

Diff for: src/librustdoc/core.rs

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub(crate) struct DocContext<'tcx> {
8181
pub(crate) inlined: FxHashSet<ItemId>,
8282
/// Used by `calculate_doc_coverage`.
8383
pub(crate) output_format: OutputFormat,
84+
/// Used by `strip_private`.
85+
pub(crate) show_coverage: bool,
8486
}
8587

8688
impl<'tcx> DocContext<'tcx> {
@@ -381,6 +383,7 @@ pub(crate) fn run_global_ctxt(
381383
inlined: FxHashSet::default(),
382384
output_format,
383385
render_options,
386+
show_coverage,
384387
};
385388

386389
// Small hack to force the Sized trait to be present.

Diff for: src/librustdoc/json/conversions.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,16 @@ impl JsonRenderer<'_> {
4343
let span = item.span(self.tcx);
4444
let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
4545
let inner = match *item.kind {
46-
clean::StrippedItem(_) | clean::KeywordItem(_) => return None,
46+
clean::KeywordItem(_) => return None,
47+
clean::StrippedItem(ref inner) => {
48+
match &**inner {
49+
// We document non-empty stripped modules as with `Module::is_stripped` set to
50+
// `true`, to prevent contained items from being orphaned for downstream users,
51+
// as JSON does no inlining.
52+
clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
53+
_ => return None,
54+
}
55+
}
4756
_ => from_clean_item(item, self.tcx),
4857
};
4958
Some(Item {
@@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
220229
let header = item.fn_header(tcx);
221230

222231
match *item.kind {
223-
ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx) }),
232+
ModuleItem(m) => {
233+
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
234+
}
224235
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
225236
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
226237
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
@@ -257,8 +268,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
257268
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
258269
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
259270
},
260-
// `convert_item` early returns `None` for striped items and keywords.
261-
StrippedItem(_) | KeywordItem(_) => unreachable!(),
271+
// `convert_item` early returns `None` for stripped items and keywords.
272+
KeywordItem(_) => unreachable!(),
273+
StrippedItem(inner) => {
274+
match *inner {
275+
ModuleItem(m) => ItemEnum::Module(Module {
276+
is_crate,
277+
items: ids(m.items, tcx),
278+
is_stripped: true,
279+
}),
280+
// `convert_item` early returns `None` for stripped items we're not including
281+
_ => unreachable!(),
282+
}
283+
}
262284
ExternCrateItem { ref src } => ItemEnum::ExternCrate {
263285
name: name.as_ref().unwrap().to_string(),
264286
rename: src.map(|x| x.to_string()),

Diff for: src/librustdoc/json/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE;
2121
use rustdoc_json_types as types;
2222

2323
use crate::clean::types::{ExternalCrate, ExternalLocation};
24+
use crate::clean::ItemKind;
2425
use crate::config::RenderOptions;
2526
use crate::docfs::PathError;
2627
use crate::error::Error;
@@ -175,6 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
175176
/// the hashmap because certain items (traits and types) need to have their mappings for trait
176177
/// implementations filled out before they're inserted.
177178
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
179+
trace!("rendering {} {:?}", item.type_(), item.name);
180+
181+
// Flatten items that recursively store other items. We include orphaned items from
182+
// stripped modules and etc that are otherwise reachable.
183+
if let ItemKind::StrippedItem(inner) = &*item.kind {
184+
inner.inner_items().for_each(|i| self.item(i.clone()).unwrap());
185+
}
186+
178187
// Flatten items that recursively store other items
179188
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
180189

Diff for: src/librustdoc/passes/strip_private.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
2424
retained: &mut retained,
2525
access_levels: &cx.cache.access_levels,
2626
update_retained: true,
27+
is_json_output: cx.output_format.is_json() && !cx.show_coverage,
2728
};
2829
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
2930
}

Diff for: src/librustdoc/passes/stripper.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@ use rustc_hir::def_id::DefId;
33
use rustc_middle::middle::privacy::AccessLevels;
44
use std::mem;
55

6-
use crate::clean::{self, Item, ItemIdSet};
6+
use crate::clean::{self, Item, ItemId, ItemIdSet};
77
use crate::fold::{strip_item, DocFolder};
88
use crate::formats::cache::Cache;
99

1010
pub(crate) struct Stripper<'a> {
1111
pub(crate) retained: &'a mut ItemIdSet,
1212
pub(crate) access_levels: &'a AccessLevels<DefId>,
1313
pub(crate) update_retained: bool,
14+
pub(crate) is_json_output: bool,
15+
}
16+
17+
impl<'a> Stripper<'a> {
18+
// We need to handle this differently for the JSON output because some non exported items could
19+
// be used in public API. And so, we need these items as well. `is_exported` only checks if they
20+
// are in the public API, which is not enough.
21+
#[inline]
22+
fn is_item_reachable(&self, item_id: ItemId) -> bool {
23+
if self.is_json_output {
24+
self.access_levels.is_reachable(item_id.expect_def_id())
25+
} else {
26+
self.access_levels.is_exported(item_id.expect_def_id())
27+
}
28+
}
1429
}
1530

1631
impl<'a> DocFolder for Stripper<'a> {
@@ -45,9 +60,8 @@ impl<'a> DocFolder for Stripper<'a> {
4560
| clean::TraitAliasItem(..)
4661
| clean::MacroItem(..)
4762
| clean::ForeignTypeItem => {
48-
if i.item_id.is_local()
49-
&& !self.access_levels.is_exported(i.item_id.expect_def_id())
50-
{
63+
let item_id = i.item_id;
64+
if item_id.is_local() && !self.is_item_reachable(item_id) {
5165
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
5266
return None;
5367
}

Diff for: src/librustdoc/visit_ast.rs

+4
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
190190
) -> bool {
191191
debug!("maybe_inline_local res: {:?}", res);
192192

193+
if self.cx.output_format.is_json() {
194+
return false;
195+
}
196+
193197
let tcx = self.cx.tcx;
194198
let Some(res_did) = res.opt_def_id() else {
195199
return false;

Diff for: src/rustdoc-json-types/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::path::PathBuf;
99
use serde::{Deserialize, Serialize};
1010

1111
/// rustdoc format-version.
12-
pub const FORMAT_VERSION: u32 = 15;
12+
pub const FORMAT_VERSION: u32 = 16;
1313

1414
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
1515
/// about the language items in the local crate, as well as info about external items to allow
@@ -245,6 +245,9 @@ pub enum ItemEnum {
245245
pub struct Module {
246246
pub is_crate: bool,
247247
pub items: Vec<Id>,
248+
/// If `true`, this module is not part of the public API, but it contains
249+
/// items that are re-exported as public API.
250+
pub is_stripped: bool,
248251
}
249252

250253
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

Diff for: src/test/rustdoc-json/doc_hidden_failure.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Regression test for <https://github.com/rust-lang/rust/issues/98007>.
2+
3+
#![feature(no_core)]
4+
#![no_core]
5+
6+
mod auto {
7+
mod action_row {
8+
pub struct ActionRowBuilder;
9+
}
10+
11+
#[doc(hidden)]
12+
pub mod builders {
13+
pub use super::action_row::ActionRowBuilder;
14+
}
15+
}
16+
17+
// @count doc_hidden_failure.json "$.index[*][?(@.name=='builders')]" 2
18+
pub use auto::*;
19+
20+
pub mod builders {
21+
pub use crate::auto::builders::*;
22+
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Foo;

Diff for: src/test/rustdoc-json/reexport/glob_extern.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
#![no_core]
44
#![feature(no_core)]
55

6-
// @!has glob_extern.json "$.index[*][?(@.name=='mod1')]"
6+
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
7+
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
78
mod mod1 {
89
extern "C" {
9-
// @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id"
10+
// @has - "$.index[*][?(@.name=='public_fn')].id"
1011
pub fn public_fn();
1112
// @!has - "$.index[*][?(@.name=='private_fn')]"
1213
fn private_fn();
1314
}
1415
}
1516

16-
// @has - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $public_fn_id
17+
// @is - "$.index[*][?(@.kind=='import')].inner.glob" true
1718
pub use mod1::*;

Diff for: src/test/rustdoc-json/reexport/glob_private.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,30 @@
33
#![no_core]
44
#![feature(no_core)]
55

6-
// @!has glob_private.json "$.index[*][?(@.name=='mod1')]"
6+
// @is glob_private.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
7+
// @is glob_private.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
78
mod mod1 {
8-
// @!has - "$.index[*][?(@.name=='mod2')]"
9+
// @is - "$.index[*][?(@.name=='mod2')].kind" \"module\"
10+
// @is - "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true"
911
mod mod2 {
1012
// @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id"
1113
pub struct Mod2Public;
1214

1315
// @!has - "$.index[*][?(@.name=='Mod2Private')]"
1416
struct Mod2Private;
1517
}
18+
19+
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]"
1620
pub use self::mod2::*;
1721

1822
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
1923
pub struct Mod1Public;
20-
2124
// @!has - "$.index[*][?(@.name=='Mod1Private')]"
2225
struct Mod1Private;
2326
}
27+
28+
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]"
2429
pub use mod1::*;
2530

26-
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id
27-
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id
31+
// @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id
32+
// @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id

Diff for: src/test/rustdoc-json/reexport/in_root_and_mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#![feature(no_core)]
22
#![no_core]
33

4+
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].kind" \"module\"
5+
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].inner.is_stripped" "true"
46
mod foo {
5-
// @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id"
7+
// @has - "$.index[*][?(@.name=='Foo')]"
68
pub struct Foo;
79
}
810

9-
// @has - "$.index[*][?(@.name=='in_root_and_mod')].inner.items[*]" $foo_id
11+
// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='foo::Foo')]"
1012
pub use foo::Foo;
1113

1214
pub mod bar {
13-
// @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id
15+
// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]"
1416
pub use crate::foo::Foo;
1517
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// aux-build:pub-struct.rs
2+
3+
// Test for the ICE in rust/83057
4+
// Am external type re-exported with different attributes shouldn't cause an error
5+
6+
#![no_core]
7+
#![feature(no_core)]
8+
9+
extern crate pub_struct as foo;
10+
11+
#[doc(inline)]
12+
pub use foo::Foo;
13+
14+
pub mod bar {
15+
pub use foo::Foo;
16+
}
17+
18+
// @count private_twice_one_inline.json "$.index[*][?(@.kind=='import')]" 2

Diff for: src/test/rustdoc-json/reexport/private_two_names.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test for the ICE in rust/83720
2+
// A pub-in-private type re-exported under two different names shouldn't cause an error
3+
4+
#![no_core]
5+
#![feature(no_core)]
6+
7+
// @is private_two_names.json "$.index[*][?(@.name=='style')].kind" \"module\"
8+
// @is private_two_names.json "$.index[*][?(@.name=='style')].inner.is_stripped" "true"
9+
mod style {
10+
// @has - "$.index[*](?(@.name=='Color'))"
11+
pub struct Color;
12+
}
13+
14+
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')]"
15+
pub use style::Color;
16+
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')]"
17+
pub use style::Color as Colour;

Diff for: src/test/rustdoc-json/reexport/rename_private.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
#![no_core]
44
#![feature(no_core)]
5-
// @!has rename_private.json "$.index[*][?(@.name=='inner')]"
5+
6+
// @is rename_private.json "$.index[*][?(@.name=='inner')].kind" \"module\"
7+
// @is rename_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
68
mod inner {
7-
// @!has - "$.index[*][?(@.name=='Public')]"
9+
// @has - "$.index[*][?(@.name=='Public')]"
810
pub struct Public;
911
}
1012

11-
// @set newname_id = - "$.index[*][?(@.name=='NewName')].id"
12-
// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\"
13-
// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id
13+
// @is - "$.index[*][?(@.kind=='import')].inner.name" \"NewName\"
1414
pub use inner::Public as NewName;

Diff for: src/test/rustdoc-json/reexport/same_type_reexported_more_than_once.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
// Regression test for https://github.com/rust-lang/rust/issues/97432.
1+
// Regression test for <https://github.com/rust-lang/rust/issues/97432>.
22

33
#![feature(no_core)]
44
#![no_std]
55
#![no_core]
66

77
// @has same_type_reexported_more_than_once.json
8-
// @set trait_id = - "$.index[*][?(@.name=='Trait')].id"
9-
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $trait_id
8+
// @has - "$.index[*][?(@.name=='Trait')]"
109
pub use inner::Trait;
11-
// @set reexport_id = - "$.index[*][?(@.name=='Reexport')].id"
12-
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id
10+
// @has - "$.index[*].inner[?(@.name=='Reexport')].id"
1311
pub use inner::Trait as Reexport;
1412

1513
mod inner {

Diff for: src/test/rustdoc-json/reexport/simple_private.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// edition:2018
2-
32
#![no_core]
43
#![feature(no_core)]
54

6-
// @!has simple_private.json "$.index[*][?(@.name=='inner')]"
5+
// @is simple_private.json "$.index[*][?(@.name=='inner')].kind" \"module\"
6+
// @is simple_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
77
mod inner {
88
// @set pub_id = - "$.index[*][?(@.name=='Public')].id"
99
pub struct Public;
1010
}
1111

12-
// @has - "$.index[*][?(@.name=='simple_private')].inner.items[*]" $pub_id
12+
// @is - "$.index[*][?(@.kind=='import')].inner.name" \"Public\"
1313
pub use inner::Public;
14+
15+
// @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id

Diff for: src/test/rustdoc-json/return_private.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Regression test for <https://github.com/rust-lang/rust/issues/96161>.
2+
// ignore-tidy-linelength
3+
4+
#![feature(no_core)]
5+
#![no_core]
6+
7+
mod secret {
8+
pub struct Secret;
9+
}
10+
11+
// @is return_private.json "$.index[*][?(@.name=='get_secret')].kind" \"function\"
12+
// @is return_private.json "$.index[*][?(@.name=='get_secret')].inner.decl.output.inner.name" \"secret::Secret\"
13+
pub fn get_secret() -> secret::Secret {
14+
secret::Secret
15+
}

0 commit comments

Comments
 (0)