Skip to content

Commit 29abe6f

Browse files
authored
Auto merge of #37890 - eddyb:rustdoc-1, r=nrc
rustdoc: separate test collection from the main "clean"-ing pipeline. While reusing the documentation "clean"-ing infrastructure for collecting code examples to test may have seemed appealing at some point, doing the same through a HIR visitor is barely any harder. At the same time, supporting both "regular documentation" and "test collection" modes in `rustdoc::clean` has its cost, requiring any use of a `TyCtxt` to be speculative, and provide some sort of fallback. This simplification is the first step towards bringing rustdoc closer to the compiler, and perhaps even unifying the "local crate" (based on the HIR AST) and "inlinined across crates" (based on crate metadata and typesystem information) implementations of rustdoc. Sadly, not all possible changes to rustdoc will be uncontroversial, so I'm starting small with this patch.
2 parents 696fab8 + 4be7786 commit 29abe6f

12 files changed

+575
-666
lines changed

src/librustdoc/clean/inline.rs

+86-107
Large diffs are not rendered by default.

src/librustdoc/clean/mod.rs

+204-238
Large diffs are not rendered by default.

src/librustdoc/clean/simplify.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext, child: DefId,
153153
if child == trait_ {
154154
return true
155155
}
156-
let predicates = cx.tcx().item_super_predicates(child).predicates;
156+
let predicates = cx.tcx.item_super_predicates(child).predicates;
157157
predicates.iter().filter_map(|pred| {
158158
if let ty::Predicate::Trait(ref pred) = *pred {
159159
if pred.0.trait_ref.self_ty().is_self() {

src/librustdoc/core.rs

+7-35
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10-
pub use self::MaybeTyped::*;
1110

1211
use rustc_lint;
1312
use rustc_driver::{driver, target_features, abort_on_err};
@@ -42,21 +41,12 @@ use html::render::RenderInfo;
4241
pub use rustc::session::config::Input;
4342
pub use rustc::session::search_paths::SearchPaths;
4443

45-
/// Are we generating documentation (`Typed`) or tests (`NotTyped`)?
46-
pub enum MaybeTyped<'a, 'tcx: 'a> {
47-
Typed(TyCtxt<'a, 'tcx, 'tcx>),
48-
NotTyped(&'a session::Session)
49-
}
50-
5144
pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
5245

5346
pub struct DocContext<'a, 'tcx: 'a> {
54-
pub map: &'a hir_map::Map<'tcx>,
55-
pub maybe_typed: MaybeTyped<'a, 'tcx>,
47+
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
5648
pub input: Input,
5749
pub populated_all_crate_impls: Cell<bool>,
58-
pub deref_trait_did: Cell<Option<DefId>>,
59-
pub deref_mut_trait_did: Cell<Option<DefId>>,
6050
// Note that external items for which `doc(hidden)` applies to are shown as
6151
// non-reachable while local items aren't. This is because we're reusing
6252
// the access levels from crateanalysis.
@@ -77,24 +67,9 @@ pub struct DocContext<'a, 'tcx: 'a> {
7767
pub export_map: ExportMap,
7868
}
7969

80-
impl<'b, 'tcx> DocContext<'b, 'tcx> {
81-
pub fn sess<'a>(&'a self) -> &'a session::Session {
82-
match self.maybe_typed {
83-
Typed(tcx) => &tcx.sess,
84-
NotTyped(ref sess) => sess
85-
}
86-
}
87-
88-
pub fn tcx_opt<'a>(&'a self) -> Option<TyCtxt<'a, 'tcx, 'tcx>> {
89-
match self.maybe_typed {
90-
Typed(tcx) => Some(tcx),
91-
NotTyped(_) => None
92-
}
93-
}
94-
95-
pub fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
96-
let tcx_opt = self.tcx_opt();
97-
tcx_opt.expect("tcx not present")
70+
impl<'a, 'tcx> DocContext<'a, 'tcx> {
71+
pub fn sess(&self) -> &session::Session {
72+
&self.tcx.sess
9873
}
9974

10075
/// Call the closure with the given parameters set as
@@ -208,24 +183,21 @@ pub fn run_core(search_paths: SearchPaths,
208183
};
209184

210185
let ctxt = DocContext {
211-
map: &tcx.map,
212-
maybe_typed: Typed(tcx),
186+
tcx: tcx,
213187
input: input,
214188
populated_all_crate_impls: Cell::new(false),
215-
deref_trait_did: Cell::new(None),
216-
deref_mut_trait_did: Cell::new(None),
217189
access_levels: RefCell::new(access_levels),
218190
external_traits: Default::default(),
219191
renderinfo: Default::default(),
220192
ty_substs: Default::default(),
221193
lt_substs: Default::default(),
222194
export_map: export_map,
223195
};
224-
debug!("crate: {:?}", ctxt.map.krate());
196+
debug!("crate: {:?}", tcx.map.krate());
225197

226198
let krate = {
227199
let mut v = RustdocVisitor::new(&ctxt);
228-
v.visit(ctxt.map.krate());
200+
v.visit(tcx.map.krate());
229201
v.clean(&ctxt)
230202
};
231203

src/librustdoc/html/render.rs

+50-53
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use std::sync::Arc;
5353
use externalfiles::ExternalHtml;
5454

5555
use serialize::json::{ToJson, Json, as_json};
56-
use syntax::abi;
56+
use syntax::{abi, ast};
5757
use syntax::feature_gate::UnstableFeatures;
5858
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
5959
use rustc::middle::privacy::AccessLevels;
@@ -62,7 +62,7 @@ use rustc::hir;
6262
use rustc::util::nodemap::{FxHashMap, FxHashSet};
6363
use rustc_data_structures::flock;
6464

65-
use clean::{self, Attributes, GetDefId, SelfTy, Mutability};
65+
use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability};
6666
use doctree;
6767
use fold::DocFolder;
6868
use html::escape::Escape;
@@ -453,30 +453,26 @@ pub fn run(mut krate: clean::Crate,
453453

454454
// Crawl the crate attributes looking for attributes which control how we're
455455
// going to emit HTML
456-
if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) {
457-
for attr in attrs {
458-
match *attr {
459-
clean::NameValue(ref x, ref s)
460-
if "html_favicon_url" == *x => {
456+
if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
457+
for attr in attrs.lists("doc") {
458+
let name = attr.name().map(|s| s.as_str());
459+
match (name.as_ref().map(|s| &s[..]), attr.value_str()) {
460+
(Some("html_favicon_url"), Some(s)) => {
461461
scx.layout.favicon = s.to_string();
462462
}
463-
clean::NameValue(ref x, ref s)
464-
if "html_logo_url" == *x => {
463+
(Some("html_logo_url"), Some(s)) => {
465464
scx.layout.logo = s.to_string();
466465
}
467-
clean::NameValue(ref x, ref s)
468-
if "html_playground_url" == *x => {
466+
(Some("html_playground_url"), Some(s)) => {
469467
markdown::PLAYGROUND.with(|slot| {
470468
let name = krate.name.clone();
471-
*slot.borrow_mut() = Some((Some(name), s.clone()));
469+
*slot.borrow_mut() = Some((Some(name), s.to_string()));
472470
});
473471
}
474-
clean::NameValue(ref x, ref s)
475-
if "issue_tracker_base_url" == *x => {
472+
(Some("issue_tracker_base_url"), Some(s)) => {
476473
scx.issue_tracker_base_url = Some(s.to_string());
477474
}
478-
clean::Word(ref x)
479-
if "html_no_source" == *x => {
475+
(Some("html_no_source"), None) if attr.is_word() => {
480476
scx.include_sources = false;
481477
}
482478
_ => {}
@@ -860,13 +856,16 @@ fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
860856

861857
// Failing that, see if there's an attribute specifying where to find this
862858
// external crate
863-
e.attrs.list("doc").value("html_root_url").map(|url| {
864-
let mut url = url.to_owned();
859+
e.attrs.lists("doc")
860+
.filter(|a| a.check_name("html_root_url"))
861+
.filter_map(|a| a.value_str())
862+
.map(|url| {
863+
let mut url = url.to_string();
865864
if !url.ends_with("/") {
866865
url.push('/')
867866
}
868867
Remote(url)
869-
}).unwrap_or(Unknown) // Well, at least we tried.
868+
}).next().unwrap_or(Unknown) // Well, at least we tried.
870869
}
871870

872871
impl<'a> DocFolder for SourceCollector<'a> {
@@ -2511,49 +2510,47 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
25112510
Ok(())
25122511
}
25132512

2514-
fn attribute_without_value(s: &str) -> bool {
2515-
["must_use", "no_mangle", "unsafe_destructor_blind_to_params"].iter().any(|x| x == &s)
2516-
}
2517-
2518-
fn attribute_with_value(s: &str) -> bool {
2519-
["export_name", "lang", "link_section", "must_use"].iter().any(|x| x == &s)
2520-
}
2521-
2522-
fn attribute_with_values(s: &str) -> bool {
2523-
["repr"].iter().any(|x| x == &s)
2524-
}
2513+
fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
2514+
let name = attr.name();
25252515

2526-
fn render_attribute(attr: &clean::Attribute, recurse: bool) -> Option<String> {
2527-
match *attr {
2528-
clean::Word(ref s) if attribute_without_value(&*s) || recurse => {
2529-
Some(format!("{}", s))
2530-
}
2531-
clean::NameValue(ref k, ref v) if attribute_with_value(&*k) => {
2532-
Some(format!("{} = \"{}\"", k, v))
2533-
}
2534-
clean::List(ref k, ref values) if attribute_with_values(&*k) => {
2535-
let display: Vec<_> = values.iter()
2536-
.filter_map(|value| render_attribute(value, true))
2537-
.map(|entry| format!("{}", entry))
2538-
.collect();
2516+
if attr.is_word() {
2517+
Some(format!("{}", name))
2518+
} else if let Some(v) = attr.value_str() {
2519+
Some(format!("{} = {:?}", name, &v.as_str()[..]))
2520+
} else if let Some(values) = attr.meta_item_list() {
2521+
let display: Vec<_> = values.iter().filter_map(|attr| {
2522+
attr.meta_item().and_then(|mi| render_attribute(mi))
2523+
}).collect();
25392524

2540-
if display.len() > 0 {
2541-
Some(format!("{}({})", k, display.join(", ")))
2542-
} else {
2543-
None
2544-
}
2545-
}
2546-
_ => {
2525+
if display.len() > 0 {
2526+
Some(format!("{}({})", name, display.join(", ")))
2527+
} else {
25472528
None
25482529
}
2530+
} else {
2531+
None
25492532
}
25502533
}
25512534

2535+
const ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
2536+
"export_name",
2537+
"lang",
2538+
"link_section",
2539+
"must_use",
2540+
"no_mangle",
2541+
"repr",
2542+
"unsafe_destructor_blind_to_params"
2543+
];
2544+
25522545
fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
25532546
let mut attrs = String::new();
25542547

2555-
for attr in &it.attrs {
2556-
if let Some(s) = render_attribute(attr, false) {
2548+
for attr in &it.attrs.other_attrs {
2549+
let name = attr.name();
2550+
if !ATTRIBUTE_WHITELIST.contains(&&name.as_str()[..]) {
2551+
continue;
2552+
}
2553+
if let Some(s) = render_attribute(attr.meta()) {
25572554
attrs.push_str(&format!("#[{}]\n", s));
25582555
}
25592556
}
@@ -2810,7 +2807,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
28102807
}
28112808
write!(w, "</span>")?;
28122809
write!(w, "</h3>\n")?;
2813-
if let Some(ref dox) = i.impl_item.attrs.value("doc") {
2810+
if let Some(ref dox) = i.impl_item.doc_value() {
28142811
write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?;
28152812
}
28162813
}

0 commit comments

Comments
 (0)