Skip to content

rustdoc: Render where clauses as appropriate #17531

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

Merged
merged 1 commit into from
Sep 29, 2014
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
24 changes: 23 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,18 +641,35 @@ impl Clean<Option<Lifetime>> for ty::Region {
}
}

#[deriving(Clone, Encodable, Decodable, PartialEq)]
pub struct WherePredicate {
pub name: String,
pub bounds: Vec<TyParamBound>
}

impl Clean<WherePredicate> for ast::WherePredicate {
fn clean(&self, cx: &DocContext) -> WherePredicate {
WherePredicate {
name: self.ident.clean(cx),
bounds: self.bounds.clean(cx)
}
}
}

// maybe use a Generic enum and use ~[Generic]?
#[deriving(Clone, Encodable, Decodable, PartialEq)]
pub struct Generics {
pub lifetimes: Vec<Lifetime>,
pub type_params: Vec<TyParam>,
pub where_predicates: Vec<WherePredicate>
}

impl Clean<Generics> for ast::Generics {
fn clean(&self, cx: &DocContext) -> Generics {
Generics {
lifetimes: self.lifetimes.clean(cx),
type_params: self.ty_params.clean(cx),
where_predicates: self.where_clause.predicates.clean(cx)
}
}
}
Expand All @@ -663,6 +680,7 @@ impl<'a> Clean<Generics> for (&'a ty::Generics, subst::ParamSpace) {
Generics {
type_params: me.types.get_slice(space).to_vec().clean(cx),
lifetimes: me.regions.get_slice(space).to_vec().clean(cx),
where_predicates: vec![]
Copy link
Member

Choose a reason for hiding this comment

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

I think this does have the consequence that any inlined documentation across crates will lose any where clause it had before, this may be alleviated over time though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe where clauses are rolled into the types field of ty::Generics for the time being, so where clauses on inlined item documentation will appear in the documentation as the "old" style of bounds (i.e. fn foo<T>() where T: Bar is documented in another crate as fn foo<T: Bar>()). I ran a quick check with a recent nightly's rustdoc and it appears to do this already.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah this behavior is ok, I suspect we'll want a more detailed fix in the future, but it may naturally fall out with a full implementation of where clauses.

}
}
}
Expand Down Expand Up @@ -1260,7 +1278,9 @@ impl Clean<Type> for ty::t {
ty::ty_bare_fn(ref fty) => BareFunction(box BareFunctionDecl {
fn_style: fty.fn_style,
generics: Generics {
lifetimes: Vec::new(), type_params: Vec::new()
lifetimes: Vec::new(),
type_params: Vec::new(),
where_predicates: Vec::new()
},
decl: (ast_util::local_def(0), &fty.sig).clean(cx),
abi: fty.abi.to_string(),
Expand Down Expand Up @@ -1670,6 +1690,7 @@ impl Clean<BareFunctionDecl> for ast::BareFnTy {
generics: Generics {
lifetimes: self.lifetimes.clean(cx),
type_params: Vec::new(),
where_predicates: Vec::new()
},
decl: self.decl.clean(cx),
abi: self.abi.to_string(),
Expand Down Expand Up @@ -2172,6 +2193,7 @@ impl Clean<Item> for ast::Typedef {
generics: Generics {
lifetimes: Vec::new(),
type_params: Vec::new(),
where_predicates: Vec::new()
},
}),
visibility: None,
Expand Down
45 changes: 37 additions & 8 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub struct RawMutableSpace(pub clean::Mutability);
pub struct Stability<'a>(pub &'a Option<clean::Stability>);
/// Wrapper struct for emitting the stability level concisely.
pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
/// Wrapper struct for emitting a where clause from Generics.
pub struct WhereClause<'a>(pub &'a clean::Generics);

/// Wrapper struct for emitting type parameter bounds.
struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]);

impl VisSpace {
pub fn get(&self) -> Option<ast::Visibility> {
Expand All @@ -57,6 +62,19 @@ impl FnStyleSpace {
}
}

impl<'a> fmt::Show for TyParamBounds<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &TyParamBounds(bounds) = self;
for (i, bound) in bounds.iter().enumerate() {
if i > 0 {
try!(f.write(" + ".as_bytes()));
}
try!(write!(f, "{}", *bound));
}
Ok(())
}
}

impl fmt::Show for clean::Generics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
Expand All @@ -73,21 +91,14 @@ impl fmt::Show for clean::Generics {
if self.lifetimes.len() > 0 {
try!(f.write(", ".as_bytes()));
}

for (i, tp) in self.type_params.iter().enumerate() {
if i > 0 {
try!(f.write(", ".as_bytes()))
}
try!(f.write(tp.name.as_bytes()));

if tp.bounds.len() > 0 {
try!(f.write(": ".as_bytes()));
for (i, bound) in tp.bounds.iter().enumerate() {
if i > 0 {
try!(f.write(" + ".as_bytes()));
}
try!(write!(f, "{}", *bound));
}
try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
}

match tp.default {
Expand All @@ -101,6 +112,24 @@ impl fmt::Show for clean::Generics {
}
}

impl<'a> fmt::Show for WhereClause<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &WhereClause(gens) = self;
if gens.where_predicates.len() == 0 {
return Ok(());
}
try!(f.write(" where ".as_bytes()));
for (i, pred) in gens.where_predicates.iter().enumerate() {
if i > 0 {
try!(f.write(", ".as_bytes()));
}
let bounds = pred.bounds.as_slice();
try!(write!(f, "{}: {}", pred.name, TyParamBounds(bounds)));
}
Ok(())
}
}

impl fmt::Show for clean::Lifetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write(self.get_ref().as_bytes()));
Expand Down
28 changes: 16 additions & 12 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use clean;
use doctree;
use fold::DocFolder;
use html::format::{VisSpace, Method, FnStyleSpace, MutableSpace, Stability};
use html::format::{ConciseStability};
use html::format::{ConciseStability, WhereClause};
use html::highlight;
use html::item_type::{ItemType, shortty};
use html::item_type;
Expand Down Expand Up @@ -1609,11 +1609,12 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
fn item_function(w: &mut fmt::Formatter, it: &clean::Item,
f: &clean::Function) -> fmt::Result {
try!(write!(w, "<pre class='rust fn'>{vis}{fn_style}fn \
{name}{generics}{decl}</pre>",
{name}{generics}{decl}{where_clause}</pre>",
vis = VisSpace(it.visibility),
fn_style = FnStyleSpace(f.fn_style),
name = it.name.get_ref().as_slice(),
generics = f.generics,
where_clause = WhereClause(&f.generics),
decl = f.decl));
document(w, it)
}
Expand All @@ -1630,11 +1631,12 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
}

// Output the trait definition
try!(write!(w, "<pre class='rust trait'>{}trait {}{}{} ",
try!(write!(w, "<pre class='rust trait'>{}trait {}{}{}{} ",
VisSpace(it.visibility),
it.name.get_ref().as_slice(),
t.generics,
bounds));
bounds,
WhereClause(&t.generics)));
let required = t.items.iter()
.filter(|m| {
match **m {
Expand Down Expand Up @@ -1718,9 +1720,9 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
match cache.implementors.find(&it.def_id) {
Some(implementors) => {
for i in implementors.iter() {
try!(writeln!(w, "<li>{}<code>impl{} {} for {}</code></li>",
try!(writeln!(w, "<li>{}<code>impl{} {} for {}{}</code></li>",
ConciseStability(&i.stability),
i.generics, i.trait_, i.for_));
i.generics, i.trait_, i.for_, WhereClause(&i.generics)));
}
}
None => {}
Expand All @@ -1746,15 +1748,16 @@ fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
g: &clean::Generics, selfty: &clean::SelfTy,
d: &clean::FnDecl) -> fmt::Result {
write!(w, "{}fn <a href='#{ty}.{name}' class='fnname'>{name}</a>\
{generics}{decl}",
{generics}{decl}{where_clause}",
match fn_style {
ast::UnsafeFn => "unsafe ",
_ => "",
},
ty = shortty(it),
name = it.name.get_ref().as_slice(),
generics = *g,
decl = Method(selfty, d))
decl = Method(selfty, d),
where_clause = WhereClause(g))
}
match meth.inner {
clean::TyMethodItem(ref m) => {
Expand Down Expand Up @@ -1809,10 +1812,11 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,

fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
e: &clean::Enum) -> fmt::Result {
try!(write!(w, "<pre class='rust enum'>{}enum {}{}",
try!(write!(w, "<pre class='rust enum'>{}enum {}{}{}",
VisSpace(it.visibility),
it.name.get_ref().as_slice(),
e.generics));
e.generics,
WhereClause(&e.generics)));
if e.variants.len() == 0 && !e.variants_stripped {
try!(write!(w, " {{}}"));
} else {
Expand Down Expand Up @@ -1916,7 +1920,7 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
if structhead {"struct "} else {""},
it.name.get_ref().as_slice()));
match g {
Some(g) => try!(write!(w, "{}", *g)),
Some(g) => try!(write!(w, "{}{}", *g, WhereClause(g))),
None => {}
}
match ty {
Expand Down Expand Up @@ -2008,7 +2012,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result {
Some(ref ty) => try!(write!(w, "{} for ", *ty)),
None => {}
}
try!(write!(w, "{}</code></h3>", i.impl_.for_));
try!(write!(w, "{}{}</code></h3>", i.impl_.for_, WhereClause(&i.impl_.generics)));
match i.dox {
Some(ref dox) => {
try!(write!(w, "<div class='docblock'>{}</div>",
Expand Down
6 changes: 6 additions & 0 deletions src/test/run-make/rustdoc-where/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-include ../tools.mk

all: verify.sh foo.rs
$(HOST_RPATH_ENV) $(RUSTDOC) -w html -o $(TMPDIR)/doc foo.rs
cp verify.sh $(TMPDIR)
$(call RUN,verify.sh) $(TMPDIR)
26 changes: 26 additions & 0 deletions src/test/run-make/rustdoc-where/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub trait MyTrait {}

pub struct Alpha<A> where A: MyTrait;
pub trait Bravo<B> where B: MyTrait {}
pub fn charlie<C>() where C: MyTrait {}

pub struct Delta<D>;
impl<D> Delta<D> where D: MyTrait {
pub fn delta() {}
}

pub struct Echo<E>;
impl<E> MyTrait for Echo<E> where E: MyTrait {}

pub enum Foxtrot<F> {}
impl<F> MyTrait for Foxtrot<F> where F: MyTrait {}
23 changes: 23 additions & 0 deletions src/test/run-make/rustdoc-where/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
set -e

# $1 is the TMPDIR
DOC=$1/doc/foo

grep "Alpha.*where.*A:.*MyTrait" $DOC/struct.Alpha.html > /dev/null
echo "Alpha"
grep "Bravo.*where.*B:.*MyTrait" $DOC/trait.Bravo.html > /dev/null
echo "Bravo"
grep "charlie.*where.*C:.*MyTrait" $DOC/fn.charlie.html > /dev/null
echo "Charlie"
grep "impl.*Delta.*where.*D:.*MyTrait" $DOC/struct.Delta.html > /dev/null
echo "Delta"
grep "impl.*MyTrait.*for.*Echo.*where.*E:.*MyTrait" $DOC/struct.Echo.html > /dev/null
echo "Echo"
grep "impl.*MyTrait.*for.*Foxtrot.*where.*F:.*MyTrait" $DOC/enum.Foxtrot.html > /dev/null
echo "Foxtrot"

# check "Implementors" section of MyTrait
grep "impl.*MyTrait.*for.*Echo.*where.*E:.*MyTrait" $DOC/trait.MyTrait.html > /dev/null
grep "impl.*MyTrait.*for.*Foxtrot.*where.*F:.*MyTrait" $DOC/trait.MyTrait.html > /dev/null
echo "Implementors OK"