From 0a46933c4d81573e78ce16cd215ba155a3114fce Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 13 Apr 2015 11:55:00 -0700 Subject: [PATCH 1/3] rustdoc: Overhaul stability displays This commit is an overhaul to how rustdoc deals with stability of the standard library. The handling has all been revisited with respect to Rust's current approach to stability in terms of implementation as well as the state of the standard library today. The high level changes made were: * Stable items now have no marker by default * Color-based small stability markers have been removed * Module listings now fade out unstable/deprecated items slightly * Trait methods have a separate background color based on stability and also list the reason that they are unstable. * `impl` blocks with stability no longer render at all. This may be re-added once the compiler recognizes stability on `impl` blocks. * `impl` blocks no longer have stability of the methods implemente indicated * The stability summary has been removed Closes #15468 Closes #21674 Closes #24201 --- src/librustdoc/clean/mod.rs | 16 +++ src/librustdoc/html/format.rs | 122 ----------------- src/librustdoc/html/render.rs | 182 ++++++++++++------------- src/librustdoc/html/static/main.css | 59 +++++---- src/librustdoc/lib.rs | 1 - src/librustdoc/stability_summary.rs | 199 ---------------------------- 6 files changed, 132 insertions(+), 447 deletions(-) delete mode 100644 src/librustdoc/stability_summary.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 72702dc8d9472..8b65a7d6791cb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -313,6 +313,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.len() > 0 { + base.push_str(" deprecated"); + } + base + } + _ => String::new(), + } + } } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index d2dccca362ef8..8b6969f586e44 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -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); -/// Wrapper struct for emitting the stability level concisely. -pub struct ConciseStability<'a>(pub &'a Option); /// Wrapper struct for emitting a where clause from Generics. pub struct WhereClause<'a>(pub &'a clean::Generics); /// Wrapper struct for emitting type parameter bounds. @@ -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, "{lvl}", - 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, "", - lvl = Escape(&*lvl), - colon = if !stability.reason.is_empty() { ": " } else { "" }, - reason = Escape(&*stability.reason)) - } - None => { - write!(f, "") - } - } - } -} - -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, "")); - try!(write!(f, "{}", { - let mut url = context[1..].to_vec(); - url.push("index.html"); - url.connect("/") - }, - path)); - try!(write!(f, "")); - try!(write!(f, " ", - (100 * cnt.stable) as f64/tot as f64)); - try!(write!(f, " ", - (100 * cnt.unstable) as f64/tot as f64)); - try!(write!(f, " ", - (100 * cnt.deprecated) as f64/tot as f64)); - try!(write!(f, " ", - (100 * cnt.unmarked) as f64/tot as f64)); - try!(write!(f, "")); - - 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"

Stability dashboard: crate {name}

-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}): -
- stable ({}%),
- unstable ({}%),
- deprecated ({}%),
- unmarked ({}%) -
-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, "")); - try!(fmt_inner(f, &mut context, self)); - write!(f, "
") - } -} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 5f4a3e74b6589..c619421635dec 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -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); @@ -437,11 +438,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 { @@ -645,8 +643,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_)); @@ -1143,38 +1140,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 { @@ -1456,21 +1428,8 @@ impl<'a> fmt::Display for Item<'a> { try!(write!(fmt, "{}", shortty(self.item), self.item.name.as_ref().unwrap())); - // Write stability level - try!(write!(fmt, "{}", Stability(&self.item.stability))); - try!(write!(fmt, "")); // in-band - // Links to out-of-band information, i.e. src and stability dashboard try!(write!(fmt, "")); - - // Write stability dashboard link - match self.item.inner { - clean::ModuleItem(ref m) if m.is_crate => { - try!(write!(fmt, "[stability] ")); - } - _ => {} - }; - try!(write!(fmt, r##" [-] [+] @@ -1554,11 +1513,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, "
{}
", Markdown(s))); - } - None => {} + if let Some(s) = short_stability(item, true) { + try!(write!(w, "
{}
", s)); + } + if let Some(s) = item.doc_value() { + try!(write!(w, "
{}
", Markdown(s))); } Ok(()) } @@ -1593,10 +1552,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)) + } + 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, + _ => {} } - (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)) + i1.name.cmp(&i2.name) } indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); @@ -1665,19 +1631,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, " - - {stab}{} - {} + + {name} + + {stab_docs} {docs} + ", - *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 +1659,30 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, write!(w, "") } +fn short_stability(item: &clean::Item, show_reason: bool) -> Option { + item.stability.as_ref().and_then(|stab| { + let reason = if show_reason && stab.reason.len() > 0 { + format!(": {}", stab.reason) + } else { + String::new() + }; + let text = if stab.deprecated_since.len() > 0 { + 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!("{}", + item.stability_class(), text)) + }) +} + struct Initializer<'a>(&'a str); impl<'a> fmt::Display for Initializer<'a> { @@ -1800,10 +1798,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, "

{}", - shortty(m), - *m.name.as_ref().unwrap(), - ConciseStability(&m.stability))); + try!(write!(w, "

", + ty = shortty(m), + name = *m.name.as_ref().unwrap(), + stab = m.stability_class())); try!(render_method(w, m, MethodLink::Anchor)); try!(write!(w, "

")); try!(document(w, m)); @@ -1854,8 +1852,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, "
  • {}impl{} {} for {}{}
  • ", - ConciseStability(&i.stability), + try!(writeln!(w, "
  • impl{} {} for {}{}
  • ", i.generics, i.trait_, i.for_, WhereClause(&i.generics))); } } @@ -1964,9 +1961,10 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item, if fields.peek().is_some() { try!(write!(w, "

    Fields

    \n")); for field in fields { - try!(write!(w, " + ")); @@ -2034,8 +2032,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, if !e.variants.is_empty() { try!(write!(w, "

    Variants

    \n
    \ - {stab}{name}", - stab = ConciseStability(&field.stability), + try!(write!(w, "
    \ + {name}", + stab = field.stability_class(), name = field.name.as_ref().unwrap())); try!(document(w, field)); try!(write!(w, "
    ")); for variant in &e.variants { - try!(write!(w, "
    {stab}{name}", - stab = ConciseStability(&variant.stability), + try!(write!(w, "
    {name}", name = variant.name.as_ref().unwrap())); try!(document(w, variant)); match variant.inner { @@ -2200,8 +2197,7 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink) -> fmt::Result { - try!(write!(w, "

    {}impl{} ", - ConciseStability(&i.stability), + try!(write!(w, "

    impl{} ", i.impl_.generics)); if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity { try!(write!(w, "!")); @@ -2216,48 +2212,43 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink) } 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, "

    {}", + try!(write!(w, "

    ", *item.name.as_ref().unwrap(), - shortty(item), - ConciseStability(&item.stability))); + shortty(item))); try!(render_method(w, item, link)); try!(write!(w, "

    \n")); } clean::TypedefItem(ref tydef) => { let name = item.name.as_ref().unwrap(); - try!(write!(w, "

    {}", + try!(write!(w, "

    ", *name, - shortty(item), - ConciseStability(&item.stability))); + shortty(item))); try!(write!(w, "type {} = {}", name, tydef.type_)); try!(write!(w, "

    \n")); } clean::AssociatedTypeItem(ref bounds, ref default) => { let name = item.name.as_ref().unwrap(); - try!(write!(w, "

    {}", + try!(write!(w, "

    ", *name, - shortty(item), - ConciseStability(&item.stability))); + shortty(item))); try!(assoc_type(w, item, bounds, default)); try!(write!(w, "

    \n")); } _ => panic!("can't make docs for trait item with name {:?}", item.name) } - match item.doc_value() { - Some(s) if dox => { - try!(write!(w, "
    {}
    ", Markdown(s))); - Ok(()) - } - Some(..) | None => Ok(()) + if let MethodLink::Anchor = link { + document(w, item) + } else { + Ok(()) } } try!(write!(w, "
    ")); 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 +2262,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(()) } diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css index 2af20ce59da57..657b2c8fef872 100644 --- a/src/librustdoc/html/static/main.css +++ b/src/librustdoc/html/static/main.css @@ -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,30 +477,27 @@ 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: 2px; + border-style: solid; + padding: 5px; } - -h1 .stability { - text-transform: lowercase; - font-weight: 400; - margin-left: 14px; - padding: 4px 10px; +em.stab p { + display: inline; } -.impl-items .stability, .methods .stability { - margin-right: 20px; +.module-item .stab { + border-width: 0; + padding: 0; + background: inherit !important; } -.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; } +.module-item.unstable { + opacity: 0.65; +} td.summary-column { width: 100%; @@ -500,11 +506,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 +556,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 { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 008da466db010..2682bbf4662e0 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -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; diff --git a/src/librustdoc/stability_summary.rs b/src/librustdoc/stability_summary.rs deleted file mode 100644 index 3e4f6896ee68d..0000000000000 --- a/src/librustdoc/stability_summary.rs +++ /dev/null @@ -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 or the MIT license -// , 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, -} - -impl PartialOrd for ModuleSummary { - fn partial_cmp(&self, other: &ModuleSummary) -> Option { - 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) { - 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() - } - } -} From 8fb31f75c95c52cfef40c6ffe2f1452c959ca5e4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 13 Apr 2015 15:25:40 -0700 Subject: [PATCH 2/3] rustdoc: Fix cross-crate macro source links The source filename for cross crate macros isn't quite right so the normal generated links are invalid. Closes #21311 --- src/librustdoc/clean/mod.rs | 3 ++ src/librustdoc/doctree.rs | 1 + src/librustdoc/html/format.rs | 12 +++---- src/librustdoc/html/render.rs | 51 ++++++++++++++++++++++-------- src/librustdoc/html/static/main.js | 3 ++ src/librustdoc/visit_ast.rs | 1 + 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8b65a7d6791cb..263a93a9d6779 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1270,6 +1270,7 @@ impl Clean for ast::ImplItem { ast::MacImplItem(_) => { MacroItem(Macro { source: self.span.to_src(cx), + imported_from: None, }) } }; @@ -2557,6 +2558,7 @@ fn resolve_def(cx: &DocContext, id: ast::NodeId) -> Option { #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Macro { pub source: String, + pub imported_from: Option, } impl Clean for doctree::Macro { @@ -2570,6 +2572,7 @@ impl Clean 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), }), } } diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 862bca1b81338..8fa92304d249c 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -214,6 +214,7 @@ pub struct Macro { pub attrs: Vec, pub whence: Span, pub stab: Option, + pub imported_from: Option, } pub struct ExternCrate { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8b6969f586e44..bb53d532f52f7 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -288,9 +288,9 @@ pub fn href(did: ast::DefId) -> Option<(String, ItemType, Vec)> { repeat("../").take(loc.len()).collect::() } else { match cache.extern_locations[&did.krate] { - render::Remote(ref s) => s.to_string(), - render::Local => repeat("../").take(loc.len()).collect::(), - 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] { @@ -379,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::()) } - render::Unknown => None, + (_, render::Unknown) => None, }; match loc { Some(root) => { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c619421635dec..e3df0fc9ba670 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -193,7 +193,7 @@ pub struct Cache { pub implementors: HashMap>, /// Cache of where external crate documentation can be found. - pub extern_locations: HashMap, + pub extern_locations: HashMap, /// Cache of where documentation for primitives can be found. pub primitive_locations: HashMap, @@ -408,7 +408,8 @@ pub fn run(mut krate: clean::Crate, // 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)); } @@ -1343,22 +1344,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 { + 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, @@ -1380,9 +1402,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, @@ -1444,7 +1466,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, "[src]", + try!(write!(fmt, "[src]", self.item.def_id.node, l)); } None => {} diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index c3ab375a9e24a..0379c04be4d29 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -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(); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index e4f4dbaafbe49..4ad693578ccdc 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -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, } } } From 71c1b5b704ca3955e5ca22d77a0d59b55c9af4f1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 13 Apr 2015 16:23:32 -0700 Subject: [PATCH 3/3] rustdoc: Inline methods inhereted through Deref Whenever a type implements Deref, rustdoc will now add a section to the "methods available" sections for "Methods from Deref", listing all the inherent methods of the type `Foo`. Closes #19190 --- src/librustdoc/clean/inline.rs | 49 +++++----- src/librustdoc/clean/mod.rs | 84 ++++++++++++++++-- src/librustdoc/core.rs | 9 +- src/librustdoc/html/render.rs | 133 +++++++++++++++++++++------- src/librustdoc/html/static/main.css | 8 +- src/librustdoc/test.rs | 3 +- src/test/auxiliary/issue-19190-3.rs | 30 +++++++ src/test/rustdoc/issue-19190-2.rs | 22 +++++ src/test/rustdoc/issue-19190-3.rs | 35 ++++++++ src/test/rustdoc/issue-19190.rs | 26 ++++++ 10 files changed, 333 insertions(+), 66 deletions(-) create mode 100644 src/test/auxiliary/issue-19190-3.rs create mode 100644 src/test/rustdoc/issue-19190-2.rs create mode 100644 src/test/rustdoc/issue-19190-3.rs create mode 100644 src/test/rustdoc/issue-19190.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index f14437b71b47a..4fc86cf181b8d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -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 { +pub fn build_impls(cx: &DocContext, tcx: &ty::ctxt, + did: ast::DefId) -> Vec { 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>) { + impls: &mut Vec) { 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 { +pub fn build_impl(cx: &DocContext, + tcx: &ty::ctxt, + did: ast::DefId, + ret: &mut Vec) { 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::>(); 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, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 263a93a9d6779..f7fbb67e08a28 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -128,6 +128,10 @@ impl<'a, 'tcx> Clean 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))); @@ -321,7 +325,7 @@ impl Item { attr::Unstable => "unstable".to_string(), attr::Stable => String::new(), }; - if s.deprecated_since.len() > 0 { + if !s.deprecated_since.is_empty() { base.push_str(" deprecated"); } base @@ -387,7 +391,7 @@ impl Clean 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))); @@ -2186,9 +2190,21 @@ fn detect_derived(attrs: &[M]) -> bool { attr::contains_name(attrs, "automatically_derived") } -impl Clean for doctree::Impl { - fn clean(&self, cx: &DocContext) -> Item { - Item { +impl Clean> for doctree::Impl { + fn clean(&self, cx: &DocContext) -> Vec { + 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), @@ -2198,12 +2214,66 @@ impl Clean 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) { + 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); + } } } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a637ba9f29706..1b74123c4adcd 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -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>>, pub inlined: RefCell>>, pub populated_crate_impls: RefCell>, + pub deref_trait_did: Cell>, } impl<'tcx> DocContext<'tcx> { @@ -77,6 +78,7 @@ pub struct CrateAnalysis { pub external_paths: ExternalPaths, pub external_typarams: RefCell>>, pub inlined: RefCell>>, + pub deref_trait_did: Option, } pub type Externs = HashMap>; @@ -147,15 +149,17 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, 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, 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) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e3df0fc9ba670..f87a86eb3a68c 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -209,6 +209,7 @@ pub struct Cache { privmod: bool, remove_priv: bool, public_items: NodeSet, + deref_trait_did: Option, // In rare case where a structure is defined in one module but implemented // in another, if the implementing module is parsed before defining module, @@ -396,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()), @@ -403,8 +405,6 @@ 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 { @@ -427,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)); @@ -1069,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 } + }) }) } }; @@ -1684,12 +1690,12 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, fn short_stability(item: &clean::Item, show_reason: bool) -> Option { item.stability.as_ref().and_then(|stab| { - let reason = if show_reason && stab.reason.len() > 0 { + let reason = if show_reason && !stab.reason.is_empty() { format!(": {}", stab.reason) } else { String::new() }; - let text = if stab.deprecated_since.len() > 0 { + let text = if !stab.deprecated_since.is_empty() { let since = if show_reason { format!(" since {}", Escape(&stab.deprecated_since)) } else { @@ -1865,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, " @@ -1995,7 +2001,7 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item, try!(write!(w, "

    ")); } } - render_methods(w, it) + render_methods(w, it.def_id, MethodRender::All) } fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, @@ -2094,7 +2100,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, try!(write!(w, "")); } - try!(render_methods(w, it)); + try!(render_methods(w, it.def_id, MethodRender::All)); Ok(()) } @@ -2183,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, "

    Methods

    ")); + let render_header = match what { + MethodRender::All => { + try!(write!(w, "

    Methods

    ")); + true + } + MethodRender::DerefFor { trait_, type_ } => { + try!(write!(w, "

    Methods from \ + {}<Target={}>

    ", 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, "

    Trait \ Implementations

    ")); - 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, "

    \ @@ -2211,27 +2251,52 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {

    ")); 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, "

    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)); +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(()) + } } - try!(write!(w, "{}{}

    ", i.impl_.for_, - WhereClause(&i.impl_.generics))); - if let Some(ref dox) = i.dox { - try!(write!(w, "
    {}
    ", Markdown(dox))); +} + +fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink, + render_header: bool) -> fmt::Result { + if render_header { + try!(write!(w, "

    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, "{}{}

    ", i.impl_.for_, + WhereClause(&i.impl_.generics))); + if let Some(ref dox) = i.dox { + try!(write!(w, "
    {}
    ", Markdown(dox))); + } } fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item, @@ -2393,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 { diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css index 657b2c8fef872..c94dbc1510332 100644 --- a/src/librustdoc/html/static/main.css +++ b/src/librustdoc/html/static/main.css @@ -481,9 +481,12 @@ em.stab.unstable { background: #FFF5D6; border-color: #FFC600; } em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; } em.stab { display: inline-block; - border-width: 2px; + border-width: 1px; border-style: solid; - padding: 5px; + padding: 3px; + margin-bottom: 5px; + font-size: 90%; + font-style: normal; } em.stab p { display: inline; @@ -492,6 +495,7 @@ em.stab p { .module-item .stab { border-width: 0; padding: 0; + margin: 0; background: inherit !important; } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 449f9c79d1d45..bbe0a6f267538 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -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); diff --git a/src/test/auxiliary/issue-19190-3.rs b/src/test/auxiliary/issue-19190-3.rs new file mode 100644 index 0000000000000..435f0176162b8 --- /dev/null +++ b/src/test/auxiliary/issue-19190-3.rs @@ -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 or the MIT license +// , 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 {} } +} diff --git a/src/test/rustdoc/issue-19190-2.rs b/src/test/rustdoc/issue-19190-2.rs new file mode 100644 index 0000000000000..b84ec6d616636 --- /dev/null +++ b/src/test/rustdoc/issue-19190-2.rs @@ -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 or the MIT license +// , 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' + diff --git a/src/test/rustdoc/issue-19190-3.rs b/src/test/rustdoc/issue-19190-3.rs new file mode 100644 index 0000000000000..c315ea26d2642 --- /dev/null +++ b/src/test/rustdoc/issue-19190-3.rs @@ -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 or the MIT license +// , 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 {} } +} + diff --git a/src/test/rustdoc/issue-19190.rs b/src/test/rustdoc/issue-19190.rs new file mode 100644 index 0000000000000..f011a3e64a6be --- /dev/null +++ b/src/test/rustdoc/issue-19190.rs @@ -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 or the MIT license +// , 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)'