about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorQuietMisdreavus <grey@quietmisdreavus.net>2018-08-07 10:10:05 -0500
committerQuietMisdreavus <grey@quietmisdreavus.net>2018-09-20 05:42:20 -0500
commit02bea3c581bf7127a5ec77d1d3d7a5a513fcf6c5 (patch)
tree6b1c899db0ea604db26cea4da42be4627db22c1f /src
parent992d1e4d3de364c895963167b70934599574d9a7 (diff)
downloadrust-02bea3c581bf7127a5ec77d1d3d7a5a513fcf6c5.tar.gz
rust-02bea3c581bf7127a5ec77d1d3d7a5a513fcf6c5.zip
rustdoc: collect trait impls as an early pass
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/inline.rs86
-rw-r--r--src/librustdoc/core.rs4
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs99
-rw-r--r--src/librustdoc/passes/mod.rs6
-rw-r--r--src/librustdoc/visit_ast.rs6
-rw-r--r--src/test/rustdoc/traits-in-bodies.rs18
6 files changed, 137 insertions, 82 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 507461f2ea1..684063beac3 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -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
+            }
         }
     }
 
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 2feeecb388f..d7e087fd624 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -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(),
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
new file mode 100644
index 00000000000..0be5ab07dea
--- /dev/null
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -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
+}
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 16251877bb1..09281aa7cfa 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -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",
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 0aaf2d526f9..a1942a966b1 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -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();
diff --git a/src/test/rustdoc/traits-in-bodies.rs b/src/test/rustdoc/traits-in-bodies.rs
index 3acf4af5fd2..26ed5444122 100644
--- a/src/test/rustdoc/traits-in-bodies.rs
+++ b/src/test/rustdoc/traits-in-bodies.rs
@@ -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 {}
+    ()
+};