Auto merge of #24396 - alexcrichton:rustdoc2, r=aturon
A few final improvements to rustdoc for 1.0: * Improve how rustdoc handles stability * Fix cross-crate macro source links * Improve experience of types inheriting methods through `Deref` Some more details can be found in the commits. [Preview](http://people.mozilla.org/~acrichton/doc/std/)
This commit is contained in:
commit
1014ac44f6
@ -218,15 +218,17 @@ fn build_type(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEn
|
||||
})
|
||||
}
|
||||
|
||||
fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
|
||||
did: ast::DefId) -> Vec<clean::Item> {
|
||||
pub fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
|
||||
did: ast::DefId) -> Vec<clean::Item> {
|
||||
ty::populate_implementations_for_type_if_necessary(tcx, did);
|
||||
let mut impls = Vec::new();
|
||||
|
||||
match tcx.inherent_impls.borrow().get(&did) {
|
||||
None => {}
|
||||
Some(i) => {
|
||||
impls.extend(i.iter().map(|&did| { build_impl(cx, tcx, did) }));
|
||||
for &did in i.iter() {
|
||||
build_impl(cx, tcx, did, &mut impls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,9 +249,9 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
|
||||
|
||||
fn populate_impls(cx: &DocContext, tcx: &ty::ctxt,
|
||||
def: decoder::DefLike,
|
||||
impls: &mut Vec<Option<clean::Item>>) {
|
||||
impls: &mut Vec<clean::Item>) {
|
||||
match def {
|
||||
decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
|
||||
decoder::DlImpl(did) => build_impl(cx, tcx, did, impls),
|
||||
decoder::DlDef(def::DefMod(did)) => {
|
||||
csearch::each_child_of_item(&tcx.sess.cstore,
|
||||
did,
|
||||
@ -262,14 +264,15 @@ fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
impls.into_iter().filter_map(|a| a).collect()
|
||||
return impls;
|
||||
}
|
||||
|
||||
fn build_impl(cx: &DocContext,
|
||||
tcx: &ty::ctxt,
|
||||
did: ast::DefId) -> Option<clean::Item> {
|
||||
pub fn build_impl(cx: &DocContext,
|
||||
tcx: &ty::ctxt,
|
||||
did: ast::DefId,
|
||||
ret: &mut Vec<clean::Item>) {
|
||||
if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) {
|
||||
return None
|
||||
return
|
||||
}
|
||||
|
||||
let attrs = load_attrs(cx, tcx, did);
|
||||
@ -278,13 +281,13 @@ fn build_impl(cx: &DocContext,
|
||||
// If this is an impl for a #[doc(hidden)] trait, be sure to not inline
|
||||
let trait_attrs = load_attrs(cx, tcx, t.def_id);
|
||||
if trait_attrs.iter().any(|a| is_doc_hidden(a)) {
|
||||
return None
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a defaulted impl, then bail out early here
|
||||
if csearch::is_default_impl(&tcx.sess.cstore, did) {
|
||||
return Some(clean::Item {
|
||||
return ret.push(clean::Item {
|
||||
inner: clean::DefaultImplItem(clean::DefaultImpl {
|
||||
// FIXME: this should be decoded
|
||||
unsafety: ast::Unsafety::Normal,
|
||||
@ -352,19 +355,25 @@ fn build_impl(cx: &DocContext,
|
||||
})
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
}).collect::<Vec<_>>();
|
||||
let polarity = csearch::get_impl_polarity(tcx, did);
|
||||
let ty = ty::lookup_item_type(tcx, did);
|
||||
return Some(clean::Item {
|
||||
let trait_ = associated_trait.clean(cx).map(|bound| {
|
||||
match bound {
|
||||
clean::TraitBound(polyt, _) => polyt.trait_,
|
||||
clean::RegionBound(..) => unreachable!(),
|
||||
}
|
||||
});
|
||||
if let Some(clean::ResolvedPath { did, .. }) = trait_ {
|
||||
if Some(did) == cx.deref_trait_did.get() {
|
||||
super::build_deref_target_impls(cx, &trait_items, ret);
|
||||
}
|
||||
}
|
||||
ret.push(clean::Item {
|
||||
inner: clean::ImplItem(clean::Impl {
|
||||
unsafety: ast::Unsafety::Normal, // FIXME: this should be decoded
|
||||
derived: clean::detect_derived(&attrs),
|
||||
trait_: associated_trait.clean(cx).map(|bound| {
|
||||
match bound {
|
||||
clean::TraitBound(polyt, _) => polyt.trait_,
|
||||
clean::RegionBound(..) => unreachable!(),
|
||||
}
|
||||
}),
|
||||
trait_: trait_,
|
||||
for_: ty.ty.clean(cx),
|
||||
generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx),
|
||||
items: trait_items,
|
||||
|
@ -128,6 +128,10 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
|
||||
fn clean(&self, cx: &DocContext) -> Crate {
|
||||
use rustc::session::config::Input;
|
||||
|
||||
if let Some(t) = cx.tcx_opt() {
|
||||
cx.deref_trait_did.set(t.lang_items.deref_trait());
|
||||
}
|
||||
|
||||
let mut externs = Vec::new();
|
||||
cx.sess().cstore.iter_crate_data(|n, meta| {
|
||||
externs.push((n, meta.clean(cx)));
|
||||
@ -313,6 +317,22 @@ impl Item {
|
||||
pub fn is_fn(&self) -> bool {
|
||||
match self.inner { FunctionItem(..) => true, _ => false }
|
||||
}
|
||||
|
||||
pub fn stability_class(&self) -> String {
|
||||
match self.stability {
|
||||
Some(ref s) => {
|
||||
let mut base = match s.level {
|
||||
attr::Unstable => "unstable".to_string(),
|
||||
attr::Stable => String::new(),
|
||||
};
|
||||
if !s.deprecated_since.is_empty() {
|
||||
base.push_str(" deprecated");
|
||||
}
|
||||
base
|
||||
}
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
|
||||
@ -371,7 +391,7 @@ impl Clean<Item> for doctree::Module {
|
||||
items.extend(self.statics.iter().map(|x| x.clean(cx)));
|
||||
items.extend(self.constants.iter().map(|x| x.clean(cx)));
|
||||
items.extend(self.traits.iter().map(|x| x.clean(cx)));
|
||||
items.extend(self.impls.iter().map(|x| x.clean(cx)));
|
||||
items.extend(self.impls.iter().flat_map(|x| x.clean(cx).into_iter()));
|
||||
items.extend(self.macros.iter().map(|x| x.clean(cx)));
|
||||
items.extend(self.def_traits.iter().map(|x| x.clean(cx)));
|
||||
|
||||
@ -1254,6 +1274,7 @@ impl Clean<Item> for ast::ImplItem {
|
||||
ast::MacImplItem(_) => {
|
||||
MacroItem(Macro {
|
||||
source: self.span.to_src(cx),
|
||||
imported_from: None,
|
||||
})
|
||||
}
|
||||
};
|
||||
@ -2169,9 +2190,21 @@ fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool {
|
||||
attr::contains_name(attrs, "automatically_derived")
|
||||
}
|
||||
|
||||
impl Clean<Item> for doctree::Impl {
|
||||
fn clean(&self, cx: &DocContext) -> Item {
|
||||
Item {
|
||||
impl Clean<Vec<Item>> for doctree::Impl {
|
||||
fn clean(&self, cx: &DocContext) -> Vec<Item> {
|
||||
let mut ret = Vec::new();
|
||||
let trait_ = self.trait_.clean(cx);
|
||||
let items = self.items.clean(cx);
|
||||
|
||||
// If this impl block is an implementation of the Deref trait, then we
|
||||
// need to try inlining the target's inherent impl blocks as well.
|
||||
if let Some(ResolvedPath { did, .. }) = trait_ {
|
||||
if Some(did) == cx.deref_trait_did.get() {
|
||||
build_deref_target_impls(cx, &items, &mut ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret.push(Item {
|
||||
name: None,
|
||||
attrs: self.attrs.clean(cx),
|
||||
source: self.whence.clean(cx),
|
||||
@ -2181,12 +2214,66 @@ impl Clean<Item> for doctree::Impl {
|
||||
inner: ImplItem(Impl {
|
||||
unsafety: self.unsafety,
|
||||
generics: self.generics.clean(cx),
|
||||
trait_: self.trait_.clean(cx),
|
||||
trait_: trait_,
|
||||
for_: self.for_.clean(cx),
|
||||
items: self.items.clean(cx),
|
||||
items: items,
|
||||
derived: detect_derived(&self.attrs),
|
||||
polarity: Some(self.polarity.clean(cx)),
|
||||
}),
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
fn build_deref_target_impls(cx: &DocContext,
|
||||
items: &[Item],
|
||||
ret: &mut Vec<Item>) {
|
||||
let tcx = match cx.tcx_opt() {
|
||||
Some(t) => t,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for item in items {
|
||||
let target = match item.inner {
|
||||
TypedefItem(ref t) => &t.type_,
|
||||
_ => continue,
|
||||
};
|
||||
let primitive = match *target {
|
||||
ResolvedPath { did, .. } if ast_util::is_local(did) => continue,
|
||||
ResolvedPath { did, .. } => {
|
||||
ret.extend(inline::build_impls(cx, tcx, did));
|
||||
continue
|
||||
}
|
||||
_ => match target.primitive_type() {
|
||||
Some(prim) => prim,
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
let did = match primitive {
|
||||
Isize => tcx.lang_items.isize_impl(),
|
||||
I8 => tcx.lang_items.i8_impl(),
|
||||
I16 => tcx.lang_items.i16_impl(),
|
||||
I32 => tcx.lang_items.i32_impl(),
|
||||
I64 => tcx.lang_items.i64_impl(),
|
||||
Usize => tcx.lang_items.usize_impl(),
|
||||
U8 => tcx.lang_items.u8_impl(),
|
||||
U16 => tcx.lang_items.u16_impl(),
|
||||
U32 => tcx.lang_items.u32_impl(),
|
||||
U64 => tcx.lang_items.u64_impl(),
|
||||
F32 => tcx.lang_items.f32_impl(),
|
||||
F64 => tcx.lang_items.f64_impl(),
|
||||
Char => tcx.lang_items.char_impl(),
|
||||
Bool => None,
|
||||
Str => tcx.lang_items.str_impl(),
|
||||
Slice => tcx.lang_items.slice_impl(),
|
||||
Array => tcx.lang_items.slice_impl(),
|
||||
PrimitiveTuple => None,
|
||||
PrimitiveRawPointer => tcx.lang_items.const_ptr_impl(),
|
||||
};
|
||||
if let Some(did) = did {
|
||||
if !ast_util::is_local(did) {
|
||||
inline::build_impl(cx, tcx, did, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2541,6 +2628,7 @@ fn resolve_def(cx: &DocContext, id: ast::NodeId) -> Option<ast::DefId> {
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
|
||||
pub struct Macro {
|
||||
pub source: String,
|
||||
pub imported_from: Option<String>,
|
||||
}
|
||||
|
||||
impl Clean<Item> for doctree::Macro {
|
||||
@ -2554,6 +2642,7 @@ impl Clean<Item> for doctree::Macro {
|
||||
def_id: ast_util::local_def(self.id),
|
||||
inner: MacroItem(Macro {
|
||||
source: self.whence.to_src(cx),
|
||||
imported_from: self.imported_from.clean(cx),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use rustc_resolve as resolve;
|
||||
|
||||
use syntax::{ast, ast_map, codemap, diagnostic};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{RefCell, Cell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use visit_ast::RustdocVisitor;
|
||||
@ -48,6 +48,7 @@ pub struct DocContext<'tcx> {
|
||||
pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
|
||||
pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
|
||||
pub populated_crate_impls: RefCell<HashSet<ast::CrateNum>>,
|
||||
pub deref_trait_did: Cell<Option<ast::DefId>>,
|
||||
}
|
||||
|
||||
impl<'tcx> DocContext<'tcx> {
|
||||
@ -77,6 +78,7 @@ pub struct CrateAnalysis {
|
||||
pub external_paths: ExternalPaths,
|
||||
pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
|
||||
pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
|
||||
pub deref_trait_did: Option<ast::DefId>,
|
||||
}
|
||||
|
||||
pub type Externs = HashMap<String, Vec<String>>;
|
||||
@ -147,15 +149,17 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
|
||||
external_paths: RefCell::new(Some(HashMap::new())),
|
||||
inlined: RefCell::new(Some(HashSet::new())),
|
||||
populated_crate_impls: RefCell::new(HashSet::new()),
|
||||
deref_trait_did: Cell::new(None),
|
||||
};
|
||||
debug!("crate: {:?}", ctxt.krate);
|
||||
|
||||
let analysis = CrateAnalysis {
|
||||
let mut analysis = CrateAnalysis {
|
||||
exported_items: exported_items,
|
||||
public_items: public_items,
|
||||
external_paths: RefCell::new(None),
|
||||
external_typarams: RefCell::new(None),
|
||||
inlined: RefCell::new(None),
|
||||
deref_trait_did: None,
|
||||
};
|
||||
|
||||
let krate = {
|
||||
@ -170,5 +174,6 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
|
||||
*analysis.external_typarams.borrow_mut() = map;
|
||||
let map = ctxt.inlined.borrow_mut().take();
|
||||
*analysis.inlined.borrow_mut() = map;
|
||||
analysis.deref_trait_did = ctxt.deref_trait_did.get();
|
||||
(krate, analysis)
|
||||
}
|
||||
|
@ -214,6 +214,7 @@ pub struct Macro {
|
||||
pub attrs: Vec<ast::Attribute>,
|
||||
pub whence: Span,
|
||||
pub stab: Option<attr::Stability>,
|
||||
pub imported_from: Option<Ident>,
|
||||
}
|
||||
|
||||
pub struct ExternCrate {
|
||||
|
@ -23,10 +23,8 @@ use syntax::ast;
|
||||
use syntax::ast_util;
|
||||
|
||||
use clean;
|
||||
use stability_summary::ModuleSummary;
|
||||
use html::item_type::ItemType;
|
||||
use html::render;
|
||||
use html::escape::Escape;
|
||||
use html::render::{cache, CURRENT_LOCATION_KEY};
|
||||
|
||||
/// Helper to render an optional visibility with a space after it (if the
|
||||
@ -45,10 +43,6 @@ pub struct MutableSpace(pub clean::Mutability);
|
||||
/// Similar to VisSpace, but used for mutability
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RawMutableSpace(pub clean::Mutability);
|
||||
/// Wrapper struct for properly emitting the stability level.
|
||||
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.
|
||||
@ -294,9 +288,9 @@ pub fn href(did: ast::DefId) -> Option<(String, ItemType, Vec<String>)> {
|
||||
repeat("../").take(loc.len()).collect::<String>()
|
||||
} else {
|
||||
match cache.extern_locations[&did.krate] {
|
||||
render::Remote(ref s) => s.to_string(),
|
||||
render::Local => repeat("../").take(loc.len()).collect::<String>(),
|
||||
render::Unknown => return None,
|
||||
(_, render::Remote(ref s)) => s.to_string(),
|
||||
(_, render::Local) => repeat("../").take(loc.len()).collect(),
|
||||
(_, render::Unknown) => return None,
|
||||
}
|
||||
};
|
||||
for component in &fqp[..fqp.len() - 1] {
|
||||
@ -385,12 +379,12 @@ fn primitive_link(f: &mut fmt::Formatter,
|
||||
node: ast::CRATE_NODE_ID,
|
||||
}];
|
||||
let loc = match m.extern_locations[&cnum] {
|
||||
render::Remote(ref s) => Some(s.to_string()),
|
||||
render::Local => {
|
||||
(_, render::Remote(ref s)) => Some(s.to_string()),
|
||||
(_, render::Local) => {
|
||||
let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
|
||||
Some(repeat("../").take(len).collect::<String>())
|
||||
}
|
||||
render::Unknown => None,
|
||||
(_, render::Unknown) => None,
|
||||
};
|
||||
match loc {
|
||||
Some(root) => {
|
||||
@ -702,119 +696,3 @@ impl fmt::Display for AbiSpace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Stability<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Stability(stab) = *self;
|
||||
match *stab {
|
||||
Some(ref stability) => {
|
||||
let lvl = if stability.deprecated_since.is_empty() {
|
||||
format!("{}", stability.level)
|
||||
} else {
|
||||
"Deprecated".to_string()
|
||||
};
|
||||
write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
|
||||
lvl = Escape(&*lvl),
|
||||
reason = Escape(&*stability.reason))
|
||||
}
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for ConciseStability<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ConciseStability(stab) = *self;
|
||||
match *stab {
|
||||
Some(ref stability) => {
|
||||
let lvl = if stability.deprecated_since.is_empty() {
|
||||
format!("{}", stability.level)
|
||||
} else {
|
||||
"Deprecated".to_string()
|
||||
};
|
||||
write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
|
||||
lvl = Escape(&*lvl),
|
||||
colon = if !stability.reason.is_empty() { ": " } else { "" },
|
||||
reason = Escape(&*stability.reason))
|
||||
}
|
||||
None => {
|
||||
write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ModuleSummary {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt_inner<'a>(f: &mut fmt::Formatter,
|
||||
context: &mut Vec<&'a str>,
|
||||
m: &'a ModuleSummary)
|
||||
-> fmt::Result {
|
||||
let cnt = m.counts;
|
||||
let tot = cnt.total();
|
||||
if tot == 0 { return Ok(()) }
|
||||
|
||||
context.push(&m.name);
|
||||
let path = context.connect("::");
|
||||
|
||||
try!(write!(f, "<tr>"));
|
||||
try!(write!(f, "<td><a href='{}'>{}</a></td>", {
|
||||
let mut url = context[1..].to_vec();
|
||||
url.push("index.html");
|
||||
url.connect("/")
|
||||
},
|
||||
path));
|
||||
try!(write!(f, "<td class='summary-column'>"));
|
||||
try!(write!(f, "<span class='summary Stable' \
|
||||
style='width: {:.4}%; display: inline-block'> </span>",
|
||||
(100 * cnt.stable) as f64/tot as f64));
|
||||
try!(write!(f, "<span class='summary Unstable' \
|
||||
style='width: {:.4}%; display: inline-block'> </span>",
|
||||
(100 * cnt.unstable) as f64/tot as f64));
|
||||
try!(write!(f, "<span class='summary Deprecated' \
|
||||
style='width: {:.4}%; display: inline-block'> </span>",
|
||||
(100 * cnt.deprecated) as f64/tot as f64));
|
||||
try!(write!(f, "<span class='summary Unmarked' \
|
||||
style='width: {:.4}%; display: inline-block'> </span>",
|
||||
(100 * cnt.unmarked) as f64/tot as f64));
|
||||
try!(write!(f, "</td></tr>"));
|
||||
|
||||
for submodule in &m.submodules {
|
||||
try!(fmt_inner(f, context, submodule));
|
||||
}
|
||||
context.pop();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let mut context = Vec::new();
|
||||
|
||||
let tot = self.counts.total();
|
||||
let (stable, unstable, deprecated, unmarked) = if tot == 0 {
|
||||
(0, 0, 0, 0)
|
||||
} else {
|
||||
((100 * self.counts.stable)/tot,
|
||||
(100 * self.counts.unstable)/tot,
|
||||
(100 * self.counts.deprecated)/tot,
|
||||
(100 * self.counts.unmarked)/tot)
|
||||
};
|
||||
|
||||
try!(write!(f,
|
||||
r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
|
||||
This dashboard summarizes the stability levels for all of the public modules of
|
||||
the crate, according to the total number of items at each level in the module and
|
||||
its children (percentages total for {name}):
|
||||
<blockquote>
|
||||
<a class='stability Stable'></a> stable ({}%),<br/>
|
||||
<a class='stability Unstable'></a> unstable ({}%),<br/>
|
||||
<a class='stability Deprecated'></a> deprecated ({}%),<br/>
|
||||
<a class='stability Unmarked'></a> unmarked ({}%)
|
||||
</blockquote>
|
||||
The counts do not include methods or trait
|
||||
implementations that are visible only through a re-exported type.",
|
||||
stable, unstable, deprecated, unmarked,
|
||||
name=self.name));
|
||||
try!(write!(f, "<table>"));
|
||||
try!(fmt_inner(f, &mut context, self));
|
||||
write!(f, "</table>")
|
||||
}
|
||||
}
|
||||
|
@ -56,19 +56,20 @@ use serialize::json::ToJson;
|
||||
use syntax::abi;
|
||||
use syntax::ast;
|
||||
use syntax::ast_util;
|
||||
use syntax::attr;
|
||||
use rustc::util::nodemap::NodeSet;
|
||||
|
||||
use clean;
|
||||
use doctree;
|
||||
use fold::DocFolder;
|
||||
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace, Stability};
|
||||
use html::format::{ConciseStability, TyParamBounds, WhereClause, href, AbiSpace};
|
||||
use html::escape::Escape;
|
||||
use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
|
||||
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
|
||||
use html::highlight;
|
||||
use html::item_type::ItemType;
|
||||
use html::layout;
|
||||
use html::markdown::Markdown;
|
||||
use html::markdown;
|
||||
use stability_summary;
|
||||
|
||||
/// A pair of name and its optional document.
|
||||
pub type NameDoc = (String, Option<String>);
|
||||
@ -192,7 +193,7 @@ pub struct Cache {
|
||||
pub implementors: HashMap<ast::DefId, Vec<Implementor>>,
|
||||
|
||||
/// Cache of where external crate documentation can be found.
|
||||
pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
|
||||
pub extern_locations: HashMap<ast::CrateNum, (String, ExternalLocation)>,
|
||||
|
||||
/// Cache of where documentation for primitives can be found.
|
||||
pub primitive_locations: HashMap<clean::PrimitiveType, ast::CrateNum>,
|
||||
@ -208,6 +209,7 @@ pub struct Cache {
|
||||
privmod: bool,
|
||||
remove_priv: bool,
|
||||
public_items: NodeSet,
|
||||
deref_trait_did: Option<ast::DefId>,
|
||||
|
||||
// In rare case where a structure is defined in one module but implemented
|
||||
// in another, if the implementing module is parsed before defining module,
|
||||
@ -395,6 +397,7 @@ pub fn run(mut krate: clean::Crate,
|
||||
public_items: public_items,
|
||||
orphan_methods: Vec::new(),
|
||||
traits: mem::replace(&mut krate.external_traits, HashMap::new()),
|
||||
deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did),
|
||||
typarams: analysis.as_ref().map(|a| {
|
||||
a.external_typarams.borrow_mut().take().unwrap()
|
||||
}).unwrap_or(HashMap::new()),
|
||||
@ -402,12 +405,11 @@ pub fn run(mut krate: clean::Crate,
|
||||
a.inlined.borrow_mut().take().unwrap()
|
||||
}).unwrap_or(HashSet::new()),
|
||||
};
|
||||
cache.stack.push(krate.name.clone());
|
||||
krate = cache.fold_crate(krate);
|
||||
|
||||
// Cache where all our extern crates are located
|
||||
for &(n, ref e) in &krate.externs {
|
||||
cache.extern_locations.insert(n, extern_location(e, &cx.dst));
|
||||
cache.extern_locations.insert(n, (e.name.clone(),
|
||||
extern_location(e, &cx.dst)));
|
||||
let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
|
||||
cache.paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
|
||||
}
|
||||
@ -425,6 +427,9 @@ pub fn run(mut krate: clean::Crate,
|
||||
cache.primitive_locations.insert(prim, ast::LOCAL_CRATE);
|
||||
}
|
||||
|
||||
cache.stack.push(krate.name.clone());
|
||||
krate = cache.fold_crate(krate);
|
||||
|
||||
// Build our search index
|
||||
let index = try!(build_index(&krate, &mut cache));
|
||||
|
||||
@ -437,11 +442,8 @@ pub fn run(mut krate: clean::Crate,
|
||||
try!(write_shared(&cx, &krate, &*cache, index));
|
||||
let krate = try!(render_sources(&mut cx, krate));
|
||||
|
||||
// Crawl the crate, building a summary of the stability levels.
|
||||
let summary = stability_summary::build(&krate);
|
||||
|
||||
// And finally render the whole crate's documentation
|
||||
cx.krate(krate, summary)
|
||||
cx.krate(krate)
|
||||
}
|
||||
|
||||
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::Result<String> {
|
||||
@ -645,8 +647,7 @@ fn write_shared(cx: &Context,
|
||||
// going on). If they're in different crates then the crate defining
|
||||
// the trait will be interested in our implementation.
|
||||
if imp.def_id.krate == did.krate { continue }
|
||||
try!(write!(&mut f, r#""{}impl{} {}{} for {}","#,
|
||||
ConciseStability(&imp.stability),
|
||||
try!(write!(&mut f, r#""impl{} {}{} for {}","#,
|
||||
imp.generics,
|
||||
if imp.polarity == Some(clean::ImplPolarity::Negative) { "!" } else { "" },
|
||||
imp.trait_, imp.for_));
|
||||
@ -1071,8 +1072,11 @@ impl DocFolder for Cache {
|
||||
}
|
||||
|
||||
ref t => {
|
||||
t.primitive_type().map(|p| {
|
||||
ast_util::local_def(p.to_node_id())
|
||||
t.primitive_type().and_then(|t| {
|
||||
self.primitive_locations.get(&t).map(|n| {
|
||||
let id = t.to_node_id();
|
||||
ast::DefId { krate: *n, node: id }
|
||||
})
|
||||
})
|
||||
}
|
||||
};
|
||||
@ -1143,38 +1147,13 @@ impl Context {
|
||||
///
|
||||
/// This currently isn't parallelized, but it'd be pretty easy to add
|
||||
/// parallelization to this function.
|
||||
fn krate(mut self, mut krate: clean::Crate,
|
||||
stability: stability_summary::ModuleSummary) -> io::Result<()> {
|
||||
fn krate(self, mut krate: clean::Crate) -> io::Result<()> {
|
||||
let mut item = match krate.module.take() {
|
||||
Some(i) => i,
|
||||
None => return Ok(())
|
||||
};
|
||||
item.name = Some(krate.name);
|
||||
|
||||
// render stability dashboard
|
||||
try!(self.recurse(stability.name.clone(), |this| {
|
||||
let json_dst = &this.dst.join("stability.json");
|
||||
let mut json_out = BufWriter::new(try!(File::create(json_dst)));
|
||||
try!(write!(&mut json_out, "{}", json::as_json(&stability)));
|
||||
|
||||
let mut title = stability.name.clone();
|
||||
title.push_str(" - Stability dashboard");
|
||||
let desc = format!("API stability overview for the Rust `{}` crate.",
|
||||
this.layout.krate);
|
||||
let page = layout::Page {
|
||||
ty: "mod",
|
||||
root_path: &this.root_path,
|
||||
title: &title,
|
||||
description: &desc,
|
||||
keywords: get_basic_keywords(),
|
||||
};
|
||||
let html_dst = &this.dst.join("stability.html");
|
||||
let mut html_out = BufWriter::new(try!(File::create(html_dst)));
|
||||
layout::render(&mut html_out, &this.layout, &page,
|
||||
&Sidebar{ cx: this, item: &item },
|
||||
&stability)
|
||||
}));
|
||||
|
||||
// render the crate documentation
|
||||
let mut work = vec!((self, item));
|
||||
loop {
|
||||
@ -1371,22 +1350,43 @@ impl<'a> Item<'a> {
|
||||
/// may happen, for example, with externally inlined items where the source
|
||||
/// of their crate documentation isn't known.
|
||||
fn href(&self, cx: &Context) -> Option<String> {
|
||||
let href = if self.item.source.loline == self.item.source.hiline {
|
||||
format!("{}", self.item.source.loline)
|
||||
} else {
|
||||
format!("{}-{}", self.item.source.loline, self.item.source.hiline)
|
||||
};
|
||||
|
||||
// First check to see if this is an imported macro source. In this case
|
||||
// we need to handle it specially as cross-crate inlined macros have...
|
||||
// odd locations!
|
||||
let imported_macro_from = match self.item.inner {
|
||||
clean::MacroItem(ref m) => m.imported_from.as_ref(),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(krate) = imported_macro_from {
|
||||
let cache = cache();
|
||||
let root = cache.extern_locations.values().find(|&&(ref n, _)| {
|
||||
*krate == *n
|
||||
}).map(|l| &l.1);
|
||||
let root = match root {
|
||||
Some(&Remote(ref s)) => s.to_string(),
|
||||
Some(&Local) => self.cx.root_path.clone(),
|
||||
None | Some(&Unknown) => return None,
|
||||
};
|
||||
Some(format!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1",
|
||||
root = root,
|
||||
krate = krate,
|
||||
name = self.item.name.as_ref().unwrap()))
|
||||
|
||||
// If this item is part of the local crate, then we're guaranteed to
|
||||
// know the span, so we plow forward and generate a proper url. The url
|
||||
// has anchors for the line numbers that we're linking to.
|
||||
if ast_util::is_local(self.item.def_id) {
|
||||
} else if ast_util::is_local(self.item.def_id) {
|
||||
let mut path = Vec::new();
|
||||
clean_srcpath(&cx.src_root, Path::new(&self.item.source.filename),
|
||||
true, |component| {
|
||||
path.push(component.to_string());
|
||||
});
|
||||
let href = if self.item.source.loline == self.item.source.hiline {
|
||||
format!("{}", self.item.source.loline)
|
||||
} else {
|
||||
format!("{}-{}",
|
||||
self.item.source.loline,
|
||||
self.item.source.hiline)
|
||||
};
|
||||
Some(format!("{root}src/{krate}/{path}.html#{href}",
|
||||
root = self.cx.root_path,
|
||||
krate = self.cx.layout.krate,
|
||||
@ -1408,9 +1408,9 @@ impl<'a> Item<'a> {
|
||||
let cache = cache();
|
||||
let path = &cache.external_paths[&self.item.def_id];
|
||||
let root = match cache.extern_locations[&self.item.def_id.krate] {
|
||||
Remote(ref s) => s.to_string(),
|
||||
Local => self.cx.root_path.clone(),
|
||||
Unknown => return None,
|
||||
(_, Remote(ref s)) => s.to_string(),
|
||||
(_, Local) => self.cx.root_path.clone(),
|
||||
(_, Unknown) => return None,
|
||||
};
|
||||
Some(format!("{root}{path}/{file}?gotosrc={goto}",
|
||||
root = root,
|
||||
@ -1456,21 +1456,8 @@ impl<'a> fmt::Display for Item<'a> {
|
||||
try!(write!(fmt, "<a class='{}' href=''>{}</a>",
|
||||
shortty(self.item), self.item.name.as_ref().unwrap()));
|
||||
|
||||
// Write stability level
|
||||
try!(write!(fmt, "<wbr>{}", Stability(&self.item.stability)));
|
||||
|
||||
try!(write!(fmt, "</span>")); // in-band
|
||||
// Links to out-of-band information, i.e. src and stability dashboard
|
||||
try!(write!(fmt, "<span class='out-of-band'>"));
|
||||
|
||||
// Write stability dashboard link
|
||||
match self.item.inner {
|
||||
clean::ModuleItem(ref m) if m.is_crate => {
|
||||
try!(write!(fmt, "<a href='stability.html'>[stability]</a> "));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
try!(write!(fmt,
|
||||
r##"<span id='render-detail'>
|
||||
<a id="collapse-all" href="#">[-]</a> <a id="expand-all" href="#">[+]</a>
|
||||
@ -1485,7 +1472,8 @@ impl<'a> fmt::Display for Item<'a> {
|
||||
if self.cx.include_sources && !is_primitive {
|
||||
match self.href(self.cx) {
|
||||
Some(l) => {
|
||||
try!(write!(fmt, "<a id='src-{}' href='{}'>[src]</a>",
|
||||
try!(write!(fmt, "<a id='src-{}' class='srclink' \
|
||||
href='{}'>[src]</a>",
|
||||
self.item.def_id.node, l));
|
||||
}
|
||||
None => {}
|
||||
@ -1554,11 +1542,11 @@ fn plain_summary_line(s: Option<&str>) -> String {
|
||||
}
|
||||
|
||||
fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
|
||||
match item.doc_value() {
|
||||
Some(s) => {
|
||||
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
||||
}
|
||||
None => {}
|
||||
if let Some(s) = short_stability(item, true) {
|
||||
try!(write!(w, "<div class='stability'>{}</div>", s));
|
||||
}
|
||||
if let Some(s) = item.doc_value() {
|
||||
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -1593,10 +1581,17 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
||||
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering {
|
||||
let ty1 = shortty(i1);
|
||||
let ty2 = shortty(i2);
|
||||
if ty1 == ty2 {
|
||||
return i1.name.cmp(&i2.name);
|
||||
if ty1 != ty2 {
|
||||
return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
|
||||
}
|
||||
(reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
|
||||
let s1 = i1.stability.as_ref().map(|s| s.level);
|
||||
let s2 = i2.stability.as_ref().map(|s| s.level);
|
||||
match (s1, s2) {
|
||||
(Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater,
|
||||
(Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less,
|
||||
_ => {}
|
||||
}
|
||||
i1.name.cmp(&i2.name)
|
||||
}
|
||||
|
||||
indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
|
||||
@ -1665,19 +1660,27 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
||||
|
||||
_ => {
|
||||
if myitem.name.is_none() { continue }
|
||||
let stab_docs = if let Some(s) = short_stability(myitem, false) {
|
||||
format!("[{}]", s)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
try!(write!(w, "
|
||||
<tr>
|
||||
<td>{stab}<a class='{class}' href='{href}'
|
||||
title='{title}'>{}</a></td>
|
||||
<td class='docblock short'>{}</td>
|
||||
<tr class='{stab} module-item'>
|
||||
<td><a class='{class}' href='{href}'
|
||||
title='{title}'>{name}</a></td>
|
||||
<td class='docblock short'>
|
||||
{stab_docs} {docs}
|
||||
</td>
|
||||
</tr>
|
||||
",
|
||||
*myitem.name.as_ref().unwrap(),
|
||||
Markdown(&shorter(myitem.doc_value())[..]),
|
||||
name = *myitem.name.as_ref().unwrap(),
|
||||
stab_docs = stab_docs,
|
||||
docs = Markdown(&shorter(myitem.doc_value())),
|
||||
class = shortty(myitem),
|
||||
stab = myitem.stability_class(),
|
||||
href = item_path(myitem),
|
||||
title = full_path(cx, myitem),
|
||||
stab = ConciseStability(&myitem.stability)));
|
||||
title = full_path(cx, myitem)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1685,6 +1688,30 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
||||
write!(w, "</table>")
|
||||
}
|
||||
|
||||
fn short_stability(item: &clean::Item, show_reason: bool) -> Option<String> {
|
||||
item.stability.as_ref().and_then(|stab| {
|
||||
let reason = if show_reason && !stab.reason.is_empty() {
|
||||
format!(": {}", stab.reason)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let text = if !stab.deprecated_since.is_empty() {
|
||||
let since = if show_reason {
|
||||
format!(" since {}", Escape(&stab.deprecated_since))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!("Deprecated{}{}", since, Markdown(&reason))
|
||||
} else if stab.level == attr::Unstable {
|
||||
format!("Unstable{}", Markdown(&reason))
|
||||
} else {
|
||||
return None
|
||||
};
|
||||
Some(format!("<em class='stab {}'>{}</em>",
|
||||
item.stability_class(), text))
|
||||
})
|
||||
}
|
||||
|
||||
struct Initializer<'a>(&'a str);
|
||||
|
||||
impl<'a> fmt::Display for Initializer<'a> {
|
||||
@ -1800,10 +1827,10 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
||||
|
||||
fn trait_item(w: &mut fmt::Formatter, m: &clean::Item)
|
||||
-> fmt::Result {
|
||||
try!(write!(w, "<h3 id='{}.{}' class='method'>{}<code>",
|
||||
shortty(m),
|
||||
*m.name.as_ref().unwrap(),
|
||||
ConciseStability(&m.stability)));
|
||||
try!(write!(w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>",
|
||||
ty = shortty(m),
|
||||
name = *m.name.as_ref().unwrap(),
|
||||
stab = m.stability_class()));
|
||||
try!(render_method(w, m, MethodLink::Anchor));
|
||||
try!(write!(w, "</code></h3>"));
|
||||
try!(document(w, m));
|
||||
@ -1844,7 +1871,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
||||
}
|
||||
|
||||
// If there are methods directly on this trait object, render them here.
|
||||
try!(render_methods(w, it));
|
||||
try!(render_methods(w, it.def_id, MethodRender::All));
|
||||
|
||||
let cache = cache();
|
||||
try!(write!(w, "
|
||||
@ -1854,8 +1881,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
||||
match cache.implementors.get(&it.def_id) {
|
||||
Some(implementors) => {
|
||||
for i in implementors {
|
||||
try!(writeln!(w, "<li>{}<code>impl{} {} for {}{}</code></li>",
|
||||
ConciseStability(&i.stability),
|
||||
try!(writeln!(w, "<li><code>impl{} {} for {}{}</code></li>",
|
||||
i.generics, i.trait_, i.for_, WhereClause(&i.generics)));
|
||||
}
|
||||
}
|
||||
@ -1964,9 +1990,10 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
if fields.peek().is_some() {
|
||||
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
|
||||
for field in fields {
|
||||
try!(write!(w, "<tr><td id='structfield.{name}'>\
|
||||
{stab}<code>{name}</code></td><td>",
|
||||
stab = ConciseStability(&field.stability),
|
||||
try!(write!(w, "<tr class='stab {stab}'>
|
||||
<td id='structfield.{name}'>\
|
||||
<code>{name}</code></td><td>",
|
||||
stab = field.stability_class(),
|
||||
name = field.name.as_ref().unwrap()));
|
||||
try!(document(w, field));
|
||||
try!(write!(w, "</td></tr>"));
|
||||
@ -1974,7 +2001,7 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
try!(write!(w, "</table>"));
|
||||
}
|
||||
}
|
||||
render_methods(w, it)
|
||||
render_methods(w, it.def_id, MethodRender::All)
|
||||
}
|
||||
|
||||
fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
@ -2034,8 +2061,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
if !e.variants.is_empty() {
|
||||
try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
|
||||
for variant in &e.variants {
|
||||
try!(write!(w, "<tr><td id='variant.{name}'>{stab}<code>{name}</code></td><td>",
|
||||
stab = ConciseStability(&variant.stability),
|
||||
try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
|
||||
name = variant.name.as_ref().unwrap()));
|
||||
try!(document(w, variant));
|
||||
match variant.inner {
|
||||
@ -2074,7 +2100,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
try!(write!(w, "</table>"));
|
||||
|
||||
}
|
||||
try!(render_methods(w, it));
|
||||
try!(render_methods(w, it.def_id, MethodRender::All));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -2163,27 +2189,61 @@ enum MethodLink {
|
||||
GotoSource(ast::DefId),
|
||||
}
|
||||
|
||||
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
||||
let v = match cache().impls.get(&it.def_id) {
|
||||
Some(v) => v.clone(),
|
||||
enum MethodRender<'a> {
|
||||
All,
|
||||
DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
|
||||
}
|
||||
|
||||
fn render_methods(w: &mut fmt::Formatter,
|
||||
it: ast::DefId,
|
||||
what: MethodRender) -> fmt::Result {
|
||||
let c = cache();
|
||||
let v = match c.impls.get(&it) {
|
||||
Some(v) => v,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let (non_trait, traits): (Vec<_>, _) = v.into_iter()
|
||||
.partition(|i| i.impl_.trait_.is_none());
|
||||
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| {
|
||||
i.impl_.trait_.is_none()
|
||||
});
|
||||
if !non_trait.is_empty() {
|
||||
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
||||
let render_header = match what {
|
||||
MethodRender::All => {
|
||||
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
||||
true
|
||||
}
|
||||
MethodRender::DerefFor { trait_, type_ } => {
|
||||
try!(write!(w, "<h2 id='deref-methods'>Methods from \
|
||||
{}<Target={}></h2>", trait_, type_));
|
||||
false
|
||||
}
|
||||
};
|
||||
for i in &non_trait {
|
||||
try!(render_impl(w, i, MethodLink::Anchor));
|
||||
try!(render_impl(w, i, MethodLink::Anchor, render_header));
|
||||
}
|
||||
}
|
||||
if let MethodRender::DerefFor { .. } = what {
|
||||
return Ok(())
|
||||
}
|
||||
if !traits.is_empty() {
|
||||
let deref_impl = traits.iter().find(|t| {
|
||||
match *t.impl_.trait_.as_ref().unwrap() {
|
||||
clean::ResolvedPath { did, .. } => {
|
||||
Some(did) == c.deref_trait_did
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
});
|
||||
if let Some(impl_) = deref_impl {
|
||||
try!(render_deref_methods(w, impl_));
|
||||
}
|
||||
try!(write!(w, "<h2 id='implementations'>Trait \
|
||||
Implementations</h2>"));
|
||||
let (derived, manual): (Vec<_>, _) = traits.into_iter()
|
||||
.partition(|i| i.impl_.derived);
|
||||
let (derived, manual): (Vec<_>, _) = traits.iter().partition(|i| {
|
||||
i.impl_.derived
|
||||
});
|
||||
for i in &manual {
|
||||
let did = i.trait_did().unwrap();
|
||||
try!(render_impl(w, i, MethodLink::GotoSource(did)));
|
||||
try!(render_impl(w, i, MethodLink::GotoSource(did), true));
|
||||
}
|
||||
if !derived.is_empty() {
|
||||
try!(write!(w, "<h3 id='derived_implementations'>\
|
||||
@ -2191,73 +2251,92 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
||||
</h3>"));
|
||||
for i in &derived {
|
||||
let did = i.trait_did().unwrap();
|
||||
try!(render_impl(w, i, MethodLink::GotoSource(did)));
|
||||
try!(render_impl(w, i, MethodLink::GotoSource(did), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
|
||||
-> fmt::Result {
|
||||
try!(write!(w, "<h3 class='impl'>{}<code>impl{} ",
|
||||
ConciseStability(&i.stability),
|
||||
i.impl_.generics));
|
||||
if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
|
||||
try!(write!(w, "!"));
|
||||
fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result {
|
||||
let deref_type = impl_.impl_.trait_.as_ref().unwrap();
|
||||
let target = impl_.impl_.items.iter().filter_map(|item| {
|
||||
match item.inner {
|
||||
clean::TypedefItem(ref t) => Some(&t.type_),
|
||||
_ => None,
|
||||
}
|
||||
}).next().unwrap();
|
||||
let what = MethodRender::DerefFor { trait_: deref_type, type_: target };
|
||||
match *target {
|
||||
clean::ResolvedPath { did, .. } => render_methods(w, did, what),
|
||||
_ => {
|
||||
if let Some(prim) = target.primitive_type() {
|
||||
if let Some(c) = cache().primitive_locations.get(&prim) {
|
||||
let did = ast::DefId { krate: *c, node: prim.to_node_id() };
|
||||
try!(render_methods(w, did, what));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
if let Some(ref ty) = i.impl_.trait_ {
|
||||
try!(write!(w, "{} for ", *ty));
|
||||
}
|
||||
try!(write!(w, "{}{}</code></h3>", i.impl_.for_,
|
||||
WhereClause(&i.impl_.generics)));
|
||||
if let Some(ref dox) = i.dox {
|
||||
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
|
||||
}
|
||||
|
||||
fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
|
||||
render_header: bool) -> fmt::Result {
|
||||
if render_header {
|
||||
try!(write!(w, "<h3 class='impl'><code>impl{} ",
|
||||
i.impl_.generics));
|
||||
if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
|
||||
try!(write!(w, "!"));
|
||||
}
|
||||
if let Some(ref ty) = i.impl_.trait_ {
|
||||
try!(write!(w, "{} for ", *ty));
|
||||
}
|
||||
try!(write!(w, "{}{}</code></h3>", i.impl_.for_,
|
||||
WhereClause(&i.impl_.generics)));
|
||||
if let Some(ref dox) = i.dox {
|
||||
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
|
||||
}
|
||||
}
|
||||
|
||||
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
|
||||
dox: bool, link: MethodLink) -> fmt::Result {
|
||||
link: MethodLink) -> fmt::Result {
|
||||
match item.inner {
|
||||
clean::MethodItem(..) | clean::TyMethodItem(..) => {
|
||||
try!(write!(w, "<h4 id='method.{}' class='{}'>{}<code>",
|
||||
try!(write!(w, "<h4 id='method.{}' class='{}'><code>",
|
||||
*item.name.as_ref().unwrap(),
|
||||
shortty(item),
|
||||
ConciseStability(&item.stability)));
|
||||
shortty(item)));
|
||||
try!(render_method(w, item, link));
|
||||
try!(write!(w, "</code></h4>\n"));
|
||||
}
|
||||
clean::TypedefItem(ref tydef) => {
|
||||
let name = item.name.as_ref().unwrap();
|
||||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
|
||||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
|
||||
*name,
|
||||
shortty(item),
|
||||
ConciseStability(&item.stability)));
|
||||
shortty(item)));
|
||||
try!(write!(w, "type {} = {}", name, tydef.type_));
|
||||
try!(write!(w, "</code></h4>\n"));
|
||||
}
|
||||
clean::AssociatedTypeItem(ref bounds, ref default) => {
|
||||
let name = item.name.as_ref().unwrap();
|
||||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
|
||||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
|
||||
*name,
|
||||
shortty(item),
|
||||
ConciseStability(&item.stability)));
|
||||
shortty(item)));
|
||||
try!(assoc_type(w, item, bounds, default));
|
||||
try!(write!(w, "</code></h4>\n"));
|
||||
}
|
||||
_ => panic!("can't make docs for trait item with name {:?}", item.name)
|
||||
}
|
||||
match item.doc_value() {
|
||||
Some(s) if dox => {
|
||||
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
||||
Ok(())
|
||||
}
|
||||
Some(..) | None => Ok(())
|
||||
if let MethodLink::Anchor = link {
|
||||
document(w, item)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
try!(write!(w, "<div class='impl-items'>"));
|
||||
for trait_item in i.impl_.items.iter() {
|
||||
try!(doctraititem(w, trait_item, true, link));
|
||||
try!(doctraititem(w, trait_item, link));
|
||||
}
|
||||
|
||||
fn render_default_methods(w: &mut fmt::Formatter,
|
||||
@ -2271,8 +2350,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
|
||||
None => {}
|
||||
}
|
||||
|
||||
try!(doctraititem(w, trait_item, false,
|
||||
MethodLink::GotoSource(did)));
|
||||
try!(doctraititem(w, trait_item, MethodLink::GotoSource(did)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -2380,7 +2458,7 @@ fn item_primitive(w: &mut fmt::Formatter,
|
||||
it: &clean::Item,
|
||||
_p: &clean::PrimitiveType) -> fmt::Result {
|
||||
try!(document(w, it));
|
||||
render_methods(w, it)
|
||||
render_methods(w, it.def_id, MethodRender::All)
|
||||
}
|
||||
|
||||
fn get_basic_keywords() -> &'static str {
|
||||
|
@ -245,6 +245,10 @@ nav.sub {
|
||||
.content .highlighted.tymethod { background-color: #c6afb3; }
|
||||
.content .highlighted.type { background-color: #c6afb3; }
|
||||
|
||||
.docblock.short p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.docblock.short.nowrap {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
@ -337,11 +341,16 @@ nav.sub {
|
||||
/* Shift "where ..." part of method definition down a line */
|
||||
.content .method .where { display: block; }
|
||||
/* Bit of whitespace to indent it */
|
||||
.content .method .where::before { content: ' '; }
|
||||
.content .method .where::before { content: ' '; }
|
||||
|
||||
.content .methods .docblock { margin-left: 40px; }
|
||||
.content .methods > div { margin-left: 40px; }
|
||||
|
||||
.content .impl-items .docblock { margin-left: 40px; }
|
||||
.content .impl-items .docblock, .content .impl-items .stability {
|
||||
margin-left: 40px;
|
||||
}
|
||||
.content .impl-items .method, .content .impl-items .type {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
nav {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
@ -468,31 +477,32 @@ a {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stability {
|
||||
border-left: 6px solid;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
em.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
||||
em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
|
||||
em.stab {
|
||||
display: inline-block;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
padding: 3px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 90%;
|
||||
font-style: normal;
|
||||
}
|
||||
em.stab p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
h1 .stability {
|
||||
text-transform: lowercase;
|
||||
font-weight: 400;
|
||||
margin-left: 14px;
|
||||
padding: 4px 10px;
|
||||
.module-item .stab {
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
.impl-items .stability, .methods .stability {
|
||||
margin-right: 20px;
|
||||
.module-item.unstable {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.stability.Deprecated { border-color: #A071A8; color: #82478C; }
|
||||
.stability.Experimental { border-color: #D46D6A; color: #AA3C39; }
|
||||
.stability.Unstable { border-color: #D4B16A; color: #AA8439; }
|
||||
.stability.Stable { border-color: #54A759; color: #2D8632; }
|
||||
.stability.Frozen { border-color: #009431; color: #007726; }
|
||||
.stability.Locked { border-color: #0084B6; color: #00668c; }
|
||||
.stability.Unmarked { border-color: #BBBBBB; }
|
||||
|
||||
td.summary-column {
|
||||
width: 100%;
|
||||
}
|
||||
@ -500,11 +510,6 @@ td.summary-column {
|
||||
.summary {
|
||||
padding-right: 0px;
|
||||
}
|
||||
.summary.Deprecated { background-color: #A071A8; }
|
||||
.summary.Experimental { background-color: #D46D6A; }
|
||||
.summary.Unstable { background-color: #D4B16A; }
|
||||
.summary.Stable { background-color: #54A759; }
|
||||
.summary.Unmarked { background-color: #BBBBBB; }
|
||||
|
||||
:target { background: #FDFFD3; }
|
||||
.line-numbers :target { background-color: transparent; }
|
||||
@ -555,9 +560,9 @@ pre.rust { position: relative; }
|
||||
.collapse-toggle {
|
||||
font-weight: 300;
|
||||
position: absolute;
|
||||
left: 13px;
|
||||
left: -23px;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.toggle-wrapper > .collapse-toggle {
|
||||
|
@ -802,6 +802,9 @@
|
||||
if (query['gotosrc']) {
|
||||
window.location = $('#src-' + query['gotosrc']).attr('href');
|
||||
}
|
||||
if (query['gotomacrosrc']) {
|
||||
window.location = $('.srclink').attr('href');
|
||||
}
|
||||
|
||||
$("#expand-all").on("click", function() {
|
||||
$(".docblock").show();
|
||||
|
@ -91,7 +91,6 @@ pub mod html {
|
||||
pub mod markdown;
|
||||
pub mod passes;
|
||||
pub mod plugins;
|
||||
pub mod stability_summary;
|
||||
pub mod visit_ast;
|
||||
pub mod test;
|
||||
mod flock;
|
||||
|
@ -1,199 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//! This module crawls a `clean::Crate` and produces a summarization of the
|
||||
//! stability levels within the crate. The summary contains the module
|
||||
//! hierarchy, with item counts for every stability level per module. A parent
|
||||
//! module's count includes its children's.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Add;
|
||||
|
||||
use syntax::attr::{Unstable, Stable};
|
||||
use syntax::ast::Public;
|
||||
|
||||
use clean::{Crate, Item, ModuleItem, Module, EnumItem, Enum};
|
||||
use clean::{ImplItem, Impl, Trait, TraitItem};
|
||||
use clean::{ExternCrateItem, ImportItem, PrimitiveItem, Stability};
|
||||
|
||||
use html::render::cache;
|
||||
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Eq)]
|
||||
/// The counts for each stability level.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Counts {
|
||||
pub deprecated: u64,
|
||||
pub unstable: u64,
|
||||
pub stable: u64,
|
||||
|
||||
/// No stability level, inherited or otherwise.
|
||||
pub unmarked: u64,
|
||||
}
|
||||
|
||||
impl Add for Counts {
|
||||
type Output = Counts;
|
||||
|
||||
fn add(self, other: Counts) -> Counts {
|
||||
Counts {
|
||||
deprecated: self.deprecated + other.deprecated,
|
||||
unstable: self.unstable + other.unstable,
|
||||
stable: self.stable + other.stable,
|
||||
unmarked: self.unmarked + other.unmarked,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Counts {
|
||||
fn zero() -> Counts {
|
||||
Counts {
|
||||
deprecated: 0,
|
||||
unstable: 0,
|
||||
stable: 0,
|
||||
unmarked: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total(&self) -> u64 {
|
||||
self.deprecated + self.unstable + self.stable + self.unmarked
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Eq)]
|
||||
/// A summarized module, which includes total counts and summarized children
|
||||
/// modules.
|
||||
pub struct ModuleSummary {
|
||||
pub name: String,
|
||||
pub counts: Counts,
|
||||
pub submodules: Vec<ModuleSummary>,
|
||||
}
|
||||
|
||||
impl PartialOrd for ModuleSummary {
|
||||
fn partial_cmp(&self, other: &ModuleSummary) -> Option<Ordering> {
|
||||
self.name.partial_cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for ModuleSummary {
|
||||
fn cmp(&self, other: &ModuleSummary) -> Ordering {
|
||||
self.name.cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
||||
// is the item considered publicly visible?
|
||||
fn visible(item: &Item) -> bool {
|
||||
match item.inner {
|
||||
ImplItem(_) => true,
|
||||
_ => item.visibility == Some(Public)
|
||||
}
|
||||
}
|
||||
|
||||
fn count_stability(stab: Option<&Stability>) -> Counts {
|
||||
match stab {
|
||||
None => Counts { unmarked: 1, .. Counts::zero() },
|
||||
Some(ref stab) => {
|
||||
if !stab.deprecated_since.is_empty() {
|
||||
return Counts { deprecated: 1, .. Counts::zero() };
|
||||
}
|
||||
match stab.level {
|
||||
Unstable => Counts { unstable: 1, .. Counts::zero() },
|
||||
Stable => Counts { stable: 1, .. Counts::zero() },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn summarize_methods(item: &Item) -> Counts {
|
||||
match cache().impls.get(&item.def_id) {
|
||||
Some(v) => {
|
||||
v.iter().map(|i| {
|
||||
let count = count_stability(i.stability.as_ref());
|
||||
if i.impl_.trait_.is_none() {
|
||||
count + i.impl_.items.iter()
|
||||
.map(|ti| summarize_item(ti).0)
|
||||
.fold(Counts::zero(), |acc, c| acc + c)
|
||||
} else {
|
||||
count
|
||||
}
|
||||
}).fold(Counts::zero(), |acc, c| acc + c)
|
||||
},
|
||||
None => {
|
||||
Counts::zero()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Produce the summary for an arbitrary item. If the item is a module, include a
|
||||
// module summary. The counts for items with nested items (e.g. modules, traits,
|
||||
// impls) include all children counts.
|
||||
fn summarize_item(item: &Item) -> (Counts, Option<ModuleSummary>) {
|
||||
let item_counts = count_stability(item.stability.as_ref()) + summarize_methods(item);
|
||||
|
||||
// Count this item's children, if any. Note that a trait impl is
|
||||
// considered to have no children.
|
||||
match item.inner {
|
||||
// Require explicit `pub` to be visible
|
||||
ImplItem(Impl { ref items, trait_: None, .. }) => {
|
||||
let subcounts = items.iter().filter(|i| visible(*i))
|
||||
.map(summarize_item)
|
||||
.map(|s| s.0)
|
||||
.fold(Counts::zero(), |acc, x| acc + x);
|
||||
(subcounts, None)
|
||||
}
|
||||
// `pub` automatically
|
||||
EnumItem(Enum { variants: ref subitems, .. }) => {
|
||||
let subcounts = subitems.iter().map(summarize_item)
|
||||
.map(|s| s.0)
|
||||
.fold(Counts::zero(), |acc, x| acc + x);
|
||||
(item_counts + subcounts, None)
|
||||
}
|
||||
TraitItem(Trait { ref items, .. }) => {
|
||||
let subcounts = items.iter().map(summarize_item)
|
||||
.map(|s| s.0)
|
||||
.fold(Counts::zero(), |acc, x| acc + x);
|
||||
(item_counts + subcounts, None)
|
||||
}
|
||||
ModuleItem(Module { ref items, .. }) => {
|
||||
let mut counts = item_counts;
|
||||
let mut submodules = Vec::new();
|
||||
|
||||
for (subcounts, submodule) in items.iter().filter(|i| visible(*i))
|
||||
.map(summarize_item) {
|
||||
counts = counts + subcounts;
|
||||
submodule.map(|m| submodules.push(m));
|
||||
}
|
||||
submodules.sort();
|
||||
|
||||
(counts, Some(ModuleSummary {
|
||||
name: item.name.as_ref().map_or("".to_string(), |n| n.clone()),
|
||||
counts: counts,
|
||||
submodules: submodules,
|
||||
}))
|
||||
}
|
||||
// no stability information for the following items:
|
||||
ExternCrateItem(..) | ImportItem(_) |
|
||||
PrimitiveItem(_) => (Counts::zero(), None),
|
||||
_ => (item_counts, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Summarizes the stability levels in a crate.
|
||||
pub fn build(krate: &Crate) -> ModuleSummary {
|
||||
match krate.module {
|
||||
None => ModuleSummary {
|
||||
name: krate.name.clone(),
|
||||
counts: Counts::zero(),
|
||||
submodules: Vec::new(),
|
||||
},
|
||||
Some(ref item) => ModuleSummary {
|
||||
name: krate.name.clone(), .. summarize_item(item).1.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{RefCell, Cell};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::dynamic_lib::DynamicLibrary;
|
||||
use std::env;
|
||||
@ -92,6 +92,7 @@ pub fn run(input: &str,
|
||||
external_typarams: RefCell::new(None),
|
||||
inlined: RefCell::new(None),
|
||||
populated_crate_impls: RefCell::new(HashSet::new()),
|
||||
deref_trait_did: Cell::new(None),
|
||||
};
|
||||
|
||||
let mut v = RustdocVisitor::new(&ctx, None);
|
||||
|
@ -398,6 +398,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
name: def.ident,
|
||||
whence: def.span,
|
||||
stab: self.stability(def.id),
|
||||
imported_from: def.imported_from,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
src/test/auxiliary/issue-19190-3.rs
Normal file
30
src/test/auxiliary/issue-19190-3.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Foo;
|
||||
|
||||
impl Deref for Foo {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &i32 { loop {} }
|
||||
}
|
||||
|
||||
pub struct Bar;
|
||||
pub struct Baz;
|
||||
|
||||
impl Baz {
|
||||
pub fn baz(&self) {}
|
||||
}
|
||||
|
||||
impl Deref for Bar {
|
||||
type Target = Baz;
|
||||
fn deref(&self) -> &Baz { loop {} }
|
||||
}
|
22
src/test/rustdoc/issue-19190-2.rs
Normal file
22
src/test/rustdoc/issue-19190-2.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Bar;
|
||||
|
||||
impl Deref for Bar {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &i32 { loop {} }
|
||||
}
|
||||
|
||||
// @has issue_19190_2/struct.Bar.html
|
||||
// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32'
|
||||
|
35
src/test/rustdoc/issue-19190-3.rs
Normal file
35
src/test/rustdoc/issue-19190-3.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// aux-build:issue-19190-3.rs
|
||||
// ignore-android
|
||||
|
||||
extern crate issue_19190_3;
|
||||
|
||||
use std::ops::Deref;
|
||||
use issue_19190_3::Baz;
|
||||
|
||||
// @has issue_19190_3/struct.Foo.html
|
||||
// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32'
|
||||
pub use issue_19190_3::Foo;
|
||||
|
||||
// @has issue_19190_3/struct.Bar.html
|
||||
// @has - '//*[@id="method.baz"]' 'fn baz(&self)'
|
||||
pub use issue_19190_3::Bar;
|
||||
|
||||
// @has issue_19190_3/struct.MyBar.html
|
||||
// @has - '//*[@id="method.baz"]' 'fn baz(&self)'
|
||||
pub struct MyBar;
|
||||
|
||||
impl Deref for MyBar {
|
||||
type Target = Baz;
|
||||
fn deref(&self) -> &Baz { loop {} }
|
||||
}
|
||||
|
26
src/test/rustdoc/issue-19190.rs
Normal file
26
src/test/rustdoc/issue-19190.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Foo;
|
||||
pub struct Bar;
|
||||
|
||||
impl Foo {
|
||||
pub fn foo(&self) {}
|
||||
}
|
||||
|
||||
impl Deref for Bar {
|
||||
type Target = Foo;
|
||||
fn deref(&self) -> &Foo { loop {} }
|
||||
}
|
||||
|
||||
// @has issue_19190/struct.Bar.html
|
||||
// @has - '//*[@id="method.foo"]' 'fn foo(&self)'
|
Loading…
x
Reference in New Issue
Block a user