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: refine cross-crate impl inlining #33002

Merged
merged 3 commits into from
Apr 19, 2016
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
1 change: 1 addition & 0 deletions src/librustc/middle/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub enum InlinedItemRef<'a> {
/// LOCAL_CRATE in their DefId.
pub const LOCAL_CRATE: ast::CrateNum = 0;

#[derive(Copy, Clone)]
pub struct ChildItem {
pub def: DefLike,
pub name: ast::Name,
Expand Down
9 changes: 8 additions & 1 deletion src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
use util::nodemap::{DefIdSet, FnvHashMap};

use std::hash::Hash;
use std::fmt;
use syntax::ast::NodeId;

// Accessibility levels, sorted in ascending order
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum AccessLevel {
// Exported items + items participating in various kinds of public interfaces,
// but not directly nameable. For example, if function `fn f() -> T {...}` is
Expand Down Expand Up @@ -56,6 +57,12 @@ impl<Id: Hash + Eq> Default for AccessLevels<Id> {
}
}

impl<Id: Hash + Eq + fmt::Debug> fmt::Debug for AccessLevels<Id> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.map, f)
}
}

/// A set containing all exported definitions from external crates.
/// The set does not contain any entries from local crates.
pub type ExternalExports = DefIdSet;
38 changes: 22 additions & 16 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ use rustc::middle::stability;

use rustc_const_eval::lookup_const_by_id;

use core::DocContext;
use core::{DocContext, DocAccessLevels};
use doctree;
use clean::{self, Attributes, GetDefId};
use clean::{self, GetDefId};

use super::{Clean, ToSource};

Expand Down Expand Up @@ -116,7 +116,7 @@ fn try_inline_def(cx: &DocContext, tcx: &TyCtxt,
}
_ => return None,
};
cx.inlined.borrow_mut().as_mut().unwrap().insert(did);
cx.renderinfo.borrow_mut().inlined.insert(did);
ret.push(clean::Item {
source: clean::Span::empty(),
name: Some(tcx.item_name(did).to_string()),
Expand Down Expand Up @@ -146,7 +146,7 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
elem.data.to_string()
});
let fqn = once(crate_name).chain(relative).collect();
cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind));
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
}
}

Expand Down Expand Up @@ -260,11 +260,6 @@ pub fn build_impls(cx: &DocContext,
match def {
cstore::DlImpl(did) => build_impl(cx, tcx, did, impls),
cstore::DlDef(Def::Mod(did)) => {
// Don't recurse if this is a #[doc(hidden)] module
if load_attrs(cx, tcx, did).list("doc").has_word("hidden") {
return;
}

for item in tcx.sess.cstore.item_children(did) {
populate_impls(cx, tcx, item.def, impls)
}
Expand Down Expand Up @@ -295,16 +290,17 @@ pub fn build_impl(cx: &DocContext,
tcx: &TyCtxt,
did: DefId,
ret: &mut Vec<clean::Item>) {
if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) {
if !cx.renderinfo.borrow_mut().inlined.insert(did) {
return
}

let attrs = load_attrs(cx, tcx, did);
let associated_trait = tcx.impl_trait_ref(did);
if let Some(ref t) = associated_trait {
// 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.list("doc").has_word("hidden") {

// Only inline impl if the implemented trait is
// reachable in rustdoc generated documentation
if let Some(traitref) = associated_trait {
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
return
}
}
Expand All @@ -330,6 +326,17 @@ pub fn build_impl(cx: &DocContext,
});
}

let ty = tcx.lookup_item_type(did);
let for_ = ty.ty.clean(cx);

// Only inline impl if the implementing type is
// reachable in rustdoc generated documentation
if let Some(did) = for_.def_id() {
if !cx.access_levels.borrow().is_doc_reachable(did) {
return
}
}

let predicates = tcx.lookup_predicates(did);
let trait_items = tcx.sess.cstore.impl_items(did)
.iter()
Expand Down Expand Up @@ -412,7 +419,6 @@ pub fn build_impl(cx: &DocContext,
}
}).collect::<Vec<_>>();
let polarity = tcx.trait_impl_polarity(did);
let ty = tcx.lookup_item_type(did);
let trait_ = associated_trait.clean(cx).map(|bound| {
match bound {
clean::TraitBound(polyt, _) => polyt.trait_,
Expand All @@ -436,7 +442,7 @@ pub fn build_impl(cx: &DocContext,
derived: clean::detect_derived(&attrs),
provided_trait_methods: provided,
trait_: trait_,
for_: ty.ty.clean(cx),
for_: for_,
generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx),
items: trait_items,
polarity: polarity.map(|p| { p.clean(cx) }),
Expand Down
28 changes: 21 additions & 7 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use syntax::ptr::P;

use rustc_trans::back::link;
use rustc::middle::cstore::{self, CrateStore};
use rustc::middle::privacy::AccessLevels;
use rustc::hir::def::Def;
use rustc::hir::def_id::{DefId, DefIndex};
use rustc::ty::subst::{self, ParamSpace, VecPerParamSpace};
Expand All @@ -46,15 +47,17 @@ use rustc::hir;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::u32;
use std::env::current_dir;
use std::mem;

use core::DocContext;
use doctree;
use visit_ast;
use html::item_type::ItemType;

mod inline;
pub mod inline;
mod simplify;

// extract the stability index for a node from tcx, if possible
Expand Down Expand Up @@ -112,13 +115,16 @@ impl<T: Clean<U>, U> Clean<Vec<U>> for P<[T]> {
}
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
#[derive(Clone, Debug)]
pub struct Crate {
pub name: String,
pub src: PathBuf,
pub module: Option<Item>,
pub externs: Vec<(ast::CrateNum, ExternalCrate)>,
pub primitives: Vec<PrimitiveType>,
pub access_levels: Arc<AccessLevels<DefId>>,
// These are later on moved into `CACHEKEY`, leaving the map empty.
// Only here so that they can be filtered through the rustdoc passes.
pub external_traits: HashMap<DefId, Trait>,
}

Expand All @@ -127,14 +133,20 @@ struct CrateNum(ast::CrateNum);
impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
fn clean(&self, cx: &DocContext) -> Crate {
use rustc::session::config::Input;
use ::visit_lib::LibEmbargoVisitor;

if let Some(t) = cx.tcx_opt() {
cx.deref_trait_did.set(t.lang_items.deref_trait());
cx.renderinfo.borrow_mut().deref_trait_did = cx.deref_trait_did.get();
}

let mut externs = Vec::new();
for cnum in cx.sess().cstore.crates() {
externs.push((cnum, CrateNum(cnum).clean(cx)));
if cx.tcx_opt().is_some() {
// Analyze doc-reachability for extern items
LibEmbargoVisitor::new(cx).visit_lib(cnum);
}
}
externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));

Expand Down Expand Up @@ -204,14 +216,17 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
Input::Str { ref name, .. } => PathBuf::from(name.clone()),
};

let mut access_levels = cx.access_levels.borrow_mut();
let mut external_traits = cx.external_traits.borrow_mut();

Crate {
name: name.to_string(),
src: src,
module: Some(module),
externs: externs,
primitives: primitives,
external_traits: cx.external_traits.borrow_mut().take()
.unwrap_or(HashMap::new()),
access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())),
external_traits: mem::replace(&mut external_traits, Default::default()),
}
}
}
Expand Down Expand Up @@ -540,8 +555,7 @@ impl Clean<TyParam> for hir::TyParam {

impl<'tcx> Clean<TyParam> for ty::TypeParameterDef<'tcx> {
fn clean(&self, cx: &DocContext) -> TyParam {
cx.external_typarams.borrow_mut().as_mut().unwrap()
.insert(self.def_id, self.name.clean(cx));
cx.renderinfo.borrow_mut().external_typarams.insert(self.def_id, self.name.clean(cx));
TyParam {
name: self.name.clean(cx),
did: self.def_id,
Expand Down Expand Up @@ -2668,7 +2682,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId {
inline::record_extern_fqn(cx, did, kind);
if let TypeTrait = kind {
let t = inline::build_external_trait(cx, tcx, did);
cx.external_traits.borrow_mut().as_mut().unwrap().insert(did, t);
cx.external_traits.borrow_mut().insert(did, t);
}
did
}
Expand Down
79 changes: 33 additions & 46 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ use syntax::feature_gate::UnstableFeatures;
use syntax::parse::token;

use std::cell::{RefCell, Cell};
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::rc::Rc;

use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;
use html::render::RenderInfo;

pub use rustc::session::config::Input;
pub use rustc::session::search_paths::SearchPaths;
Expand All @@ -45,19 +46,24 @@ pub enum MaybeTyped<'a, 'tcx: 'a> {
NotTyped(&'a session::Session)
}

pub type ExternalPaths = RefCell<Option<HashMap<DefId,
(Vec<String>, clean::TypeKind)>>>;
pub type Externs = HashMap<String, Vec<String>>;
pub type ExternalPaths = HashMap<DefId, (Vec<String>, clean::TypeKind)>;

pub struct DocContext<'a, 'tcx: 'a> {
pub map: &'a hir_map::Map<'tcx>,
pub maybe_typed: MaybeTyped<'a, 'tcx>,
pub input: Input,
pub external_paths: ExternalPaths,
pub external_traits: RefCell<Option<HashMap<DefId, clean::Trait>>>,
pub external_typarams: RefCell<Option<HashMap<DefId, String>>>,
pub inlined: RefCell<Option<HashSet<DefId>>>,
pub all_crate_impls: RefCell<HashMap<ast::CrateNum, Vec<clean::Item>>>,
pub deref_trait_did: Cell<Option<DefId>>,
// Note that external items for which `doc(hidden)` applies to are shown as
// non-reachable while local items aren't. This is because we're reusing
// the access levels from crateanalysis.
/// Later on moved into `clean::Crate`
pub access_levels: RefCell<AccessLevels<DefId>>,
/// Later on moved into `html::render::CACHE_KEY`
pub renderinfo: RefCell<RenderInfo>,
/// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
pub external_traits: RefCell<HashMap<DefId, clean::Trait>>,
}

impl<'b, 'tcx> DocContext<'b, 'tcx> {
Expand All @@ -81,20 +87,23 @@ impl<'b, 'tcx> DocContext<'b, 'tcx> {
}
}

pub struct CrateAnalysis {
pub access_levels: AccessLevels<DefId>,
pub external_paths: ExternalPaths,
pub external_typarams: RefCell<Option<HashMap<DefId, String>>>,
pub inlined: RefCell<Option<HashSet<DefId>>>,
pub deref_trait_did: Option<DefId>,
pub trait DocAccessLevels {
fn is_doc_reachable(&self, DefId) -> bool;
}

pub type Externs = HashMap<String, Vec<String>>;
impl DocAccessLevels for AccessLevels<DefId> {
fn is_doc_reachable(&self, did: DefId) -> bool {
self.is_public(did)
}
}

pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
input: Input, triple: Option<String>)
-> (clean::Crate, CrateAnalysis) {

pub fn run_core(search_paths: SearchPaths,
cfgs: Vec<String>,
externs: Externs,
input: Input,
triple: Option<String>) -> (clean::Crate, RenderInfo)
{
// Parse, resolve, and typecheck the given crate.

let cpath = match input {
Expand Down Expand Up @@ -148,7 +157,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
let arenas = ty::CtxtArenas::new();
let hir_map = driver::make_map(&sess, &mut hir_forest);

let krate_and_analysis = abort_on_err(driver::phase_3_run_analysis_passes(&sess,
abort_on_err(driver::phase_3_run_analysis_passes(&sess,
&cstore,
hir_map,
&arenas,
Expand All @@ -175,42 +184,20 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
map: &tcx.map,
maybe_typed: Typed(tcx),
input: input,
external_traits: RefCell::new(Some(HashMap::new())),
external_typarams: RefCell::new(Some(HashMap::new())),
external_paths: RefCell::new(Some(HashMap::new())),
inlined: RefCell::new(Some(HashSet::new())),
all_crate_impls: RefCell::new(HashMap::new()),
deref_trait_did: Cell::new(None),
access_levels: RefCell::new(access_levels),
external_traits: RefCell::new(HashMap::new()),
renderinfo: RefCell::new(Default::default()),
};
debug!("crate: {:?}", ctxt.map.krate());

let mut analysis = CrateAnalysis {
access_levels: access_levels,
external_paths: RefCell::new(None),
external_typarams: RefCell::new(None),
inlined: RefCell::new(None),
deref_trait_did: None,
};

let krate = {
let mut v = RustdocVisitor::new(&ctxt, Some(&analysis));
let mut v = RustdocVisitor::new(&ctxt);
v.visit(ctxt.map.krate());
v.clean(&ctxt)
};

let external_paths = ctxt.external_paths.borrow_mut().take();
*analysis.external_paths.borrow_mut() = external_paths;

let map = ctxt.external_typarams.borrow_mut().take();
*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();

Some((krate, analysis))
}), &sess);

krate_and_analysis.unwrap()
Some((krate, ctxt.renderinfo.into_inner()))
}), &sess).unwrap()
}
4 changes: 4 additions & 0 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use syntax::abi::Abi;
use rustc::hir;

use clean;
use core::DocAccessLevels;
use html::item_type::ItemType;
use html::render;
use html::render::{cache, CURRENT_LOCATION_KEY};
Expand Down Expand Up @@ -298,6 +299,9 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
let mut url = if did.is_local() || cache.inlined.contains(&did) {
repeat("../").take(loc.len()).collect::<String>()
} else {
if !cache.access_levels.is_doc_reachable(did) {
return None
}
match cache.extern_locations[&did.krate] {
(_, render::Remote(ref s)) => s.to_string(),
(_, render::Local) => repeat("../").take(loc.len()).collect(),
Expand Down
Loading