rustdoc: collect trait impls as an early pass
This commit is contained in:
parent
992d1e4d3d
commit
02bea3c581
6 changed files with 137 additions and 82 deletions
|
@ -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,9 +306,12 @@ 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.access_levels.borrow().is_doc_reachable(traitref.def_id) {
|
||||
debug!("trait {:?} not reachable, bailing: {:?}", traitref.def_id, did);
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,9 +319,12 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
|
|||
|
||||
// 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.access_levels.borrow().is_doc_reachable(did) {
|
||||
debug!("impl type {:?} not accessible, bailing", did);
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
99
src/librustdoc/passes/collect_trait_impls.rs
Normal file
99
src/librustdoc/passes/collect_trait_impls.rs
Normal 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
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {}
|
||||
()
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue