Skip to content

Commit 867ac8d

Browse files
committed
Respond to comments and start adding tests
1 parent 9328cd1 commit 867ac8d

File tree

8 files changed

+329
-75
lines changed

8 files changed

+329
-75
lines changed

src/librustdoc/clean/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2262,7 +2262,7 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
22622262
name: None,
22632263
attrs: self.attrs.clean(cx),
22642264
source: self.whence.clean(cx),
2265-
def_id: DefId::local(CRATE_DEF_INDEX),
2265+
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
22662266
visibility: self.vis.clean(cx),
22672267
stability: None,
22682268
deprecation: None,

src/librustdoc/clean/types.rs

+36
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,42 @@ pub enum ItemEnum {
279279
}
280280

281281
impl ItemEnum {
282+
/// Some items contain others such as structs (for their fields) and Enums
283+
/// (for their variants). This method returns those contained items.
284+
pub fn inner_items(&self) -> impl Iterator<Item = &Item> {
285+
match self {
286+
StructItem(s) => s.fields.iter(),
287+
UnionItem(u) => u.fields.iter(),
288+
VariantItem(Variant { kind: VariantKind::Struct(v) }) => v.fields.iter(),
289+
EnumItem(e) => e.variants.iter(),
290+
TraitItem(t) => t.items.iter(),
291+
ImplItem(i) => i.items.iter(),
292+
ModuleItem(m) => m.items.iter(),
293+
ExternCrateItem(_, _)
294+
| ImportItem(_)
295+
| FunctionItem(_)
296+
| TypedefItem(_, _)
297+
| OpaqueTyItem(_, _)
298+
| StaticItem(_)
299+
| ConstantItem(_)
300+
| TraitAliasItem(_)
301+
| TyMethodItem(_)
302+
| MethodItem(_)
303+
| StructFieldItem(_)
304+
| VariantItem(_)
305+
| ForeignFunctionItem(_)
306+
| ForeignStaticItem(_)
307+
| ForeignTypeItem
308+
| MacroItem(_)
309+
| ProcMacroItem(_)
310+
| PrimitiveItem(_)
311+
| AssocConstItem(_,_)
312+
| AssocTypeItem(_,_)
313+
| StrippedItem(_)
314+
| KeywordItem(_) => [].iter(),
315+
}
316+
}
317+
282318
pub fn is_associated(&self) -> bool {
283319
match *self {
284320
ItemEnum::TypedefItem(_, _) | ItemEnum::AssocTypeItem(_, _) => true,

src/librustdoc/json/conversions.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//! These from impls are used to create the JSON types which get serialized. They're very close to
2+
//! the `clean` types but with some fields removed or stringified to simplify the output and not
3+
//! expose unstable compiler internals.
4+
15
use std::convert::From;
26

37
use rustc_ast::ast;
@@ -22,7 +26,7 @@ impl From<clean::Item> for Item {
2226
deprecation,
2327
} = item;
2428
Item {
25-
crate_num: def_id.krate.as_u32(),
29+
crate_id: def_id.krate.as_u32(),
2630
name,
2731
source: source.into(),
2832
visibility: visibility.into(),
@@ -329,14 +333,13 @@ impl From<clean::Type> for Type {
329333
ResolvedPath { path, param_names, did, is_generic: _ } => Type::ResolvedPath {
330334
name: path.whole_name(),
331335
id: did.into(),
332-
args: Box::new(path.segments.last().map(|args| args.clone().args.into())),
336+
args: path.segments.last().map(|args| Box::new(args.clone().args.into())),
333337
param_names: param_names
334338
.map(|v| v.into_iter().map(Into::into).collect())
335339
.unwrap_or_default(),
336340
},
337341
Generic(s) => Type::Generic(s),
338342
Primitive(p) => Type::Primitive(p.as_str().to_string()),
339-
// TODO: check if there's a more idiomatic way of calling `into` on Box<T>
340343
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into())),
341344
Tuple(t) => Type::Tuple(t.into_iter().map(Into::into).collect()),
342345
Slice(t) => Type::Slice(Box::new((*t).into())),

src/librustdoc/json/mod.rs

+74-50
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
//! Rustdoc's JSON backend
2+
//!
3+
//! This module contains the logic for rendering a crate as JSON rather than the normal static HTML
4+
//! output. See [the RFC](https://github.com/rust-lang/rfcs/pull/2963) and the [`types`] module
5+
//! docs for usage and details.
6+
17
mod conversions;
28
mod types;
39

410
use std::cell::RefCell;
511
use std::fs::File;
12+
use std::path::PathBuf;
613
use std::rc::Rc;
714

815
use rustc_data_structures::fx::FxHashMap;
@@ -17,10 +24,17 @@ use crate::html::render::cache::ExternalLocation;
1724

1825
#[derive(Clone)]
1926
pub struct JsonRenderer {
27+
/// A mapping of IDs that contains all local items for this crate which gets output as a top
28+
/// level field of the JSON blob.
2029
index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
30+
/// The directory where the blob will be written to.
31+
out_path: PathBuf,
2132
}
2233

2334
impl JsonRenderer {
35+
/// Inserts an item into the index. This should be used rather than directly calling insert on
36+
/// the hashmap because certain items (traits and types) need to have their mappings for trait
37+
/// implementations filled out before they're inserted.
2438
fn insert(&self, item: clean::Item, cache: &Cache) {
2539
let id = item.def_id;
2640
let mut new_item: types::Item = item.into();
@@ -75,33 +89,74 @@ impl JsonRenderer {
7589
})
7690
.unwrap_or_default()
7791
}
92+
93+
fn get_trait_items(&self, cache: &Cache) -> Vec<(types::Id, types::Item)> {
94+
cache
95+
.traits
96+
.iter()
97+
.filter_map(|(id, trait_item)| {
98+
// only need to synthesize items for external traits
99+
if !id.is_local() {
100+
trait_item.items.clone().into_iter().for_each(|i| self.insert(i, cache));
101+
Some((
102+
(*id).into(),
103+
types::Item {
104+
crate_id: id.krate.as_u32(),
105+
name: cache
106+
.paths
107+
.get(&id)
108+
.unwrap_or_else(|| {
109+
cache
110+
.external_paths
111+
.get(&id)
112+
.expect("Trait should either be in local or external paths")
113+
})
114+
.0
115+
.last()
116+
.map(Clone::clone),
117+
visibility: types::Visibility::Public,
118+
kind: types::ItemKind::Trait,
119+
inner: types::ItemEnum::TraitItem(trait_item.clone().into()),
120+
source: None,
121+
docs: Default::default(),
122+
links: Default::default(),
123+
attrs: Default::default(),
124+
deprecation: Default::default(),
125+
},
126+
))
127+
} else {
128+
None
129+
}
130+
})
131+
.collect()
132+
}
78133
}
79134

80135
impl FormatRenderer for JsonRenderer {
81136
fn init(
82137
krate: clean::Crate,
83-
_options: RenderOptions,
138+
options: RenderOptions,
84139
_render_info: RenderInfo,
85140
_edition: Edition,
86141
_cache: &mut Cache,
87142
) -> Result<(Self, clean::Crate), Error> {
88143
debug!("Initializing json renderer");
89-
Ok((JsonRenderer { index: Rc::new(RefCell::new(FxHashMap::default())) }, krate))
144+
Ok((
145+
JsonRenderer {
146+
index: Rc::new(RefCell::new(FxHashMap::default())),
147+
out_path: options.output,
148+
},
149+
krate,
150+
))
90151
}
91152

92153
fn item(&mut self, item: clean::Item, cache: &Cache) -> Result<(), Error> {
93154
use clean::ItemEnum::*;
94-
// Flatten items that recursively store other items by putting their children in the index
95-
match item.inner.clone() {
96-
StructItem(s) => s.fields.into_iter().for_each(|i| self.insert(i, cache)),
97-
UnionItem(u) => u.fields.into_iter().for_each(|i| self.insert(i, cache)),
98-
VariantItem(clean::Variant { kind: clean::VariantKind::Struct(v) }) => {
99-
v.fields.into_iter().for_each(|i| self.insert(i, cache));
100-
}
101-
EnumItem(e) => e.variants.into_iter().for_each(|i| self.item(i, cache).unwrap()),
102-
TraitItem(t) => t.items.into_iter().for_each(|i| self.insert(i, cache)),
103-
ImplItem(i) => i.items.into_iter().for_each(|i| self.insert(i, cache)),
104-
_ => {}
155+
// Flatten items that recursively store other items by inserting them into the index
156+
if let ModuleItem(_) = &item.inner {
157+
// but ignore modules because we handle recursing into them separately
158+
} else {
159+
item.inner.inner_items().for_each(|i| self.item(i.clone(), cache).unwrap())
105160
}
106161
self.insert(item.clone(), cache);
107162
Ok(())
@@ -124,41 +179,7 @@ impl FormatRenderer for JsonRenderer {
124179
fn after_krate(&mut self, krate: &clean::Crate, cache: &Cache) -> Result<(), Error> {
125180
debug!("Done with crate");
126181
let mut index = (*self.index).clone().into_inner();
127-
let trait_items = cache.traits.iter().filter_map(|(id, trait_item)| {
128-
// only need to synthesize items for external traits
129-
if !id.is_local() {
130-
trait_item.items.clone().into_iter().for_each(|i| self.insert(i, cache));
131-
Some((
132-
(*id).into(),
133-
types::Item {
134-
crate_num: id.krate.as_u32(),
135-
name: cache
136-
.paths
137-
.get(&id)
138-
.unwrap_or_else(|| {
139-
cache
140-
.external_paths
141-
.get(&id)
142-
.expect("Trait should either be in local or external paths")
143-
})
144-
.0
145-
.last()
146-
.map(Clone::clone),
147-
visibility: types::Visibility::Public,
148-
kind: types::ItemKind::Trait,
149-
inner: types::ItemEnum::TraitItem(trait_item.clone().into()),
150-
source: None,
151-
docs: Default::default(),
152-
links: Default::default(),
153-
attrs: Default::default(),
154-
deprecation: Default::default(),
155-
},
156-
))
157-
} else {
158-
None
159-
}
160-
});
161-
index.extend(trait_items);
182+
index.extend(self.get_trait_items(cache));
162183
let output = types::Crate {
163184
root: types::Id(String::from("0:0")),
164185
version: krate.version.clone(),
@@ -172,7 +193,7 @@ impl FormatRenderer for JsonRenderer {
172193
.map(|(k, (path, kind))| {
173194
(
174195
k.into(),
175-
types::ItemSummary { crate_num: k.krate.as_u32(), path, kind: kind.into() },
196+
types::ItemSummary { crate_id: k.krate.as_u32(), path, kind: kind.into() },
176197
)
177198
})
178199
.collect(),
@@ -194,7 +215,10 @@ impl FormatRenderer for JsonRenderer {
194215
.collect(),
195216
format_version: 1,
196217
};
197-
serde_json::ser::to_writer_pretty(&File::create("test.json").unwrap(), &output).unwrap();
218+
let mut p = self.out_path.clone();
219+
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
220+
p.set_extension("json");
221+
serde_json::ser::to_writer_pretty(&File::create(p).unwrap(), &output).unwrap();
198222
Ok(())
199223
}
200224

src/librustdoc/json/types.rs

+20-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Rustdoc's JSON output interface
22
//!
3-
//! These types are the public API exposed through the `--output-format json` flag. The [`Crate`][]
3+
//! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
44
//! struct is the root of the JSON blob and all other items are contained within.
55
66
use std::path::PathBuf;
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
1313
/// tools to find or link to them.
1414
#[derive(Clone, Debug, Serialize, Deserialize)]
1515
pub struct Crate {
16-
/// The id of the root [`Module`][] item of the local crate.
16+
/// The id of the root [`Module`] item of the local crate.
1717
pub root: Id,
1818
/// The version string given to `--crate-version`, if any.
1919
pub version: Option<String>,
@@ -22,10 +22,9 @@ pub struct Crate {
2222
/// A collection of all items in the local crate as well as some external traits and their
2323
/// items that are referenced locally.
2424
pub index: FxHashMap<Id, Item>,
25-
/// Maps ids to fully qualified paths (e.g. `["std", "io", "lazy", "Lazy"]` for
26-
/// `std::io::lazy::Lazy`) as well as their `ItemKind`
25+
/// Maps IDs to fully qualified paths and other info helpful for generating links.
2726
pub paths: FxHashMap<Id, ItemSummary>,
28-
/// Maps `crate_num` of items to a crate name and html_root_url if it exists
27+
/// Maps `crate_id` of items to a crate name and html_root_url if it exists.
2928
pub external_crates: FxHashMap<u32, ExternalCrate>,
3029
/// A single version number to be used in the future when making backwards incompatible changes
3130
/// to the JSON output.
@@ -38,31 +37,36 @@ pub struct ExternalCrate {
3837
pub html_root_url: Option<String>,
3938
}
4039

41-
/// For external items (stuff not defined in the local crate), you don't get the same level of
40+
/// For external (not defined in the local crate) items, you don't get the same level of
4241
/// information. This struct should contain enough to generate a link/reference to the item in
4342
/// question, or can be used by a tool that takes the json output of multiple crates to find
4443
/// the actual item definition with all the relevant info.
4544
#[derive(Clone, Debug, Serialize, Deserialize)]
4645
pub struct ItemSummary {
47-
pub crate_num: u32,
46+
/// Can be used to look up the name and html_root_url of the crate this item came from in the
47+
/// `external_crates` map.
48+
pub crate_id: u32,
49+
/// The list of path components for the fully qualified path of this item (e.g.
50+
/// `["std", "io", "lazy", "Lazy"]` for `std::io::lazy::Lazy`).
4851
pub path: Vec<String>,
52+
/// Whether this item is a struct, trait, macro, etc.
4953
pub kind: ItemKind,
5054
}
5155

5256
#[derive(Clone, Debug, Serialize, Deserialize)]
5357
pub struct Item {
54-
/// This can be used as a key to the `external_crates` map of [`Crate`][] to see which crate
58+
/// This can be used as a key to the `external_crates` map of [`Crate`] to see which crate
5559
/// this item came from.
56-
pub crate_num: u32,
60+
pub crate_id: u32,
5761
/// Some items such as impls don't have names.
5862
pub name: Option<String>,
59-
/// The source location of this item. May not be present if it came from a macro expansion,
60-
/// inline assembly, other "virtual" files.
63+
/// The source location of this item (absent if it came from a macro expansion or inline
64+
/// assembly).
6165
pub source: Option<Span>,
62-
/// Usually documented items are all public, but you can tell rustdoc to output private items
66+
/// By default all documented items are public, but you can tell rustdoc to output private items
6367
/// so this field is needed to differentiate.
6468
pub visibility: Visibility,
65-
/// The full docstring of this item.
69+
/// The full markdown docstring of this item.
6670
pub docs: String,
6771
/// This mapping resolves [intradoc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
6872
pub links: FxHashMap<String, Id>,
@@ -71,10 +75,6 @@ pub struct Item {
7175
pub deprecation: Option<Deprecation>,
7276
pub kind: ItemKind,
7377
pub inner: ItemEnum,
74-
// TODO: should we stringify the cfg attrs as well, or should we preserve their structure so
75-
// the consumer doesn't have to parse an arbitrarily nested tree to figure out what platforms
76-
// the item is available on?
77-
// TODO: should we have a "stability" field if it's only used by the standard library?
7878
}
7979

8080
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -97,6 +97,8 @@ pub struct Deprecation {
9797
#[derive(Clone, Debug, Serialize, Deserialize)]
9898
pub enum Visibility {
9999
Public,
100+
/// For the most part items are private by default. The exceptions are associated items of
101+
/// public traits and variants of public enums.
100102
Default,
101103
Crate,
102104
// TODO: Restricted(Id, String),
@@ -336,7 +338,7 @@ pub enum Type {
336338
ResolvedPath {
337339
name: String,
338340
id: Id,
339-
args: Box<Option<GenericArgs>>,
341+
args: Option<Box<GenericArgs>>,
340342
param_names: Vec<GenericBound>,
341343
},
342344
/// Parameterized types
@@ -429,9 +431,6 @@ pub struct Impl {
429431
pub blanket_impl: Option<Type>,
430432
}
431433

432-
// TODO: this is currently broken because imports have the same ID as the module that contains
433-
// them. The only obvious fix is to modify the clean types to renumber imports so that IDs are
434-
// actually unique.
435434
#[serde(rename_all = "snake_case")]
436435
#[derive(Clone, Debug, Serialize, Deserialize)]
437436
pub struct Import {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-include ../tools.mk
2+
3+
tests: *.rs
4+
$(RUSTDOC) $< -o $(TMPDIR) --output-format json
5+
$(PYTHON) check_missing_items.py $(TMPDIR)/$(basename $<).json

0 commit comments

Comments
 (0)