Skip to content

Commit 478a33d

Browse files
committed
Auto merge of #33002 - mitaa:rdoc-cross-impls, r=alexcrichton
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 #14586 fixes #31948 .. and also applies the reachability checking to cross-crate links. fixes #28480 r? @alexcrichton
2 parents e8c0aeb + 77b409a commit 478a33d

18 files changed

+472
-129
lines changed

src/librustc/middle/cstore.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub enum InlinedItemRef<'a> {
113113
/// LOCAL_CRATE in their DefId.
114114
pub const LOCAL_CRATE: ast::CrateNum = 0;
115115

116+
#[derive(Copy, Clone)]
116117
pub struct ChildItem {
117118
pub def: DefLike,
118119
pub name: ast::Name,

src/librustc/middle/privacy.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
use util::nodemap::{DefIdSet, FnvHashMap};
1616

1717
use std::hash::Hash;
18+
use std::fmt;
1819
use syntax::ast::NodeId;
1920

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

60+
impl<Id: Hash + Eq + fmt::Debug> fmt::Debug for AccessLevels<Id> {
61+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62+
fmt::Debug::fmt(&self.map, f)
63+
}
64+
}
65+
5966
/// A set containing all exported definitions from external crates.
6067
/// The set does not contain any entries from local crates.
6168
pub type ExternalExports = DefIdSet;

src/librustdoc/clean/inline.rs

+22-16
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ use rustc::middle::stability;
2626

2727
use rustc_const_eval::lookup_const_by_id;
2828

29-
use core::DocContext;
29+
use core::{DocContext, DocAccessLevels};
3030
use doctree;
31-
use clean::{self, Attributes, GetDefId};
31+
use clean::{self, GetDefId};
3232

3333
use super::{Clean, ToSource};
3434

@@ -116,7 +116,7 @@ fn try_inline_def(cx: &DocContext, tcx: &TyCtxt,
116116
}
117117
_ => return None,
118118
};
119-
cx.inlined.borrow_mut().as_mut().unwrap().insert(did);
119+
cx.renderinfo.borrow_mut().inlined.insert(did);
120120
ret.push(clean::Item {
121121
source: clean::Span::empty(),
122122
name: Some(tcx.item_name(did).to_string()),
@@ -146,7 +146,7 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
146146
elem.data.to_string()
147147
});
148148
let fqn = once(crate_name).chain(relative).collect();
149-
cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind));
149+
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
150150
}
151151
}
152152

@@ -260,11 +260,6 @@ pub fn build_impls(cx: &DocContext,
260260
match def {
261261
cstore::DlImpl(did) => build_impl(cx, tcx, did, impls),
262262
cstore::DlDef(Def::Mod(did)) => {
263-
// Don't recurse if this is a #[doc(hidden)] module
264-
if load_attrs(cx, tcx, did).list("doc").has_word("hidden") {
265-
return;
266-
}
267-
268263
for item in tcx.sess.cstore.item_children(did) {
269264
populate_impls(cx, tcx, item.def, impls)
270265
}
@@ -295,16 +290,17 @@ pub fn build_impl(cx: &DocContext,
295290
tcx: &TyCtxt,
296291
did: DefId,
297292
ret: &mut Vec<clean::Item>) {
298-
if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) {
293+
if !cx.renderinfo.borrow_mut().inlined.insert(did) {
299294
return
300295
}
301296

302297
let attrs = load_attrs(cx, tcx, did);
303298
let associated_trait = tcx.impl_trait_ref(did);
304-
if let Some(ref t) = associated_trait {
305-
// If this is an impl for a #[doc(hidden)] trait, be sure to not inline
306-
let trait_attrs = load_attrs(cx, tcx, t.def_id);
307-
if trait_attrs.list("doc").has_word("hidden") {
299+
300+
// Only inline impl if the implemented trait is
301+
// reachable in rustdoc generated documentation
302+
if let Some(traitref) = associated_trait {
303+
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
308304
return
309305
}
310306
}
@@ -330,6 +326,17 @@ pub fn build_impl(cx: &DocContext,
330326
});
331327
}
332328

329+
let ty = tcx.lookup_item_type(did);
330+
let for_ = ty.ty.clean(cx);
331+
332+
// Only inline impl if the implementing type is
333+
// reachable in rustdoc generated documentation
334+
if let Some(did) = for_.def_id() {
335+
if !cx.access_levels.borrow().is_doc_reachable(did) {
336+
return
337+
}
338+
}
339+
333340
let predicates = tcx.lookup_predicates(did);
334341
let trait_items = tcx.sess.cstore.impl_items(did)
335342
.iter()
@@ -412,7 +419,6 @@ pub fn build_impl(cx: &DocContext,
412419
}
413420
}).collect::<Vec<_>>();
414421
let polarity = tcx.trait_impl_polarity(did);
415-
let ty = tcx.lookup_item_type(did);
416422
let trait_ = associated_trait.clean(cx).map(|bound| {
417423
match bound {
418424
clean::TraitBound(polyt, _) => polyt.trait_,
@@ -436,7 +442,7 @@ pub fn build_impl(cx: &DocContext,
436442
derived: clean::detect_derived(&attrs),
437443
provided_trait_methods: provided,
438444
trait_: trait_,
439-
for_: ty.ty.clean(cx),
445+
for_: for_,
440446
generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx),
441447
items: trait_items,
442448
polarity: polarity.map(|p| { p.clean(cx) }),

src/librustdoc/clean/mod.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use syntax::ptr::P;
3636

3737
use rustc_trans::back::link;
3838
use rustc::middle::cstore::{self, CrateStore};
39+
use rustc::middle::privacy::AccessLevels;
3940
use rustc::hir::def::Def;
4041
use rustc::hir::def_id::{DefId, DefIndex};
4142
use rustc::ty::subst::{self, ParamSpace, VecPerParamSpace};
@@ -47,15 +48,17 @@ use rustc::hir;
4748
use std::collections::{HashMap, HashSet};
4849
use std::path::PathBuf;
4950
use std::rc::Rc;
51+
use std::sync::Arc;
5052
use std::u32;
5153
use std::env::current_dir;
54+
use std::mem;
5255

5356
use core::DocContext;
5457
use doctree;
5558
use visit_ast;
5659
use html::item_type::ItemType;
5760

58-
mod inline;
61+
pub mod inline;
5962
mod simplify;
6063

6164
// extract the stability index for a node from tcx, if possible
@@ -113,13 +116,16 @@ impl<T: Clean<U>, U> Clean<Vec<U>> for P<[T]> {
113116
}
114117
}
115118

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

@@ -128,14 +134,20 @@ struct CrateNum(ast::CrateNum);
128134
impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
129135
fn clean(&self, cx: &DocContext) -> Crate {
130136
use rustc::session::config::Input;
137+
use ::visit_lib::LibEmbargoVisitor;
131138

132139
if let Some(t) = cx.tcx_opt() {
133140
cx.deref_trait_did.set(t.lang_items.deref_trait());
141+
cx.renderinfo.borrow_mut().deref_trait_did = cx.deref_trait_did.get();
134142
}
135143

136144
let mut externs = Vec::new();
137145
for cnum in cx.sess().cstore.crates() {
138146
externs.push((cnum, CrateNum(cnum).clean(cx)));
147+
if cx.tcx_opt().is_some() {
148+
// Analyze doc-reachability for extern items
149+
LibEmbargoVisitor::new(cx).visit_lib(cnum);
150+
}
139151
}
140152
externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
141153

@@ -205,14 +217,17 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
205217
Input::Str { ref name, .. } => PathBuf::from(name.clone()),
206218
};
207219

220+
let mut access_levels = cx.access_levels.borrow_mut();
221+
let mut external_traits = cx.external_traits.borrow_mut();
222+
208223
Crate {
209224
name: name.to_string(),
210225
src: src,
211226
module: Some(module),
212227
externs: externs,
213228
primitives: primitives,
214-
external_traits: cx.external_traits.borrow_mut().take()
215-
.unwrap_or(HashMap::new()),
229+
access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())),
230+
external_traits: mem::replace(&mut external_traits, Default::default()),
216231
}
217232
}
218233
}
@@ -541,8 +556,7 @@ impl Clean<TyParam> for hir::TyParam {
541556

542557
impl<'tcx> Clean<TyParam> for ty::TypeParameterDef<'tcx> {
543558
fn clean(&self, cx: &DocContext) -> TyParam {
544-
cx.external_typarams.borrow_mut().as_mut().unwrap()
545-
.insert(self.def_id, self.name.clean(cx));
559+
cx.renderinfo.borrow_mut().external_typarams.insert(self.def_id, self.name.clean(cx));
546560
TyParam {
547561
name: self.name.clean(cx),
548562
did: self.def_id,
@@ -2685,7 +2699,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId {
26852699
inline::record_extern_fqn(cx, did, kind);
26862700
if let TypeTrait = kind {
26872701
let t = inline::build_external_trait(cx, tcx, did);
2688-
cx.external_traits.borrow_mut().as_mut().unwrap().insert(did, t);
2702+
cx.external_traits.borrow_mut().insert(did, t);
26892703
}
26902704
did
26912705
}

src/librustdoc/core.rs

+33-46
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ use syntax::feature_gate::UnstableFeatures;
2929
use syntax::parse::token;
3030

3131
use std::cell::{RefCell, Cell};
32-
use std::collections::{HashMap, HashSet};
32+
use std::collections::HashMap;
3333
use std::rc::Rc;
3434

3535
use visit_ast::RustdocVisitor;
3636
use clean;
3737
use clean::Clean;
38+
use html::render::RenderInfo;
3839

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

48-
pub type ExternalPaths = RefCell<Option<HashMap<DefId,
49-
(Vec<String>, clean::TypeKind)>>>;
49+
pub type Externs = HashMap<String, Vec<String>>;
50+
pub type ExternalPaths = HashMap<DefId, (Vec<String>, clean::TypeKind)>;
5051

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

6369
impl<'b, 'tcx> DocContext<'b, 'tcx> {
@@ -81,20 +87,23 @@ impl<'b, 'tcx> DocContext<'b, 'tcx> {
8187
}
8288
}
8389

84-
pub struct CrateAnalysis {
85-
pub access_levels: AccessLevels<DefId>,
86-
pub external_paths: ExternalPaths,
87-
pub external_typarams: RefCell<Option<HashMap<DefId, String>>>,
88-
pub inlined: RefCell<Option<HashSet<DefId>>>,
89-
pub deref_trait_did: Option<DefId>,
90+
pub trait DocAccessLevels {
91+
fn is_doc_reachable(&self, DefId) -> bool;
9092
}
9193

92-
pub type Externs = HashMap<String, Vec<String>>;
94+
impl DocAccessLevels for AccessLevels<DefId> {
95+
fn is_doc_reachable(&self, did: DefId) -> bool {
96+
self.is_public(did)
97+
}
98+
}
9399

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

101+
pub fn run_core(search_paths: SearchPaths,
102+
cfgs: Vec<String>,
103+
externs: Externs,
104+
input: Input,
105+
triple: Option<String>) -> (clean::Crate, RenderInfo)
106+
{
98107
// Parse, resolve, and typecheck the given crate.
99108

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

151-
let krate_and_analysis = abort_on_err(driver::phase_3_run_analysis_passes(&sess,
160+
abort_on_err(driver::phase_3_run_analysis_passes(&sess,
152161
&cstore,
153162
hir_map,
154163
&arenas,
@@ -175,42 +184,20 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
175184
map: &tcx.map,
176185
maybe_typed: Typed(tcx),
177186
input: input,
178-
external_traits: RefCell::new(Some(HashMap::new())),
179-
external_typarams: RefCell::new(Some(HashMap::new())),
180-
external_paths: RefCell::new(Some(HashMap::new())),
181-
inlined: RefCell::new(Some(HashSet::new())),
182187
all_crate_impls: RefCell::new(HashMap::new()),
183188
deref_trait_did: Cell::new(None),
189+
access_levels: RefCell::new(access_levels),
190+
external_traits: RefCell::new(HashMap::new()),
191+
renderinfo: RefCell::new(Default::default()),
184192
};
185193
debug!("crate: {:?}", ctxt.map.krate());
186194

187-
let mut analysis = CrateAnalysis {
188-
access_levels: access_levels,
189-
external_paths: RefCell::new(None),
190-
external_typarams: RefCell::new(None),
191-
inlined: RefCell::new(None),
192-
deref_trait_did: None,
193-
};
194-
195195
let krate = {
196-
let mut v = RustdocVisitor::new(&ctxt, Some(&analysis));
196+
let mut v = RustdocVisitor::new(&ctxt);
197197
v.visit(ctxt.map.krate());
198198
v.clean(&ctxt)
199199
};
200200

201-
let external_paths = ctxt.external_paths.borrow_mut().take();
202-
*analysis.external_paths.borrow_mut() = external_paths;
203-
204-
let map = ctxt.external_typarams.borrow_mut().take();
205-
*analysis.external_typarams.borrow_mut() = map;
206-
207-
let map = ctxt.inlined.borrow_mut().take();
208-
*analysis.inlined.borrow_mut() = map;
209-
210-
analysis.deref_trait_did = ctxt.deref_trait_did.get();
211-
212-
Some((krate, analysis))
213-
}), &sess);
214-
215-
krate_and_analysis.unwrap()
201+
Some((krate, ctxt.renderinfo.into_inner()))
202+
}), &sess).unwrap()
216203
}

src/librustdoc/html/format.rs

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use syntax::abi::Abi;
2424
use rustc::hir;
2525

2626
use clean;
27+
use core::DocAccessLevels;
2728
use html::item_type::ItemType;
2829
use html::render;
2930
use html::render::{cache, CURRENT_LOCATION_KEY};
@@ -298,6 +299,9 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
298299
let mut url = if did.is_local() || cache.inlined.contains(&did) {
299300
repeat("../").take(loc.len()).collect::<String>()
300301
} else {
302+
if !cache.access_levels.is_doc_reachable(did) {
303+
return None
304+
}
301305
match cache.extern_locations[&did.krate] {
302306
(_, render::Remote(ref s)) => s.to_string(),
303307
(_, render::Local) => repeat("../").take(loc.len()).collect(),

0 commit comments

Comments
 (0)