Skip to content

Commit

Permalink
Auto merge of rust-lang#33002 - mitaa:rdoc-cross-impls, r=alexcrichton
Browse files Browse the repository at this point in the history
rustdoc: refine cross-crate impl inlining

This changes the current rule that impls within `doc(hidden)` modules aren't inlined, to only inlining impls where the implemented trait and type are reachable in documentation.

fixes rust-lang#14586
fixes rust-lang#31948

.. and also applies the reachability checking to cross-crate links.

fixes rust-lang#28480

r? @alexcrichton
  • Loading branch information
bors committed Apr 19, 2016
2 parents e8c0aeb + 77b409a commit 478a33d
Show file tree
Hide file tree
Showing 18 changed files with 472 additions and 129 deletions.
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 @@ -36,6 +36,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 @@ -47,15 +48,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 @@ -113,13 +116,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 @@ -128,14 +134,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 @@ -205,14 +217,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 @@ -541,8 +556,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 @@ -2685,7 +2699,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

0 comments on commit 478a33d

Please sign in to comment.