Auto merge of #53162 - QuietMisdreavus:crouching-impl-hidden-trait, r=GuillaumeGomez

rustdoc: collect trait impls as an early pass

Fixes https://github.com/rust-lang/rust/issues/52545, fixes https://github.com/rust-lang/rust/issues/41480, fixes https://github.com/rust-lang/rust/issues/36922

Right now, rustdoc pulls all its impl information by scanning a crate's HIR for any items it finds. However, it doesn't recurse into anything other than modules, preventing it from seeing trait impls that may be inside things like functions or consts. Thanks to https://github.com/rust-lang/rust/pull/53002, now these items actually *exist* for rustdoc to see, but they still weren't getting collected for display.

But there was a secret. Whenever we pull in an item from another crate, we don't have any of its impls in the local HIR, so instead we ask the compiler for *everything* and filter out after the fact. This process is only triggered if there's a cross-crate re-export in the crate being documented, which can sometimes leave this info out of the docs. This PR instead moves this collection into an early pass, which occurs immediately after crate cleaning, so that that collection occurs regardless. In addition, by including the HIR's own `trait_impls` in addition to the existing `all_trait_implementations` calls, we can collect all these tricky trait impls without having to scan for them!
This commit is contained in:
bors 2018-09-20 14:32:26 +00:00
commit 3bc2ca7e4f
25 changed files with 643 additions and 221 deletions

View File

@ -2443,6 +2443,7 @@ name = "rustdoc"
version = "0.0.0"
dependencies = [
"minifier 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -709,17 +709,22 @@ impl<'hir> Map<'hir> {
}
}
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
/// Returns the DefId of `id`'s nearest module parent, or `id` itself if no
/// module parent is in this map.
pub fn get_module_parent(&self, id: NodeId) -> DefId {
let id = match self.walk_parent_nodes(id, |node| match *node {
self.local_def_id(self.get_module_parent_node(id))
}
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
/// module parent is in this map.
pub fn get_module_parent_node(&self, id: NodeId) -> NodeId {
match self.walk_parent_nodes(id, |node| match *node {
Node::Item(&Item { node: ItemKind::Mod(_), .. }) => true,
_ => false,
}, |_| false) {
Ok(id) => id,
Err(id) => id,
};
self.local_def_id(id)
}
}
/// Returns the nearest enclosing scope. A scope is an item or block.

View File

@ -11,3 +11,4 @@ path = "lib.rs"
pulldown-cmark = { version = "0.1.2", default-features = false }
minifier = "0.0.19"
tempfile = "3"
parking_lot = "0.6.4"

View File

@ -69,7 +69,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> BlanketImplFinder <'a, 'tcx, 'rcx, 'cstore> {
let real_name = name.clone().map(|name| Ident::from_str(&name));
let param_env = self.cx.tcx.param_env(def_id);
for &trait_def_id in self.cx.all_traits.iter() {
if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
if !self.cx.renderinfo.borrow().access_levels.is_doc_reachable(trait_def_id) ||
self.cx.generated_synthetics
.borrow_mut()
.get(&(def_id, trait_def_id))

View File

@ -29,8 +29,6 @@ use clean::{
self,
GetDefId,
ToSource,
get_auto_traits_with_def_id,
get_blanket_impls_with_def_id,
};
use super::Clean;
@ -56,7 +54,7 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa
let inner = match def {
Def::Trait(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Trait);
ret.extend(build_impls(cx, did, false));
ret.extend(build_impls(cx, did));
clean::TraitItem(build_external_trait(cx, did))
}
Def::Fn(did) => {
@ -65,27 +63,27 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa
}
Def::Struct(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Struct);
ret.extend(build_impls(cx, did, true));
ret.extend(build_impls(cx, did));
clean::StructItem(build_struct(cx, did))
}
Def::Union(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Union);
ret.extend(build_impls(cx, did, true));
ret.extend(build_impls(cx, did));
clean::UnionItem(build_union(cx, did))
}
Def::TyAlias(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Typedef);
ret.extend(build_impls(cx, did, false));
ret.extend(build_impls(cx, did));
clean::TypedefItem(build_type_alias(cx, did), false)
}
Def::Enum(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Enum);
ret.extend(build_impls(cx, did, true));
ret.extend(build_impls(cx, did));
clean::EnumItem(build_enum(cx, did))
}
Def::ForeignTy(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Foreign);
ret.extend(build_impls(cx, did, false));
ret.extend(build_impls(cx, did));
clean::ForeignTypeItem
}
// Never inline enum variants but leave them shown as re-exports.
@ -159,12 +157,11 @@ pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes {
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
let mut crate_name = cx.tcx.crate_name(did.krate).to_string();
if did.is_local() {
debug!("record_extern_fqn(did={:?}, kind+{:?}): def_id is local, aborting", did, kind);
return;
crate_name = cx.crate_name.clone().unwrap_or(crate_name);
}
let crate_name = cx.tcx.crate_name(did.krate).to_string();
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
// extern blocks have an empty name
let s = elem.data.to_string();
@ -179,7 +176,12 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
} else {
once(crate_name).chain(relative).collect()
};
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
if did.is_local() {
cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn);
} else {
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
}
}
pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
@ -271,7 +273,7 @@ fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef {
}
}
pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean::Item> {
pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
let tcx = cx.tcx;
let mut impls = Vec::new();
@ -279,85 +281,6 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean:
build_impl(cx, did, &mut impls);
}
if auto_traits {
let auto_impls = get_auto_traits_with_def_id(cx, did);
{
let mut renderinfo = cx.renderinfo.borrow_mut();
let new_impls: Vec<clean::Item> = auto_impls.into_iter()
.filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
impls.extend(new_impls);
}
impls.extend(get_blanket_impls_with_def_id(cx, did));
}
// If this is the first time we've inlined something from another crate, then
// we inline *all* impls from all the crates into this crate. Note that there's
// currently no way for us to filter this based on type, and we likely need
// many impls for a variety of reasons.
//
// Primarily, the impls will be used to populate the documentation for this
// type being inlined, but impls can also be used when generating
// documentation for primitives (no way to find those specifically).
if cx.populated_all_crate_impls.get() {
return impls;
}
cx.populated_all_crate_impls.set(true);
for &cnum in tcx.crates().iter() {
for did in tcx.all_trait_implementations(cnum).iter() {
build_impl(cx, *did, &mut impls);
}
}
// Also try to inline primitive impls from other crates.
let lang_items = tcx.lang_items();
let primitive_impls = [
lang_items.isize_impl(),
lang_items.i8_impl(),
lang_items.i16_impl(),
lang_items.i32_impl(),
lang_items.i64_impl(),
lang_items.i128_impl(),
lang_items.usize_impl(),
lang_items.u8_impl(),
lang_items.u16_impl(),
lang_items.u32_impl(),
lang_items.u64_impl(),
lang_items.u128_impl(),
lang_items.f32_impl(),
lang_items.f64_impl(),
lang_items.f32_runtime_impl(),
lang_items.f64_runtime_impl(),
lang_items.char_impl(),
lang_items.str_impl(),
lang_items.slice_impl(),
lang_items.slice_u8_impl(),
lang_items.str_alloc_impl(),
lang_items.slice_alloc_impl(),
lang_items.slice_u8_alloc_impl(),
lang_items.const_ptr_impl(),
lang_items.mut_ptr_impl(),
];
for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) {
if !def_id.is_local() {
build_impl(cx, def_id, &mut impls);
let auto_impls = get_auto_traits_with_def_id(cx, def_id);
let blanket_impls = get_blanket_impls_with_def_id(cx, def_id);
let mut renderinfo = cx.renderinfo.borrow_mut();
let new_impls: Vec<clean::Item> = auto_impls.into_iter()
.chain(blanket_impls.into_iter())
.filter(|i| renderinfo.inlined.insert(i.def_id))
.collect();
impls.extend(new_impls);
}
}
impls
}
@ -372,30 +295,60 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
// 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
if !did.is_local() {
if let Some(traitref) = associated_trait {
if !cx.renderinfo.borrow().access_levels.is_doc_reachable(traitref.def_id) {
return
}
}
}
let for_ = tcx.type_of(did).clean(cx);
let for_ = if let Some(nodeid) = tcx.hir.as_local_node_id(did) {
match tcx.hir.expect_item(nodeid).node {
hir::ItemKind::Impl(.., ref t, _) => {
t.clean(cx)
}
_ => panic!("did given to build_impl was not an impl"),
}
} else {
tcx.type_of(did).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
if !did.is_local() {
if let Some(did) = for_.def_id() {
if !cx.renderinfo.borrow().access_levels.is_doc_reachable(did) {
return
}
}
}
let predicates = tcx.predicates_of(did);
let trait_items = tcx.associated_items(did).filter_map(|item| {
if associated_trait.is_some() || item.vis == ty::Visibility::Public {
Some(item.clean(cx))
} else {
None
let (trait_items, generics) = if let Some(nodeid) = tcx.hir.as_local_node_id(did) {
match tcx.hir.expect_item(nodeid).node {
hir::ItemKind::Impl(.., ref gen, _, _, ref item_ids) => {
(
item_ids.iter()
.map(|ii| tcx.hir.impl_item(ii.id).clean(cx))
.collect::<Vec<_>>(),
gen.clean(cx),
)
}
_ => panic!("did given to build_impl was not an impl"),
}
}).collect::<Vec<_>>();
} else {
(
tcx.associated_items(did).filter_map(|item| {
if associated_trait.is_some() || item.vis == ty::Visibility::Public {
Some(item.clean(cx))
} else {
None
}
}).collect::<Vec<_>>(),
(tcx.generics_of(did), &predicates).clean(cx),
)
};
let polarity = tcx.impl_polarity(did);
let trait_ = associated_trait.clean(cx).map(|bound| {
match bound {
@ -417,10 +370,12 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
.collect()
}).unwrap_or(FxHashSet());
debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
ret.push(clean::Item {
inner: clean::ImplItem(clean::Impl {
unsafety: hir::Unsafety::Normal,
generics: (tcx.generics_of(did), &predicates).clean(cx),
generics,
provided_trait_methods: provided,
trait_,
for_,
@ -465,7 +420,11 @@ fn build_module(cx: &DocContext, did: DefId, visited: &mut FxHashSet<DefId>) ->
}
pub fn print_inlined_const(cx: &DocContext, did: DefId) -> String {
cx.tcx.rendered_const(did)
if let Some(node_id) = cx.tcx.hir.as_local_node_id(did) {
cx.tcx.hir.node_to_pretty_string(node_id)
} else {
cx.tcx.rendered_const(did)
}
}
fn build_const(cx: &DocContext, did: DefId) -> clean::Constant {
@ -576,16 +535,27 @@ fn separate_supertrait_bounds(mut g: clean::Generics)
}
pub fn record_extern_trait(cx: &DocContext, did: DefId) {
if cx.external_traits.borrow().contains_key(&did) ||
cx.active_extern_traits.borrow().contains(&did)
{
if did.is_local() {
return;
}
{
let external_traits = cx.external_traits.lock();
if external_traits.borrow().contains_key(&did) ||
cx.active_extern_traits.borrow().contains(&did)
{
return;
}
}
cx.active_extern_traits.borrow_mut().push(did);
debug!("record_extern_trait: {:?}", did);
let trait_ = build_external_trait(cx, did);
cx.external_traits.borrow_mut().insert(did, trait_);
{
let external_traits = cx.external_traits.lock();
external_traits.borrow_mut().insert(did, trait_);
}
cx.active_extern_traits.borrow_mut().remove_item(&did);
}

View File

@ -28,7 +28,6 @@ use syntax::symbol::InternedString;
use syntax_pos::{self, DUMMY_SP, Pos, FileName};
use rustc::mir::interpret::ConstValue;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::resolve_lifetime as rl;
use rustc::ty::fold::TypeFolder;
use rustc::middle::lang_items;
@ -56,6 +55,8 @@ use std::cell::RefCell;
use std::sync::Arc;
use std::u32;
use parking_lot::ReentrantMutex;
use core::{self, DocContext};
use doctree;
use visit_ast;
@ -135,10 +136,9 @@ pub struct Crate {
pub module: Option<Item>,
pub externs: Vec<(CrateNum, ExternalCrate)>,
pub primitives: Vec<(DefId, PrimitiveType, Attributes)>,
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: FxHashMap<DefId, Trait>,
pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, Trait>>>>,
pub masked_crates: FxHashSet<CrateNum>,
}
@ -209,9 +209,6 @@ impl<'a, 'tcx, 'rcx, 'cstore> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tc
}));
}
let mut access_levels = cx.access_levels.borrow_mut();
let mut external_traits = cx.external_traits.borrow_mut();
Crate {
name,
version: None,
@ -219,8 +216,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tc
module: Some(module),
externs,
primitives,
access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())),
external_traits: mem::replace(&mut external_traits, Default::default()),
external_traits: cx.external_traits.clone(),
masked_crates,
}
}
@ -579,9 +575,9 @@ impl Clean<Item> for doctree::Module {
let mut items: Vec<Item> = vec![];
items.extend(self.extern_crates.iter().map(|x| x.clean(cx)));
items.extend(self.imports.iter().flat_map(|x| x.clean(cx)));
items.extend(self.structs.iter().flat_map(|x| x.clean(cx)));
items.extend(self.unions.iter().flat_map(|x| x.clean(cx)));
items.extend(self.enums.iter().flat_map(|x| x.clean(cx)));
items.extend(self.structs.iter().map(|x| x.clean(cx)));
items.extend(self.unions.iter().map(|x| x.clean(cx)));
items.extend(self.enums.iter().map(|x| x.clean(cx)));
items.extend(self.fns.iter().map(|x| x.clean(cx)));
items.extend(self.foreigns.iter().flat_map(|x| x.clean(cx)));
items.extend(self.mods.iter().map(|x| x.clean(cx)));
@ -2436,7 +2432,7 @@ impl Clean<Type> for hir::Ty {
if let Def::TyAlias(def_id) = path.def {
// Substitute private type aliases
if let Some(node_id) = cx.tcx.hir.as_local_node_id(def_id) {
if !cx.access_levels.borrow().is_exported(def_id) {
if !cx.renderinfo.borrow().access_levels.is_exported(def_id) {
alias = Some(&cx.tcx.hir.expect_item(node_id).node);
}
}
@ -2816,14 +2812,10 @@ pub struct Union {
pub fields_stripped: bool,
}
impl Clean<Vec<Item>> for doctree::Struct {
fn clean(&self, cx: &DocContext) -> Vec<Item> {
let name = self.name.clean(cx);
let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
ret.push(Item {
name: Some(name),
impl Clean<Item> for doctree::Struct {
fn clean(&self, cx: &DocContext) -> Item {
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
source: self.whence.clean(cx),
def_id: cx.tcx.hir.local_def_id(self.id),
@ -2836,20 +2828,14 @@ impl Clean<Vec<Item>> for doctree::Struct {
fields: self.fields.clean(cx),
fields_stripped: false,
}),
});
ret
}
}
}
impl Clean<Vec<Item>> for doctree::Union {
fn clean(&self, cx: &DocContext) -> Vec<Item> {
let name = self.name.clean(cx);
let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
ret.push(Item {
name: Some(name),
impl Clean<Item> for doctree::Union {
fn clean(&self, cx: &DocContext) -> Item {
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
source: self.whence.clean(cx),
def_id: cx.tcx.hir.local_def_id(self.id),
@ -2862,9 +2848,7 @@ impl Clean<Vec<Item>> for doctree::Union {
fields: self.fields.clean(cx),
fields_stripped: false,
}),
});
ret
}
}
}
@ -2895,14 +2879,10 @@ pub struct Enum {
pub variants_stripped: bool,
}
impl Clean<Vec<Item>> for doctree::Enum {
fn clean(&self, cx: &DocContext) -> Vec<Item> {
let name = self.name.clean(cx);
let mut ret = get_auto_traits_with_node_id(cx, self.id, name.clone());
ret.extend(get_blanket_impls_with_node_id(cx, self.id, name.clone()));
ret.push(Item {
name: Some(name),
impl Clean<Item> for doctree::Enum {
fn clean(&self, cx: &DocContext) -> Item {
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
source: self.whence.clean(cx),
def_id: cx.tcx.hir.local_def_id(self.id),
@ -2914,9 +2894,7 @@ impl Clean<Vec<Item>> for doctree::Enum {
generics: self.generics.clean(cx),
variants_stripped: false,
}),
});
ret
}
}
}
@ -3445,11 +3423,7 @@ fn build_deref_target_impls(cx: &DocContext,
let primitive = match *target {
ResolvedPath { did, .. } if did.is_local() => continue,
ResolvedPath { did, .. } => {
// We set the last parameter to false to avoid looking for auto-impls for traits
// and therefore avoid an ICE.
// The reason behind this is that auto-traits don't propagate through Deref so
// we're not supposed to synthesise impls for them.
ret.extend(inline::build_impls(cx, did, false));
ret.extend(inline::build_impls(cx, did));
continue
}
_ => match target.primitive_type() {

View File

@ -26,7 +26,7 @@ use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::CStore;
use rustc_target::spec::TargetTriple;
use syntax::ast::{self, Ident};
use syntax::ast::{self, Ident, NodeId};
use syntax::source_map;
use syntax::edition::Edition;
use syntax::feature_gate::UnstableFeatures;
@ -36,11 +36,13 @@ use syntax::symbol::keywords;
use syntax_pos::DUMMY_SP;
use errors;
use errors::emitter::{Emitter, EmitterWriter};
use parking_lot::ReentrantMutex;
use std::cell::{RefCell, Cell};
use std::cell::RefCell;
use std::mem;
use rustc_data_structures::sync::{self, Lrc};
use std::rc::Rc;
use std::sync::Arc;
use std::path::PathBuf;
use visit_ast::RustdocVisitor;
@ -60,16 +62,13 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx> {
/// The stack of module NodeIds up till this point
pub crate_name: Option<String>,
pub cstore: Rc<CStore>,
pub populated_all_crate_impls: Cell<bool>,
// 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<FxHashMap<DefId, clean::Trait>>,
pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, clean::Trait>>>>,
/// Used while populating `external_traits` to ensure we don't process the same trait twice at
/// the same time.
pub active_extern_traits: RefCell<Vec<DefId>>,
@ -164,6 +163,16 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocContext<'a, 'tcx, 'rcx, 'cstore> {
def_id.clone()
}
/// Like the function of the same name on the HIR map, but skips calling it on fake DefIds.
/// (This avoids a slice-index-out-of-bounds panic.)
pub fn as_local_node_id(&self, def_id: DefId) -> Option<NodeId> {
if self.all_fake_def_ids.borrow().contains(&def_id) {
None
} else {
self.tcx.hir.as_local_node_id(def_id)
}
}
pub fn get_real_ty<F>(&self,
def_id: DefId,
def_ctor: &F,
@ -509,16 +518,17 @@ pub fn run_core(search_paths: SearchPaths,
clean::path_to_def(&tcx, &["core", "marker", "Send"])
};
let mut renderinfo = RenderInfo::default();
renderinfo.access_levels = access_levels;
let ctxt = DocContext {
tcx,
resolver: &resolver,
crate_name,
cstore: cstore.clone(),
populated_all_crate_impls: Cell::new(false),
access_levels: RefCell::new(access_levels),
external_traits: Default::default(),
active_extern_traits: Default::default(),
renderinfo: Default::default(),
renderinfo: RefCell::new(renderinfo),
ty_substs: Default::default(),
lt_substs: Default::default(),
impl_trait_bounds: Default::default(),

View File

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
use clean::*;
pub struct StripItem(pub Item);
@ -116,11 +114,14 @@ pub trait DocFolder : Sized {
fn fold_crate(&mut self, mut c: Crate) -> Crate {
c.module = c.module.take().and_then(|module| self.fold_item(module));
let traits = mem::replace(&mut c.external_traits, Default::default());
c.external_traits.extend(traits.into_iter().map(|(k, mut v)| {
v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
(k, v)
}));
{
let guard = c.external_traits.lock();
let traits = guard.replace(Default::default());
guard.borrow_mut().extend(traits.into_iter().map(|(k, mut v)| {
v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
(k, v)
}));
}
c
}
}

View File

@ -313,7 +313,7 @@ pub struct Cache {
// 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.
pub access_levels: Arc<AccessLevels<DefId>>,
pub access_levels: AccessLevels<DefId>,
/// The version of the crate being documented, if given from the `--crate-version` flag.
pub crate_version: Option<String>,
@ -337,6 +337,15 @@ pub struct Cache {
// and their parent id here and indexes them at the end of crate parsing.
orphan_impl_items: Vec<(DefId, clean::Item)>,
// Similarly to `orphan_impl_items`, sometimes trait impls are picked up
// even though the trait itself is not exported. This can happen if a trait
// was defined in function/expression scope, since the impl will be picked
// up by `collect-trait-impls` but the trait won't be scraped out in the HIR
// crawl. In order to prevent crashes when looking for spotlight traits or
// when gathering trait documentation on a type, hold impls here while
// folding and add them to the cache later on if we find the trait.
orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
/// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
/// we need the alias element to have an array of items.
aliases: FxHashMap<String, Vec<IndexItem>>,
@ -350,6 +359,7 @@ pub struct RenderInfo {
pub external_paths: ::core::ExternalPaths,
pub external_typarams: FxHashMap<DefId, String>,
pub exact_paths: FxHashMap<DefId, Vec<String>>,
pub access_levels: AccessLevels<DefId>,
pub deref_trait_did: Option<DefId>,
pub deref_mut_trait_did: Option<DefId>,
pub owned_box_did: Option<DefId>,
@ -569,6 +579,7 @@ pub fn run(mut krate: clean::Crate,
external_paths,
external_typarams,
exact_paths,
access_levels,
deref_trait_did,
deref_mut_trait_did,
owned_box_did,
@ -591,10 +602,11 @@ pub fn run(mut krate: clean::Crate,
extern_locations: FxHashMap(),
primitive_locations: FxHashMap(),
stripped_mod: false,
access_levels: krate.access_levels.clone(),
access_levels,
crate_version: krate.version.take(),
orphan_impl_items: Vec::new(),
traits: mem::replace(&mut krate.external_traits, FxHashMap()),
orphan_trait_impls: Vec::new(),
traits: krate.external_traits.lock().replace(FxHashMap()),
deref_trait_did,
deref_mut_trait_did,
owned_box_did,
@ -636,6 +648,14 @@ pub fn run(mut krate: clean::Crate,
cache.stack.push(krate.name.clone());
krate = cache.fold_crate(krate);
for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) {
if cache.traits.contains_key(&trait_did) {
for did in dids {
cache.impls.entry(did).or_insert(vec![]).push(impl_.clone());
}
}
}
// Build our search index
let index = build_index(&krate, &mut cache);
@ -1223,6 +1243,10 @@ impl<'a> SourceCollector<'a> {
impl DocFolder for Cache {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
if item.def_id.is_local() {
debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id);
}
// If this is a stripped module,
// we don't want it or its children in the search index.
let orig_stripped_mod = match item.inner {
@ -1453,10 +1477,16 @@ impl DocFolder for Cache {
} else {
unreachable!()
};
for did in dids {
self.impls.entry(did).or_default().push(Impl {
impl_item: item.clone(),
});
let impl_item = Impl {
impl_item: item,
};
if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) {
for did in dids {
self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
}
} else {
let trait_did = impl_item.trait_did().unwrap();
self.orphan_trait_impls.push((trait_did, dids, impl_item));
}
None
} else {

View File

@ -24,6 +24,7 @@
#![feature(ptr_offset_from)]
#![feature(crate_visibility_modifier)]
#![feature(const_fn)]
#![feature(drain_filter)]
#![recursion_limit="256"]
@ -48,6 +49,7 @@ extern crate rustc_errors as errors;
extern crate pulldown_cmark;
extern crate tempfile;
extern crate minifier;
extern crate parking_lot;
extern crate serialize as rustc_serialize; // used by deriving

View File

@ -69,16 +69,21 @@ impl<'a, 'tcx, 'rcx, 'cstore> LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
/// Resolve a given string as a path, along with whether or not it is
/// in the value namespace. Also returns an optional URL fragment in the case
/// of variants and methods
fn resolve(&self, path_str: &str, is_val: bool, current_item: &Option<String>)
fn resolve(&self,
path_str: &str,
is_val: bool,
current_item: &Option<String>,
parent_id: Option<NodeId>)
-> Result<(Def, Option<String>), ()>
{
let cx = self.cx;
// In case we're in a module, try to resolve the relative
// path
if let Some(id) = self.mod_ids.last() {
if let Some(id) = parent_id.or(self.mod_ids.last().cloned()) {
// FIXME: `with_scope` requires the NodeId of a module
let result = cx.resolver.borrow_mut()
.with_scope(*id,
.with_scope(id,
|resolver| {
resolver.resolve_str_path_error(DUMMY_SP,
&path_str, is_val)
@ -129,8 +134,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
}
}
// FIXME: `with_scope` requires the NodeId of a module
let ty = cx.resolver.borrow_mut()
.with_scope(*id,
.with_scope(id,
|resolver| {
resolver.resolve_str_path_error(DUMMY_SP, &path, false)
})?;
@ -218,6 +224,20 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
None
};
// FIXME: get the resolver to work with non-local resolve scopes
let parent_node = self.cx.as_local_node_id(item.def_id).and_then(|node_id| {
// FIXME: this fails hard for impls in non-module scope, but is necessary for the
// current resolve() implementation
match self.cx.tcx.hir.get_module_parent_node(node_id) {
id if id != node_id => Some(id),
_ => None,
}
});
if parent_node.is_some() {
debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
}
let current_item = match item.inner {
ModuleItem(..) => {
if item.attrs.inner_docs {
@ -227,10 +247,10 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
None
}
} else {
match self.mod_ids.last() {
Some(parent) if *parent != NodeId::new(0) => {
match parent_node.or(self.mod_ids.last().cloned()) {
Some(parent) if parent != NodeId::new(0) => {
//FIXME: can we pull the parent module's name from elsewhere?
Some(self.cx.tcx.hir.name(*parent).to_string())
Some(self.cx.tcx.hir.name(parent).to_string())
}
_ => None,
}
@ -294,7 +314,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
match kind {
PathKind::Value => {
if let Ok(def) = self.resolve(path_str, true, &current_item) {
if let Ok(def) = self.resolve(path_str, true, &current_item, parent_node) {
def
} else {
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
@ -305,7 +325,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
}
}
PathKind::Type => {
if let Ok(def) = self.resolve(path_str, false, &current_item) {
if let Ok(def) = self.resolve(path_str, false, &current_item, parent_node) {
def
} else {
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
@ -316,16 +336,18 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
PathKind::Unknown => {
// try everything!
if let Some(macro_def) = macro_resolve(cx, path_str) {
if let Ok(type_def) = self.resolve(path_str, false, &current_item) {
if let Ok(type_def) =
self.resolve(path_str, false, &current_item, parent_node)
{
let (type_kind, article, type_disambig)
= type_ns_kind(type_def.0, path_str);
ambiguity_error(cx, &item.attrs, path_str,
article, type_kind, &type_disambig,
"a", "macro", &format!("macro@{}", path_str));
continue;
} else if let Ok(value_def) = self.resolve(path_str,
true,
&current_item) {
} else if let Ok(value_def) =
self.resolve(path_str, true, &current_item, parent_node)
{
let (value_kind, value_disambig)
= value_ns_kind(value_def.0, path_str)
.expect("struct and mod cases should have been \
@ -335,12 +357,16 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
"a", "macro", &format!("macro@{}", path_str));
}
(macro_def, None)
} else if let Ok(type_def) = self.resolve(path_str, false, &current_item) {
} else if let Ok(type_def) =
self.resolve(path_str, false, &current_item, parent_node)
{
// It is imperative we search for not-a-value first
// Otherwise we will find struct ctors for when we are looking
// for structs, and the link won't work.
// if there is something in both namespaces
if let Ok(value_def) = self.resolve(path_str, true, &current_item) {
if let Ok(value_def) =
self.resolve(path_str, true, &current_item, parent_node)
{
let kind = value_ns_kind(value_def.0, path_str);
if let Some((value_kind, value_disambig)) = kind {
let (type_kind, article, type_disambig)
@ -352,7 +378,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
}
}
type_def
} else if let Ok(value_def) = self.resolve(path_str, true, &current_item) {
} else if let Ok(value_def) =
self.resolve(path_str, true, &current_item, parent_node)
{
value_def
} else {
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);

View File

@ -0,0 +1,219 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clean::*;
use rustc::util::nodemap::FxHashSet;
use rustc::hir::def_id::DefId;
use super::Pass;
use core::DocContext;
use fold::DocFolder;
pub const COLLECT_TRAIT_IMPLS: Pass =
Pass::early("collect-trait-impls", collect_trait_impls,
"retrieves trait impls for items in the crate");
pub fn collect_trait_impls(krate: Crate, cx: &DocContext) -> Crate {
let mut synth = SyntheticImplCollector::new(cx);
let mut krate = synth.fold_crate(krate);
let prims: FxHashSet<PrimitiveType> =
krate.primitives.iter().map(|p| p.1).collect();
let crate_items = {
let mut coll = ItemCollector::new();
krate = coll.fold_crate(krate);
coll.items
};
let mut new_items = Vec::new();
for &cnum in cx.tcx.crates().iter() {
for &did in cx.tcx.all_trait_implementations(cnum).iter() {
inline::build_impl(cx, did, &mut new_items);
}
}
// Also try to inline primitive impls from other crates.
let lang_items = cx.tcx.lang_items();
let primitive_impls = [
lang_items.isize_impl(),
lang_items.i8_impl(),
lang_items.i16_impl(),
lang_items.i32_impl(),
lang_items.i64_impl(),
lang_items.i128_impl(),
lang_items.usize_impl(),
lang_items.u8_impl(),
lang_items.u16_impl(),
lang_items.u32_impl(),
lang_items.u64_impl(),
lang_items.u128_impl(),
lang_items.f32_impl(),
lang_items.f64_impl(),
lang_items.f32_runtime_impl(),
lang_items.f64_runtime_impl(),
lang_items.char_impl(),
lang_items.str_impl(),
lang_items.slice_impl(),
lang_items.slice_u8_impl(),
lang_items.str_alloc_impl(),
lang_items.slice_alloc_impl(),
lang_items.slice_u8_alloc_impl(),
lang_items.const_ptr_impl(),
lang_items.mut_ptr_impl(),
];
for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) {
if !def_id.is_local() {
inline::build_impl(cx, def_id, &mut new_items);
let auto_impls = get_auto_traits_with_def_id(cx, def_id);
let blanket_impls = get_blanket_impls_with_def_id(cx, def_id);
let mut renderinfo = cx.renderinfo.borrow_mut();
let new_impls: Vec<Item> = auto_impls.into_iter()
.chain(blanket_impls.into_iter())
.filter(|i| renderinfo.inlined.insert(i.def_id))
.collect();
new_items.extend(new_impls);
}
}
let mut cleaner = BadImplStripper {
prims,
items: crate_items,
};
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
for it in &new_items {
if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.inner {
if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() {
let target = items.iter().filter_map(|item| {
match item.inner {
TypedefItem(ref t, true) => Some(&t.type_),
_ => None,
}
}).next().expect("Deref impl without Target type");
if let Some(prim) = target.primitive_type() {
cleaner.prims.insert(prim);
} else if let Some(did) = target.def_id() {
cleaner.items.insert(did);
}
}
}
}
new_items.retain(|it| {
if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.inner {
cleaner.keep_item(for_) ||
trait_.as_ref().map_or(false, |t| cleaner.keep_item(t)) ||
blanket_impl.is_some()
} else {
true
}
});
// `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations`
// doesn't work with it anyway, so pull them from the HIR map instead
for &trait_did in cx.all_traits.iter() {
for &impl_node in cx.tcx.hir.trait_impls(trait_did) {
let impl_did = cx.tcx.hir.local_def_id(impl_node);
inline::build_impl(cx, impl_did, &mut new_items);
}
}
if let Some(ref mut it) = krate.module {
if let ModuleItem(Module { ref mut items, .. }) = it.inner {
items.extend(synth.impls);
items.extend(new_items);
} else {
panic!("collect-trait-impls can't run");
}
} else {
panic!("collect-trait-impls can't run");
}
krate
}
struct SyntheticImplCollector<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx> {
cx: &'a DocContext<'a, 'tcx, 'rcx, 'cstore>,
impls: Vec<Item>,
}
impl<'a, 'tcx, 'rcx, 'cstore> SyntheticImplCollector<'a, 'tcx, 'rcx, 'cstore> {
fn new(cx: &'a DocContext<'a, 'tcx, 'rcx, 'cstore>) -> Self {
SyntheticImplCollector {
cx,
impls: Vec::new(),
}
}
}
impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for SyntheticImplCollector<'a, 'tcx, 'rcx, 'cstore> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if i.is_struct() || i.is_enum() || i.is_union() {
if let (Some(node_id), Some(name)) =
(self.cx.tcx.hir.as_local_node_id(i.def_id), i.name.clone())
{
self.impls.extend(get_auto_traits_with_node_id(self.cx, node_id, name.clone()));
self.impls.extend(get_blanket_impls_with_node_id(self.cx, node_id, name));
} else {
self.impls.extend(get_auto_traits_with_def_id(self.cx, i.def_id));
self.impls.extend(get_blanket_impls_with_def_id(self.cx, i.def_id));
}
}
self.fold_item_recur(i)
}
}
#[derive(Default)]
struct ItemCollector {
items: FxHashSet<DefId>,
}
impl ItemCollector {
fn new() -> Self {
Self::default()
}
}
impl DocFolder for ItemCollector {
fn fold_item(&mut self, i: Item) -> Option<Item> {
self.items.insert(i.def_id);
self.fold_item_recur(i)
}
}
struct BadImplStripper {
prims: FxHashSet<PrimitiveType>,
items: FxHashSet<DefId>,
}
impl BadImplStripper {
fn keep_item(&self, ty: &Type) -> bool {
if let Generic(_) = ty {
// keep impls made on generics
true
} else if let Some(prim) = ty.primitive_type() {
self.prims.contains(&prim)
} else if let Some(did) = ty.def_id() {
self.items.contains(&did)
} else {
false
}
}
}

View File

@ -43,6 +43,9 @@ pub use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
mod collect_intra_doc_links;
pub use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;
mod collect_trait_impls;
pub use self::collect_trait_impls::COLLECT_TRAIT_IMPLS;
/// Represents a single pass.
#[derive(Copy, Clone)]
pub enum Pass {
@ -132,10 +135,12 @@ pub const PASSES: &'static [Pass] = &[
STRIP_PRIV_IMPORTS,
PROPAGATE_DOC_CFG,
COLLECT_INTRA_DOC_LINKS,
COLLECT_TRAIT_IMPLS,
];
/// The list of passes run by default.
pub const DEFAULT_PASSES: &'static [&'static str] = &[
"collect-trait-impls",
"strip-hidden",
"strip-private",
"collect-intra-doc-links",
@ -146,6 +151,7 @@ pub const DEFAULT_PASSES: &'static [&'static str] = &[
/// The list of default passes run with `--document-private-items` is passed to rustdoc.
pub const DEFAULT_PRIVATE_PASSES: &'static [&'static str] = &[
"collect-trait-impls",
"strip-priv-imports",
"collect-intra-doc-links",
"collapse-docs",
@ -189,6 +195,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
// We need to recurse into stripped modules to strip things
// like impl methods but when doing so we must not add any
// items to the `retained` set.
debug!("Stripper: recursing into stripped {} {:?}", i.type_(), i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = self.fold_item_recur(i);
self.update_retained = old;
@ -212,6 +219,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
| clean::ForeignTypeItem => {
if i.def_id.is_local() {
if !self.access_levels.is_exported(i.def_id) {
debug!("Stripper: stripping {} {:?}", i.type_(), i.name);
return None;
}
}
@ -225,6 +233,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
clean::ModuleItem(..) => {
if i.def_id.is_local() && i.visibility != Some(clean::Public) {
debug!("Stripper: stripping module {:?}", i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
self.update_retained = old;
@ -296,11 +305,13 @@ impl<'a> fold::DocFolder for ImplStripper<'a> {
}
if let Some(did) = imp.for_.def_id() {
if did.is_local() && !imp.for_.is_generic() && !self.retained.contains(&did) {
debug!("ImplStripper: impl item for stripped type; removing");
return None;
}
}
if let Some(did) = imp.trait_.def_id() {
if did.is_local() && !self.retained.contains(&did) {
debug!("ImplStripper: impl item for stripped trait; removing");
return None;
}
}
@ -308,6 +319,8 @@ impl<'a> fold::DocFolder for ImplStripper<'a> {
for typaram in generics {
if let Some(did) = typaram.def_id() {
if did.is_local() && !self.retained.contains(&did) {
debug!("ImplStripper: stripped item in trait's generics; \
removing impl");
return None;
}
}

View File

@ -35,7 +35,9 @@ pub fn strip_hidden(krate: clean::Crate, _: &DocContext) -> clean::Crate {
// strip all impls referencing stripped items
let mut stripper = ImplStripper { retained: &retained };
stripper.fold_crate(krate)
let krate = stripper.fold_crate(krate);
krate
}
struct Stripper<'a> {
@ -46,7 +48,7 @@ struct Stripper<'a> {
impl<'a> fold::DocFolder for Stripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if i.attrs.lists("doc").has_word("hidden") {
debug!("found one in strip_hidden; removing");
debug!("strip_hidden: stripping {} {:?}", i.type_(), i.name);
// use a dedicated hidden item for given item type if any
match i.inner {
clean::StructFieldItem(..) | clean::ModuleItem(..) => {

View File

@ -22,10 +22,10 @@ pub const STRIP_PRIVATE: Pass =
/// Strip private items from the point of view of a crate or externally from a
/// crate, specified by the `xcrate` flag.
pub fn strip_private(mut krate: clean::Crate, _: &DocContext) -> clean::Crate {
pub fn strip_private(mut krate: clean::Crate, cx: &DocContext) -> clean::Crate {
// This stripper collects all *retained* nodes.
let mut retained = DefIdSet();
let access_levels = krate.access_levels.clone();
let access_levels = cx.renderinfo.borrow().access_levels.clone();
// strip all private items
{

View File

@ -269,7 +269,10 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
Def::Enum(did) |
Def::ForeignTy(did) |
Def::TyAlias(did) if !self_is_hidden => {
self.cx.access_levels.borrow_mut().map.insert(did, AccessLevel::Public);
self.cx.renderinfo
.borrow_mut()
.access_levels.map
.insert(did, AccessLevel::Public);
},
Def::Mod(did) => if !self_is_hidden {
::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did);
@ -284,7 +287,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
Some(n) => n, None => return false
};
let is_private = !self.cx.access_levels.borrow().is_public(def_did);
let is_private = !self.cx.renderinfo.borrow().access_levels.is_public(def_did);
let is_hidden = inherits_doc_hidden(self.cx, def_node_id);
// Only inline if requested or if the item would otherwise be stripped
@ -510,9 +513,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
ref tr,
ref ty,
ref item_ids) => {
// Don't duplicate impls when inlining, we'll pick them up
// regardless of where they're located.
if !self.inlining {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && tr.is_none() {
let items = item_ids.iter()
.map(|ii| self.cx.tcx.hir.impl_item(ii.id).clone())
.collect();

View File

@ -38,7 +38,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> LibEmbargoVisitor<'a, 'tcx, 'rcx, 'cstore> {
) -> LibEmbargoVisitor<'a, 'tcx, 'rcx, 'cstore> {
LibEmbargoVisitor {
cx,
access_levels: cx.access_levels.borrow_mut(),
access_levels: RefMut::map(cx.renderinfo.borrow_mut(), |ri| &mut ri.access_levels),
prev_level: Some(AccessLevel::Public),
visited_mods: FxHashSet()
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-stage1
// Issue #52129: ICE when trying to document the `quote` proc-macro from proc_macro
// As of this writing, we don't currently attempt to document proc-macros. However, we shouldn't

View File

@ -0,0 +1,23 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_name = "inner"]
pub struct SomeStruct;
fn asdf() {
const _FOO: () = {
impl Clone for SomeStruct {
fn clone(&self) -> Self {
SomeStruct
}
}
};
}

View File

@ -0,0 +1,17 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:trait-vis.rs
extern crate inner;
// @has trait_vis/struct.SomeStruct.html
// @has - '//code' 'impl Clone for SomeStruct'
pub use inner::SomeStruct;

View File

@ -0,0 +1,28 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub trait ThisTrait {}
mod asdf {
use ThisTrait;
pub struct SomeStruct;
impl ThisTrait for SomeStruct {}
trait PrivateTrait {}
impl PrivateTrait for SomeStruct {}
}
// @has trait_vis/struct.SomeStruct.html
// @has - '//code' 'impl ThisTrait for SomeStruct'
// !@has - '//code' 'impl PrivateTrait for SomeStruct'
pub use asdf::SomeStruct;

View File

@ -0,0 +1,40 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// we need to make sure that intra-doc links on trait impls get resolved in the right scope
#![deny(intra_doc_link_resolution_failure)]
pub mod inner {
pub struct SomethingOutOfScope;
}
pub mod other {
use inner::SomethingOutOfScope;
use SomeTrait;
pub struct OtherStruct;
/// Let's link to [SomethingOutOfScope] while we're at it.
impl SomeTrait for OtherStruct {}
}
pub trait SomeTrait {}
pub struct SomeStruct;
fn __implementation_details() {
use inner::SomethingOutOfScope;
// FIXME: intra-links resolve in their nearest module scope, not their actual scope in cases
// like this
// Let's link to [SomethingOutOfScope] while we're at it.
impl SomeTrait for SomeStruct {}
}

View File

@ -10,9 +10,6 @@
#![crate_name = "foo"]
// we need to reexport something from libstd so that `all_trait_implementations` is called.
pub use std::string::String;
include!("primitive/primitive-generic-impl.rs");
// @has foo/primitive.i32.html '//h3[@id="impl-ToString"]//code' 'impl<T> ToString for T'

View File

@ -0,0 +1,23 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// when implementing the fix for traits-in-bodies, there was an ICE when documenting private items
// and a trait was defined in non-module scope
// compile-flags:--document-private-items
// @has traits_in_bodies_private/struct.SomeStruct.html
// @!has - '//code' 'impl HiddenTrait for SomeStruct'
pub struct SomeStruct;
fn __implementation_details() {
trait HiddenTrait {}
impl HiddenTrait for SomeStruct {}
}

View File

@ -11,11 +11,10 @@
//prior to fixing `everybody_loops` to preserve items, rustdoc would crash on this file, as it
//didn't see that `SomeStruct` implemented `Clone`
//FIXME(misdreavus): whenever rustdoc shows traits impl'd inside bodies, make sure this test
//reflects that
pub struct Bounded<T: Clone>(T);
// @has traits_in_bodies/struct.SomeStruct.html
// @has - '//code' 'impl Clone for SomeStruct'
pub struct SomeStruct;
fn asdf() -> Bounded<SomeStruct> {
@ -27,3 +26,37 @@ fn asdf() -> Bounded<SomeStruct> {
Bounded(SomeStruct)
}
// @has traits_in_bodies/struct.Point.html
// @has - '//code' 'impl Copy for Point'
#[derive(Clone)]
pub struct Point {
x: i32,
y: i32,
}
const _FOO: () = {
impl Copy for Point {}
()
};
// @has traits_in_bodies/struct.Inception.html
// @has - '//code' 'impl Clone for Inception'
pub struct Inception;
static _BAR: usize = {
trait HiddenTrait {
fn hidden_fn(&self) {
for _ in 0..5 {
impl Clone for Inception {
fn clone(&self) -> Self {
// we need to go deeper
Inception
}
}
}
}
}
5
};