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
Show file tree
Hide file tree
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
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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(),
}
}
}
Expand Down Expand Up @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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(),
Expand All @@ -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,
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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")?;
Expand Down Expand Up @@ -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>")?;
Expand Down Expand Up @@ -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)
}

Expand Down
22 changes: 22 additions & 0 deletions src/librustdoc/html/static/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
}
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading