Skip to content

Render cfg(feature) requirements in documentation #44994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ use syntax::ast;
use syntax::attr;
use syntax::codemap::Spanned;
use syntax::ptr::P;
use syntax::symbol::keywords;
use syntax::symbol::{keywords, InternedString};
use syntax_pos::{self, DUMMY_SP, Pos};

use rustc::middle::const_val::ConstVal;
@@ -121,6 +121,7 @@ pub struct Crate {
// Only here so that they can be filtered through the rustdoc passes.
pub external_traits: FxHashMap<DefId, Trait>,
pub masked_crates: FxHashSet<CrateNum>,
pub cfg_feature_map: ConfigFeatureMap,
}

impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
@@ -190,6 +191,7 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())),
external_traits: mem::replace(&mut external_traits, Default::default()),
masked_crates,
cfg_feature_map: Default::default(),
}
}
}
@@ -3054,3 +3056,46 @@ impl Clean<TypeBinding> for hir::TypeBinding {
}
}
}

#[derive(Default, Clone, Debug)]
pub struct ConfigFeatureMap {
/// A map from item ids the set of features directly or inherited from
/// parent items that are required for a given item to be included in the
/// output crate.
all: FxHashMap<DefId, FxHashSet<InternedString>>,

/// A map from item ids to the set of features directly required for the
/// item to be included in the output crate.
introduced: FxHashMap<DefId, FxHashSet<InternedString>>,

/// A helper for returning empty results.
empty: FxHashSet<InternedString>,
}

impl ConfigFeatureMap {
pub fn set_all(&mut self, id: DefId, all: FxHashSet<InternedString>) {
if !all.is_empty() {
self.all.insert(id, all);
}
}

pub fn set_introduced(&mut self, id: DefId, introduced: FxHashSet<InternedString>) {
if !introduced.is_empty() {
self.introduced.insert(id, introduced);
}
}

pub fn all<'a>(&'a self, id: DefId) -> impl ExactSizeIterator<Item=&'a str> + 'a {
self.all.get(&id)
.unwrap_or(&self.empty)
.iter()
.map(|s| s.as_ref())
}

pub fn introduced<'a>(&'a self, id: DefId) -> impl ExactSizeIterator<Item=&'a str> + 'a {
self.introduced.get(&id)
.unwrap_or(&self.empty)
.iter()
.map(|s| s.as_ref())
}
}
71 changes: 67 additions & 4 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ use rustc::hir;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::flock;

use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability, Span};
use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability, Span, ConfigFeatureMap};
use doctree;
use fold::DocFolder;
use html::escape::Escape;
@@ -256,6 +256,10 @@ pub struct Cache {
// the access levels from crateanalysis.
pub access_levels: Arc<AccessLevels<DefId>>,

/// This map contains information about which #[cfg(feature)]'s are required
/// to be active for a given item to be included in the crate.
pub cfg_feature_map: ConfigFeatureMap,

// Private fields only used when initially crawling a crate to build a cache

stack: Vec<String>,
@@ -541,6 +545,7 @@ pub fn run(mut krate: clean::Crate,
owned_box_did,
masked_crates: mem::replace(&mut krate.masked_crates, FxHashSet()),
typarams: external_typarams,
cfg_feature_map: mem::replace(&mut krate.cfg_feature_map, Default::default()),
};

// Cache where all our extern crates are located
@@ -1695,6 +1700,7 @@ impl<'a> fmt::Display for Item<'a> {

write!(fmt, "</span>")?; // in-band
write!(fmt, "<span class='out-of-band'>")?;
render_all_cfg_features(fmt, self.item.def_id)?;
if let Some(version) = self.item.stable_since() {
write!(fmt, "<span class='since' title='Stable since Rust version {0}'>{0}</span>",
version)?;
@@ -2071,13 +2077,19 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
_ => "",
};

let mut features = String::new();
render_introduced_cfg_features(&mut features, myitem.def_id)?;

let doc_value = myitem.doc_value().unwrap_or("");
write!(w, "
<tr class='{stab} module-item'>
<td><a class=\"{class}\" href=\"{href}\"
title='{title_type} {title}'>{name}</a>{unsafety_flag}</td>
<td class='docblock-short'>
{stab_docs} {docs}
<span class='out-of-band'>
{features}
</span>
</td>
</tr>",
name = *myitem.name.as_ref().unwrap(),
@@ -2089,6 +2101,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
} else {
format!("{}", MarkdownSummaryLine(doc_value))
},
features = features,
class = myitem.type_(),
stab = myitem.stability_class().unwrap_or("".to_string()),
unsafety_flag = unsafety_flag,
@@ -2362,6 +2375,9 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
ns_id = ns_id)?;
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl)?;
write!(w, "</code>")?;
write!(w, "<span class='out-of-band'>")?;
render_introduced_cfg_features(w, m.def_id)?;
write!(w, "</span>")?;
render_stability_since(w, m, t)?;
write!(w, "</span></h3>")?;
document(w, cx, m)?;
@@ -2502,7 +2518,9 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
write!(w, ";</span>")?;
}
}
writeln!(w, "</code></li>")?;
writeln!(w, "</code><span class='out-of-band'>")?;
render_all_cfg_features(w, implementor.def_id)?;
writeln!(w, "</span></li>")?;
}
} else {
// even without any implementations to write in, we still want the heading and list, so the
@@ -2584,6 +2602,41 @@ fn render_stability_since_raw<'a>(w: &mut fmt::Formatter,
Ok(())
}

fn render_all_cfg_features(w: &mut fmt::Write, def_id: DefId) -> fmt::Result {
let cache = cache();

let cfg_features = cache.cfg_feature_map.all(def_id);
if !cfg_features.is_empty() {
let s = if cfg_features.len() == 1 { "" } else { "s" };
let mut features = cfg_features.fold(String::new(), |acc, f| acc + f + ", ");
features.pop();
features.pop();
write!(w,
"<div class='cfg-feature' title='Requires feature{0} {1}'>{1}</div>",
s, features)?;
}

Ok(())
}

fn render_introduced_cfg_features(w: &mut fmt::Write, def_id: DefId) -> fmt::Result {
let cache = cache();

let introduced = cache.cfg_feature_map.introduced(def_id);
if !introduced.is_empty() {
let s = if introduced.len() == 1 { "" } else { "s" };
let mut features = introduced.fold(String::new(), |acc, f| acc + f + ", ");
features.pop();
features.pop();
write!(w,
"<div class='cfg-feature cfg-feature-introduced' \
title='Requires feature{0} {1}'>{1}</div>",
s, features)?;
}

Ok(())
}

fn render_stability_since(w: &mut fmt::Formatter,
item: &clean::Item,
containing_item: &clean::Item) -> fmt::Result {
@@ -2708,13 +2761,15 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
write!(w, "<span id=\"{id}\" class=\"{item_type} small-section-header\">
<a href=\"#{id}\" class=\"anchor field\"></a>
<span id=\"{ns_id}\" class='invisible'>
<code>{name}: {ty}</code>
</span></span>",
<code>{name}: {ty}</code>",
item_type = ItemType::StructField,
id = id,
ns_id = ns_id,
name = field.name.as_ref().unwrap(),
ty = ty)?;
write!(w, "<span class='out-of-band'>")?;
render_introduced_cfg_features(w, field.def_id)?;
write!(w, "</span></span></span>")?;
if let Some(stability_class) = field.stability_class() {
write!(w, "<span class='stab {stab}'></span>",
stab = stability_class)?;
@@ -2816,6 +2871,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
write!(w, "}}")?;
}
write!(w, "</pre>")?;
render_introduced_cfg_features(w, it.def_id)?;

document(w, cx, it)?;
if !e.variants.is_empty() {
@@ -2886,6 +2942,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
write!(w, "</table></span>")?;
}
render_stability_since(w, variant, it)?;
render_introduced_cfg_features(w, variant.def_id)?;
}
}
render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?;
@@ -3180,10 +3237,12 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
if let Some(l) = (Item { item: &i.impl_item, cx: cx }).src_href() {
write!(w, "<div class='ghost'></div>")?;
render_stability_since_raw(w, since, outer_version)?;
render_introduced_cfg_features(w, i.impl_item.def_id)?;
write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>",
l, "goto source code")?;
} else {
render_stability_since_raw(w, since, outer_version)?;
render_introduced_cfg_features(w, i.impl_item.def_id)?;
}
write!(w, "</span>")?;
write!(w, "</h3>\n")?;
@@ -3238,6 +3297,9 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
write!(w, "<code>")?;
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?;
write!(w, "</code>")?;
write!(w, "<span class='out-of-band'>")?;
render_introduced_cfg_features(w, item.def_id)?;
write!(w, "</span>")?;
if let Some(l) = (Item { cx, item }).src_href() {
write!(w, "</span><span class='out-of-band'>")?;
write!(w, "<div class='ghost'></div>")?;
@@ -3697,6 +3759,7 @@ fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
None,
None,
None))?;
render_introduced_cfg_features(w, it.def_id)?;
document(w, cx, it)
}

22 changes: 22 additions & 0 deletions src/librustdoc/html/static/rustdoc.css
Original file line number Diff line number Diff line change
@@ -598,6 +598,19 @@ body.blur > :not(#help) {
top: 0;
}

.cfg-feature {
padding-left: 10px;
font-family: "Comic Sans MS", monospace;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comic Sans MS
welp.

font-weight: normal;
font-size: initial;
color: purple;
display: inline-block;
}

.cfg-feature-introduced {
color: red;
}

.variants_table {
width: 100%;
}
@@ -699,12 +712,21 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
height: 12px;
}

.cfg-feature + .srclink {
padding-left: 10px;
}

span.since {
position: initial;
font-size: 20px;
margin-right: 5px;
}

span.cfg-feature {
position: initial;
margin-right: 5px;
}

.toggle-wrapper > .collapse-toggle {
left: 0;
}
2 changes: 2 additions & 0 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
#![feature(unicode)]
#![feature(vec_remove_item)]
#![feature(ascii_ctype)]
#![feature(conservative_impl_trait)]
#![feature(exact_size_is_empty)]

extern crate arena;
extern crate getopts;
Loading