Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rustdoc/linkage fixes #10003

Merged
merged 3 commits into from
Oct 22, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 72 additions & 35 deletions src/librustc/middle/reachable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,36 @@ use middle::privacy;
use middle::resolve;

use std::hashmap::HashSet;
use syntax::ast::*;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{def_id_of_def, is_local};
use syntax::ast_util::{def_id_of_def, is_local, local_def};
use syntax::attr;
use syntax::parse::token;
use syntax::visit::Visitor;
use syntax::visit;

// Returns true if the given set of attributes contains the `#[inline]`
// attribute.
fn attributes_specify_inlining(attrs: &[Attribute]) -> bool {
fn attributes_specify_inlining(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "inline")
}

// Returns true if the given set of generics implies that the item it's
// associated with must be inlined.
fn generics_require_inlining(generics: &Generics) -> bool {
fn generics_require_inlining(generics: &ast::Generics) -> bool {
!generics.ty_params.is_empty()
}

// Returns true if the given item must be inlined because it may be
// monomorphized or it was marked with `#[inline]`. This will only return
// true for functions.
fn item_might_be_inlined(item: @item) -> bool {
fn item_might_be_inlined(item: @ast::item) -> bool {
if attributes_specify_inlining(item.attrs) {
return true
}

match item.node {
item_fn(_, _, _, ref generics, _) => {
ast::item_fn(_, _, _, ref generics, _) => {
generics_require_inlining(generics)
}
_ => false,
Expand All @@ -59,17 +59,17 @@ fn item_might_be_inlined(item: @item) -> bool {

// Returns true if the given type method must be inlined because it may be
// monomorphized or it was marked with `#[inline]`.
fn ty_method_might_be_inlined(ty_method: &TypeMethod) -> bool {
fn ty_method_might_be_inlined(ty_method: &ast::TypeMethod) -> bool {
attributes_specify_inlining(ty_method.attrs) ||
generics_require_inlining(&ty_method.generics)
}

// Returns true if the given trait method must be inlined because it may be
// monomorphized or it was marked with `#[inline]`.
fn trait_method_might_be_inlined(trait_method: &trait_method) -> bool {
fn trait_method_might_be_inlined(trait_method: &ast::trait_method) -> bool {
match *trait_method {
required(ref ty_method) => ty_method_might_be_inlined(ty_method),
provided(_) => true
ast::required(ref ty_method) => ty_method_might_be_inlined(ty_method),
ast::provided(_) => true
}
}

Expand All @@ -81,27 +81,27 @@ struct ReachableContext {
// methods they've been resolved to.
method_map: typeck::method_map,
// The set of items which must be exported in the linkage sense.
reachable_symbols: @mut HashSet<NodeId>,
reachable_symbols: @mut HashSet<ast::NodeId>,
// A worklist of item IDs. Each item ID in this worklist will be inlined
// and will be scanned for further references.
worklist: @mut ~[NodeId],
worklist: @mut ~[ast::NodeId],
// Known reexports of modules
exp_map2: resolve::ExportMap2,
}

struct MarkSymbolVisitor {
worklist: @mut ~[NodeId],
worklist: @mut ~[ast::NodeId],
method_map: typeck::method_map,
tcx: ty::ctxt,
reachable_symbols: @mut HashSet<NodeId>,
reachable_symbols: @mut HashSet<ast::NodeId>,
}

impl Visitor<()> for MarkSymbolVisitor {

fn visit_expr(&mut self, expr:@Expr, _:()) {
fn visit_expr(&mut self, expr:@ast::Expr, _:()) {

match expr.node {
ExprPath(_) => {
ast::ExprPath(_) => {
let def = match self.tcx.def_map.find(&expr.id) {
Some(&def) => def,
None => {
Expand All @@ -118,7 +118,7 @@ impl Visitor<()> for MarkSymbolVisitor {
}
self.reachable_symbols.insert(def_id.node);
}
ExprMethodCall(*) => {
ast::ExprMethodCall(*) => {
match self.method_map.find(&expr.id) {
Some(&typeck::method_map_entry {
origin: typeck::method_static(def_id),
Expand Down Expand Up @@ -162,24 +162,24 @@ impl ReachableContext {

// Returns true if the given def ID represents a local item that is
// eligible for inlining and false otherwise.
fn def_id_represents_local_inlined_item(tcx: ty::ctxt, def_id: DefId)
fn def_id_represents_local_inlined_item(tcx: ty::ctxt, def_id: ast::DefId)
-> bool {
if def_id.crate != LOCAL_CRATE {
if def_id.crate != ast::LOCAL_CRATE {
return false
}

let node_id = def_id.node;
match tcx.items.find(&node_id) {
Some(&ast_map::node_item(item, _)) => {
match item.node {
item_fn(*) => item_might_be_inlined(item),
ast::item_fn(*) => item_might_be_inlined(item),
_ => false,
}
}
Some(&ast_map::node_trait_method(trait_method, _, _)) => {
match *trait_method {
required(_) => false,
provided(_) => true,
ast::required(_) => false,
ast::provided(_) => true,
}
}
Some(&ast_map::node_method(method, impl_did, _)) => {
Expand All @@ -189,11 +189,11 @@ impl ReachableContext {
} else {
// Check the impl. If the generics on the self type of the
// impl require inlining, this method does too.
assert!(impl_did.crate == LOCAL_CRATE);
assert!(impl_did.crate == ast::LOCAL_CRATE);
match tcx.items.find(&impl_did.node) {
Some(&ast_map::node_item(item, _)) => {
match item.node {
item_impl(ref generics, _, _, _) => {
ast::item_impl(ref generics, _, _, _) => {
generics_require_inlining(generics)
}
_ => false
Expand Down Expand Up @@ -231,7 +231,7 @@ impl ReachableContext {
}
}

fn propagate_mod(&self, id: NodeId) {
fn propagate_mod(&self, id: ast::NodeId) {
match self.exp_map2.find(&id) {
Some(l) => {
for reexport in l.iter() {
Expand Down Expand Up @@ -262,21 +262,58 @@ impl ReachableContext {
match self.tcx.items.find(&search_item) {
Some(&ast_map::node_item(item, _)) => {
match item.node {
item_fn(_, _, _, _, ref search_block) => {
ast::item_fn(_, _, _, _, ref search_block) => {
visit::walk_block(&mut visitor, search_block, ())
}
// Our recursion into modules involves looking up their
// public reexports and the destinations of those
// exports. Privacy will put them in the worklist, but
// we won't find them in the ast_map, so this is where
// we deal with publicly re-exported items instead.
item_mod(*) => { self.propagate_mod(item.id); }
ast::item_mod(*) => self.propagate_mod(item.id),

// Implementations of exported structs/enums need to get
// added to the worklist (as all their methods should be
// accessible)
ast::item_struct(*) | ast::item_enum(*) => {
let def = local_def(item.id);
let impls = match self.tcx.inherent_impls.find(&def) {
Some(&impls) => impls,
None => continue
};
for imp in impls.iter() {
if is_local(imp.did) {
self.worklist.push(imp.did.node);
}
}
}

// Propagate through this impl
ast::item_impl(_, _, _, ref methods) => {
for method in methods.iter() {
self.worklist.push(method.id);
}
}

// Default methods of exported traits need to all be
// accessible.
ast::item_trait(_, _, ref methods) => {
for method in methods.iter() {
match *method {
ast::required(*) => {}
ast::provided(ref method) => {
self.worklist.push(method.id);
}
}
}
}

// These are normal, nothing reachable about these
// inherently and their children are already in the
// worklist
item_struct(*) | item_impl(*) | item_static(*) |
item_enum(*) | item_ty(*) | item_trait(*) |
item_foreign_mod(*) => {}
ast::item_static(*) | ast::item_ty(*) |
ast::item_foreign_mod(*) => {}

_ => {
self.tcx.sess.span_bug(item.span,
"found non-function item \
Expand All @@ -286,10 +323,10 @@ impl ReachableContext {
}
Some(&ast_map::node_trait_method(trait_method, _, _)) => {
match *trait_method {
required(*) => {
ast::required(*) => {
// Keep going, nothing to get exported
}
provided(ref method) => {
ast::provided(ref method) => {
visit::walk_block(&mut visitor, &method.body, ())
}
}
Expand All @@ -310,7 +347,7 @@ impl ReachableContext {
worklist: {}",
desc))
}
None if search_item == CRATE_NODE_ID => {
None if search_item == ast::CRATE_NODE_ID => {
self.propagate_mod(search_item);
}
None => {
Expand All @@ -329,7 +366,7 @@ impl ReachableContext {
// reachability, which might result in a compile time loss.
fn mark_destructors_reachable(&self) {
for (_, destructor_def_id) in self.tcx.destructor_for_type.iter() {
if destructor_def_id.crate == LOCAL_CRATE {
if destructor_def_id.crate == ast::LOCAL_CRATE {
self.reachable_symbols.insert(destructor_def_id.node);
}
}
Expand All @@ -340,7 +377,7 @@ pub fn find_reachable(tcx: ty::ctxt,
method_map: typeck::method_map,
exp_map2: resolve::ExportMap2,
exported_items: &privacy::ExportedItems)
-> @mut HashSet<NodeId> {
-> @mut HashSet<ast::NodeId> {
// XXX(pcwalton): We only need to mark symbols that are exported. But this
// is more complicated than just looking at whether the symbol is `pub`,
// because it might be the target of a `pub use` somewhere. For now, I
Expand Down
85 changes: 56 additions & 29 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ pub struct Cache {

/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
/// parent trait if no extra documentation is specified, and this map is
/// keyed on trait id with a value of a 'method name => documentation'
/// mapping.
traits: HashMap<ast::NodeId, HashMap<~str, ~str>>,
/// parent trait if no extra documentation is specified, and default methods
/// should show up in documentation about trait implementations.
traits: HashMap<ast::NodeId, clean::Trait>,

/// When rendering traits, it's often useful to be able to list all
/// implementors of the trait, and this mapping is exactly, that: a mapping
Expand Down Expand Up @@ -488,18 +487,7 @@ impl DocFolder for Cache {
// trait
match item.inner {
clean::TraitItem(ref t) => {
let mut dox = HashMap::new();
for meth in t.methods.iter() {
let it = meth.item();
match it.doc_value() {
None => {}
Some(s) => {
dox.insert(it.name.get_ref().to_owned(),
s.to_owned());
}
}
}
self.traits.insert(item.id, dox);
self.traits.insert(item.id, t.clone());
}
_ => {}
}
Expand Down Expand Up @@ -1480,18 +1468,25 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl, dox: &Option<~str>) {
}
None => {}
}
write!(w, "<div class='methods'>");
for meth in i.methods.iter() {

fn docmeth(w: &mut io::Writer, item: &clean::Item) -> bool {
write!(w, "<h4 id='method.{}' class='method'><code>",
*meth.name.get_ref());
render_method(w, meth, false);
*item.name.get_ref());
render_method(w, item, false);
write!(w, "</code></h4>\n");
match meth.doc_value() {
match item.doc_value() {
Some(s) => {
write!(w, "<div class='docblock'>{}</div>", Markdown(s));
continue
true
}
None => {}
None => false
}
}

write!(w, "<div class='methods'>");
for meth in i.methods.iter() {
if docmeth(w, meth) {
continue
}

// No documentation? Attempt to slurp in the trait's documentation
Expand All @@ -1501,13 +1496,19 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl, dox: &Option<~str>) {
};
do local_data::get(cache_key) |cache| {
do cache.unwrap().read |cache| {
let name = meth.name.get_ref().as_slice();
match cache.traits.find(&trait_id) {
Some(m) => {
match m.find_equiv(&name) {
Some(s) => {
write!(w, "<div class='docblock'>{}</div>",
Markdown(s.as_slice()));
Some(t) => {
let name = meth.name.clone();
match t.methods.iter().find(|t| t.item().name == name) {
Some(method) => {
match method.item().doc_value() {
Some(s) => {
write!(w,
"<div class='docblock'>{}</div>",
Markdown(s));
}
None => {}
}
}
None => {}
}
Expand All @@ -1517,6 +1518,32 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl, dox: &Option<~str>) {
}
}
}

// If we've implemented a trait, then also emit documentation for all
// default methods which weren't overridden in the implementation block.
match trait_id {
None => {}
Some(id) => {
do local_data::get(cache_key) |cache| {
do cache.unwrap().read |cache| {
match cache.traits.find(&id) {
Some(t) => {
for method in t.methods.iter() {
let n = method.item().name.clone();
match i.methods.iter().find(|m| m.name == n) {
Some(*) => continue,
None => {}
}

docmeth(w, method.item());
}
}
None => {}
}
}
}
}
}
write!(w, "</div>");
}

Expand Down
Loading