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));
|
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
|
impls
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
|
pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
|
||||||
if !cx.renderinfo.borrow_mut().inlined.insert(did) {
|
if !cx.renderinfo.borrow_mut().inlined.insert(did) {
|
||||||
|
debug!("already inlined, bailing: {:?}", did);
|
||||||
return
|
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
|
// Only inline impl if the implemented trait is
|
||||||
// reachable in rustdoc generated documentation
|
// reachable in rustdoc generated documentation
|
||||||
|
if !did.is_local() {
|
||||||
if let Some(traitref) = associated_trait {
|
if let Some(traitref) = associated_trait {
|
||||||
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
|
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
|
||||||
|
debug!("trait {:?} not reachable, bailing: {:?}", traitref.def_id, did);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let for_ = tcx.type_of(did).clean(cx);
|
let for_ = tcx.type_of(did).clean(cx);
|
||||||
|
|
||||||
// Only inline impl if the implementing type is
|
// Only inline impl if the implementing type is
|
||||||
// reachable in rustdoc generated documentation
|
// reachable in rustdoc generated documentation
|
||||||
|
if !did.is_local() {
|
||||||
if let Some(did) = for_.def_id() {
|
if let Some(did) = for_.def_id() {
|
||||||
if !cx.access_levels.borrow().is_doc_reachable(did) {
|
if !cx.access_levels.borrow().is_doc_reachable(did) {
|
||||||
|
debug!("impl type {:?} not accessible, bailing", did);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let predicates = tcx.predicates_of(did);
|
let predicates = tcx.predicates_of(did);
|
||||||
let trait_items = tcx.associated_items(did).filter_map(|item| {
|
let trait_items = tcx.associated_items(did).filter_map(|item| {
|
||||||
|
|
|
@ -37,7 +37,7 @@ use syntax_pos::DUMMY_SP;
|
||||||
use errors;
|
use errors;
|
||||||
use errors::emitter::{Emitter, EmitterWriter};
|
use errors::emitter::{Emitter, EmitterWriter};
|
||||||
|
|
||||||
use std::cell::{RefCell, Cell};
|
use std::cell::RefCell;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use rustc_data_structures::sync::{self, Lrc};
|
use rustc_data_structures::sync::{self, Lrc};
|
||||||
use std::rc::Rc;
|
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
|
/// The stack of module NodeIds up till this point
|
||||||
pub crate_name: Option<String>,
|
pub crate_name: Option<String>,
|
||||||
pub cstore: Rc<CStore>,
|
pub cstore: Rc<CStore>,
|
||||||
pub populated_all_crate_impls: Cell<bool>,
|
|
||||||
// Note that external items for which `doc(hidden)` applies to are shown as
|
// 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
|
// non-reachable while local items aren't. This is because we're reusing
|
||||||
// the access levels from crateanalysis.
|
// the access levels from crateanalysis.
|
||||||
|
@ -514,7 +513,6 @@ pub fn run_core(search_paths: SearchPaths,
|
||||||
resolver: &resolver,
|
resolver: &resolver,
|
||||||
crate_name,
|
crate_name,
|
||||||
cstore: cstore.clone(),
|
cstore: cstore.clone(),
|
||||||
populated_all_crate_impls: Cell::new(false),
|
|
||||||
access_levels: RefCell::new(access_levels),
|
access_levels: RefCell::new(access_levels),
|
||||||
external_traits: Default::default(),
|
external_traits: Default::default(),
|
||||||
active_extern_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;
|
mod collect_intra_doc_links;
|
||||||
pub use self::collect_intra_doc_links::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.
|
/// Represents a single pass.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum Pass {
|
pub enum Pass {
|
||||||
|
@ -132,10 +135,12 @@ pub const PASSES: &'static [Pass] = &[
|
||||||
STRIP_PRIV_IMPORTS,
|
STRIP_PRIV_IMPORTS,
|
||||||
PROPAGATE_DOC_CFG,
|
PROPAGATE_DOC_CFG,
|
||||||
COLLECT_INTRA_DOC_LINKS,
|
COLLECT_INTRA_DOC_LINKS,
|
||||||
|
COLLECT_TRAIT_IMPLS,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// The list of passes run by default.
|
/// The list of passes run by default.
|
||||||
pub const DEFAULT_PASSES: &'static [&'static str] = &[
|
pub const DEFAULT_PASSES: &'static [&'static str] = &[
|
||||||
|
"collect-trait-impls",
|
||||||
"strip-hidden",
|
"strip-hidden",
|
||||||
"strip-private",
|
"strip-private",
|
||||||
"collect-intra-doc-links",
|
"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.
|
/// The list of default passes run with `--document-private-items` is passed to rustdoc.
|
||||||
pub const DEFAULT_PRIVATE_PASSES: &'static [&'static str] = &[
|
pub const DEFAULT_PRIVATE_PASSES: &'static [&'static str] = &[
|
||||||
|
"collect-trait-impls",
|
||||||
"strip-priv-imports",
|
"strip-priv-imports",
|
||||||
"collect-intra-doc-links",
|
"collect-intra-doc-links",
|
||||||
"collapse-docs",
|
"collapse-docs",
|
||||||
|
|
|
@ -510,9 +510,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
|
||||||
ref tr,
|
ref tr,
|
||||||
ref ty,
|
ref ty,
|
||||||
ref item_ids) => {
|
ref item_ids) => {
|
||||||
// Don't duplicate impls when inlining, we'll pick them up
|
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
|
||||||
// regardless of where they're located.
|
// them up regardless of where they're located.
|
||||||
if !self.inlining {
|
if !self.inlining && tr.is_none() {
|
||||||
let items = item_ids.iter()
|
let items = item_ids.iter()
|
||||||
.map(|ii| self.cx.tcx.hir.impl_item(ii.id).clone())
|
.map(|ii| self.cx.tcx.hir.impl_item(ii.id).clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
//prior to fixing `everybody_loops` to preserve items, rustdoc would crash on this file, as it
|
//prior to fixing `everybody_loops` to preserve items, rustdoc would crash on this file, as it
|
||||||
//didn't see that `SomeStruct` implemented `Clone`
|
//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);
|
pub struct Bounded<T: Clone>(T);
|
||||||
|
|
||||||
|
// @has traits_in_bodies/struct.SomeStruct.html
|
||||||
|
// @has - '//code' 'impl Clone for SomeStruct'
|
||||||
pub struct SomeStruct;
|
pub struct SomeStruct;
|
||||||
|
|
||||||
fn asdf() -> Bounded<SomeStruct> {
|
fn asdf() -> Bounded<SomeStruct> {
|
||||||
|
@ -27,3 +26,16 @@ fn asdf() -> Bounded<SomeStruct> {
|
||||||
|
|
||||||
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