Skip to content
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

show in docs whether the return type of a function impls Iterator/Read/Write #45039

Merged
merged 7 commits into from
Nov 21, 2017
Merged
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
30 changes: 30 additions & 0 deletions src/doc/unstable-book/src/language-features/doc-spotlight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# `doc_spotlight`

The tracking issue for this feature is: [#45040]

The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,
to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`
attribute to a trait definition will make rustdoc print extra information for functions which return
a type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and
`io::Write` traits in the standard library.

You can do this on your own traits, like this:

```
#![feature(doc_spotlight)]

#[doc(spotlight)]
pub trait MyTrait {}

pub struct MyStruct;
impl MyTrait for MyStruct {}

/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,
/// without having to write that yourself!
pub fn my_fn() -> MyStruct { MyStruct }
```

This feature was originally implemented in PR [#45039].

[#45040]: https://github.com/rust-lang/rust/issues/45040
[#45039]: https://github.com/rust-lang/rust/pull/45039
1 change: 1 addition & 0 deletions src/libcore/iter/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling \
`.iter()` or a similar method"]
#[doc(spotlight)]
pub trait Iterator {
/// The type of the elements being iterated over.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
18 changes: 18 additions & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@
#![feature(const_unsafe_cell_new)]
#![feature(const_cell_new)]
#![feature(const_nonzero_new)]
#![cfg_attr(not(stage0), feature(doc_spotlight))]

#![cfg_attr(not(stage0), feature(const_min_value))]
#![cfg_attr(not(stage0), feature(const_max_value))]
#![cfg_attr(not(stage0), feature(const_atomic_bool_new))]
#![cfg_attr(not(stage0), feature(const_atomic_isize_new))]
#![cfg_attr(not(stage0), feature(const_atomic_usize_new))]
#![cfg_attr(not(stage0), feature(const_atomic_i8_new))]
#![cfg_attr(not(stage0), feature(const_atomic_u8_new))]
#![cfg_attr(not(stage0), feature(const_atomic_i16_new))]
#![cfg_attr(not(stage0), feature(const_atomic_u16_new))]
#![cfg_attr(not(stage0), feature(const_atomic_i32_new))]
#![cfg_attr(not(stage0), feature(const_atomic_u32_new))]
#![cfg_attr(not(stage0), feature(const_atomic_i64_new))]
#![cfg_attr(not(stage0), feature(const_atomic_u64_new))]
#![cfg_attr(not(stage0), feature(const_unsafe_cell_new))]
#![cfg_attr(not(stage0), feature(const_cell_new))]
#![cfg_attr(not(stage0), feature(const_nonzero_new))]

#[prelude_import]
#[allow(unused)]
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,13 @@ pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
let generics = (cx.tcx.generics_of(did), &predicates).clean(cx);
let generics = filter_non_trait_generics(did, generics);
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
let is_spotlight = load_attrs(cx, did).has_doc_flag("spotlight");
clean::Trait {
unsafety: cx.tcx.trait_def(did).unsafety,
generics,
items: trait_items,
bounds: supertrait_bounds,
is_spotlight,
}
}

Expand Down
21 changes: 17 additions & 4 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
match module.inner {
ModuleItem(ref module) => {
for it in &module.items {
if it.is_extern_crate() && it.attrs.has_doc_masked() {
if it.is_extern_crate() && it.attrs.has_doc_flag("masked") {
masked_crates.insert(it.def_id.krate);
}
}
Expand Down Expand Up @@ -596,12 +596,12 @@ impl Attributes {
None
}

pub fn has_doc_masked(&self) -> bool {
pub fn has_doc_flag(&self, flag: &str) -> bool {
for attr in &self.other_attrs {
if !attr.check_name("doc") { continue; }

if let Some(items) = attr.meta_item_list() {
if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name("masked")) {
if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name(flag)) {
return true;
}
}
Expand Down Expand Up @@ -1331,19 +1331,31 @@ impl Clean<FunctionRetTy> for hir::FunctionRetTy {
}
}

impl GetDefId for FunctionRetTy {
fn def_id(&self) -> Option<DefId> {
match *self {
Return(ref ty) => ty.def_id(),
DefaultReturn => None,
}
}
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Trait {
pub unsafety: hir::Unsafety,
pub items: Vec<Item>,
pub generics: Generics,
pub bounds: Vec<TyParamBound>,
pub is_spotlight: bool,
}

impl Clean<Item> for doctree::Trait {
fn clean(&self, cx: &DocContext) -> Item {
let attrs = self.attrs.clean(cx);
let is_spotlight = attrs.has_doc_flag("spotlight");
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
attrs: attrs,
source: self.whence.clean(cx),
def_id: cx.tcx.hir.local_def_id(self.id),
visibility: self.vis.clean(cx),
Expand All @@ -1354,6 +1366,7 @@ impl Clean<Item> for doctree::Trait {
items: self.items.clean(cx),
generics: self.generics.clean(cx),
bounds: self.bounds.clean(cx),
is_spotlight: is_spotlight,
}),
}
}
Expand Down
81 changes: 74 additions & 7 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,7 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
AbiSpace(f.abi),
it.name.as_ref().unwrap(),
f.generics).len();
write!(w, "<pre class='rust fn'>")?;
write!(w, "{}<pre class='rust fn'>", render_spotlight_traits(it)?)?;
render_attributes(w, it)?;
write!(w, "{vis}{constness}{unsafety}{abi}fn \
{name}{generics}{decl}{where_clause}</pre>",
Expand Down Expand Up @@ -2400,8 +2400,9 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
let item_type = m.type_();
let id = derive_id(format!("{}.{}", item_type, name));
let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h3 id='{id}' class='method'>\
write!(w, "{extra}<h3 id='{id}' class='method'>\
<span id='{ns_id}' class='invisible'><code>",
extra = render_spotlight_traits(m)?,
id = id,
ns_id = ns_id)?;
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl)?;
Expand Down Expand Up @@ -2603,10 +2604,10 @@ fn assoc_const(w: &mut fmt::Formatter,
Ok(())
}

fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
bounds: &Vec<clean::TyParamBound>,
default: Option<&clean::Type>,
link: AssocItemLink) -> fmt::Result {
fn assoc_type<W: fmt::Write>(w: &mut W, it: &clean::Item,
bounds: &Vec<clean::TyParamBound>,
default: Option<&clean::Type>,
link: AssocItemLink) -> fmt::Result {
write!(w, "type <a href='{}' class=\"type\">{}</a>",
naive_assoc_href(it, link),
it.name.as_ref().unwrap())?;
Expand Down Expand Up @@ -3236,6 +3237,69 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
}
}

fn render_spotlight_traits(item: &clean::Item) -> Result<String, fmt::Error> {
let mut out = String::new();

match item.inner {
clean::FunctionItem(clean::Function { ref decl, .. }) |
clean::TyMethodItem(clean::TyMethod { ref decl, .. }) |
clean::MethodItem(clean::Method { ref decl, .. }) |
clean::ForeignFunctionItem(clean::Function { ref decl, .. }) => {
out = spotlight_decl(decl)?;
}
_ => {}
}

Ok(out)
}

fn spotlight_decl(decl: &clean::FnDecl) -> Result<String, fmt::Error> {
let mut out = String::new();
let mut trait_ = String::new();

if let Some(did) = decl.output.def_id() {
let c = cache();
if let Some(impls) = c.impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
if impl_.trait_.def_id().and_then(|d| c.traits.get(&d))
.map_or(false, |t| t.is_spotlight) {
if out.is_empty() {
out.push_str(
&format!("<h3 class=\"important\">Important traits for {}</h3>\
<code class=\"content\">",
impl_.for_));
trait_.push_str(&format!("{}", impl_.for_));
}

//use the "where" class here to make it small
out.push_str(&format!("<span class=\"where fmt-newline\">{}</span>", impl_));
let t_did = impl_.trait_.def_id().unwrap();
for it in &impl_.items {
if let clean::TypedefItem(ref tydef, _) = it.inner {
out.push_str("<span class=\"where fmt-newline\"> ");
assoc_type(&mut out, it, &vec![],
Some(&tydef.type_),
AssocItemLink::GotoSource(t_did, &FxHashSet()))?;
out.push_str(";</span>");
}
}
}
}
}
}

if !out.is_empty() {
out.insert_str(0, &format!("<div class=\"important-traits\"><div class='tooltip'>ⓘ\
<span class='tooltiptext'>Important traits for {}</span></div>\
<div class=\"content hidden\">",
trait_));
out.push_str("</code></div></div>");
}

Ok(out)
}

fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink,
render_mode: RenderMode, outer_version: Option<&str>,
show_def_docs: bool) -> fmt::Result {
Expand Down Expand Up @@ -3277,12 +3341,14 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
};

match item.inner {
clean::MethodItem(..) | clean::TyMethodItem(..) => {
clean::MethodItem(clean::Method { ref decl, .. }) |
clean::TyMethodItem(clean::TyMethod{ ref decl, .. }) => {
// Only render when the method is not static or we allow static methods
if render_method_item {
let id = derive_id(format!("{}.{}", item_type, name));
let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
write!(w, "{}", spotlight_decl(decl)?)?;
write!(w, "<span id='{}' class='invisible'>", ns_id)?;
write!(w, "<code>")?;
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?;
Expand Down Expand Up @@ -3329,6 +3395,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi

if render_method_item || render_mode == RenderMode::Normal {
let prefix = render_assoc_const_value(item);

if !is_default_item {
if let Some(t) = trait_ {
// The trait item may have been stripped so we might not
Expand Down
28 changes: 28 additions & 0 deletions src/librustdoc/html/static/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
var help = document.getElementById("help");
switch (getVirtualKey(ev)) {
case "Escape":
hideModal();
var search = document.getElementById("search");
if (!hasClass(help, "hidden")) {
displayHelp(false, ev);
Expand All @@ -228,6 +229,7 @@
case "s":
case "S":
displayHelp(false, ev);
hideModal();
ev.preventDefault();
focusSearchBar();
break;
Expand All @@ -240,6 +242,7 @@

case "?":
if (ev.shiftKey) {
hideModal();
displayHelp(true, ev);
}
break;
Expand Down Expand Up @@ -1713,6 +1716,31 @@
}
});

function showModal(content) {
var modal = document.createElement('div');
modal.id = "important";
addClass(modal, 'modal');
modal.innerHTML = '<div class="modal-content"><div class="close" id="modal-close">✕</div>' +
'<div class="whiter"></div><span class="docblock">' + content +
'</span></div>';
document.getElementsByTagName('body')[0].appendChild(modal);
document.getElementById('modal-close').onclick = hideModal;
modal.onclick = hideModal;
}

function hideModal() {
var modal = document.getElementById("important");
if (modal) {
modal.parentNode.removeChild(modal);
}
}

onEach(document.getElementsByClassName('important-traits'), function(e) {
e.onclick = function() {
showModal(e.lastElementChild.innerHTML);
};
});

var search_input = document.getElementsByClassName("search-input")[0];

if (search_input) {
Expand Down
Loading