1
Fork 0

rustdoc: collect trait impls as an early pass

This commit is contained in:
QuietMisdreavus 2018-08-07 10:10:05 -05:00
parent 992d1e4d3d
commit 02bea3c581
6 changed files with 137 additions and 82 deletions

View file

@ -291,78 +291,12 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean:
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
}
pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
if !cx.renderinfo.borrow_mut().inlined.insert(did) {
debug!("already inlined, bailing: {:?}", did);
return
}
@ -372,21 +306,27 @@ 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 !did.is_local() {
if let Some(traitref) = associated_trait {
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
debug!("trait {:?} not reachable, bailing: {:?}", traitref.def_id, did);
return
}
}
}
let for_ = tcx.type_of(did).clean(cx);
// Only inline impl if the implementing type is
// reachable in rustdoc generated documentation
if !did.is_local() {
if let Some(did) = for_.def_id() {
if !cx.access_levels.borrow().is_doc_reachable(did) {
debug!("impl type {:?} not accessible, bailing", did);
return
}
}
}
let predicates = tcx.predicates_of(did);
let trait_items = tcx.associated_items(did).filter_map(|item| {

View file

@ -37,7 +37,7 @@ use syntax_pos::DUMMY_SP;
use errors;
use errors::emitter::{Emitter, EmitterWriter};
use std::cell::{RefCell, Cell};
use std::cell::RefCell;
use std::mem;
use rustc_data_structures::sync::{self, Lrc};
use std::rc::Rc;
@ -60,7 +60,6 @@ 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.
@ -514,7 +513,6 @@ pub fn run_core(search_paths: SearchPaths,
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(),

View file

@ -0,0 +1,99 @@
// 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 super::Pass;
use core::DocContext;
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(mut krate: Crate, cx: &DocContext) -> Crate {
if let Some(ref mut it) = krate.module {
if let ModuleItem(Module { ref mut items, .. }) = it.inner {
for &cnum in cx.tcx.crates().iter() {
for &did in cx.tcx.all_trait_implementations(cnum).iter() {
inline::build_impl(cx, did, items);
}
}
// `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, 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, 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();
items.extend(new_impls);
}
}
} else {
panic!("collect-trait-impls can't run");
}
} else {
panic!("collect-trait-impls can't run");
}
// pulling in the impls puts their trait info into the DocContext, but that's already been
// drained by now, so stuff that info into the Crate so the rendering can pick it up
let mut external_traits = cx.external_traits.borrow_mut();
for (did, trait_) in external_traits.drain() {
krate.external_traits.entry(did).or_insert(trait_);
}
krate
}

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",

View file

@ -510,9 +510,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

@ -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,16 @@ 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 {}
()
};