about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-02 19:46:10 +0000
committerbors <bors@rust-lang.org>2021-05-02 19:46:10 +0000
commit6b5de7aaec2955f2c7c7c82521a9186f2ba4f037 (patch)
treed9c6bbabdb3bbac42693c3090826455f487d36c8
parente10cbc33c1fd2fcd446cab5cdcc57332d154721c (diff)
parent0d52eb9de81a5fa8989edfd3db8fcc82c49c29fc (diff)
downloadrust-6b5de7aaec2955f2c7c7c82521a9186f2ba4f037.tar.gz
rust-6b5de7aaec2955f2c7c7c82521a9186f2ba4f037.zip
Auto merge of #84754 - GuillaumeGomez:toggle-migration, r=jsha
Migrate trait and impl blocks' toggles into

Part of #83332

After this, I think only the "global" doc comment will be used as JS toggle. Once this PR is merged, I check what remains and remove them.

There is one change that this PR brings:

![Screenshot from 2021-04-30 15-39-04](https://user-images.githubusercontent.com/3050060/116713412-0f9ce200-a9d5-11eb-979c-2e7a73d16706.png)
![Screenshot from 2021-04-30 15-39-07](https://user-images.githubusercontent.com/3050060/116713415-10357880-a9d5-11eb-9868-1ba9e5ebf65e.png)

As you can see, I had to move the "undocumented" items below, they're not mixed with the others anymore. Unfortunately, I don't see a way to keep the current appearance without JS. As a a reminder, currently it looks like this:

![Screenshot from 2021-04-30 15-39-12](https://user-images.githubusercontent.com/3050060/116713547-31966480-a9d5-11eb-90bb-686042eefeec.png)
![Screenshot from 2021-04-30 15-39-15](https://user-images.githubusercontent.com/3050060/116713549-322efb00-a9d5-11eb-94a9-cfea073120db.png)

r? `@jsha`
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/librustdoc/html/render/mod.rs187
-rw-r--r--src/librustdoc/html/static/main.js97
-rw-r--r--src/librustdoc/html/static/rustdoc.css49
-rw-r--r--src/librustdoc/html/static/themes/ayu.css3
-rw-r--r--src/librustdoc/html/static/themes/dark.css3
-rw-r--r--src/librustdoc/html/static/themes/light.css3
-rw-r--r--src/test/rustdoc/assoc-consts.rs6
-rw-r--r--src/test/rustdoc/inline_cross/assoc-items.rs9
-rw-r--r--src/test/rustdoc/inline_cross/impl-inline-without-trait.rs3
-rw-r--r--src/test/rustdoc/manual_impl.rs23
-rw-r--r--src/test/rustdoc/trait-impl.rs31
12 files changed, 186 insertions, 232 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 33cb11e539b..a556fdba2f3 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -678,10 +678,6 @@ impl ItemKind {
             | KeywordItem(_) => [].iter(),
         }
     }
-
-    crate fn is_type_alias(&self) -> bool {
-        matches!(self, ItemKind::TypedefItem(..) | ItemKind::AssocTypeItem(..))
-    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 0a8026ef942..811c4233386 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -508,23 +508,16 @@ fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(w, cx, item, false, parent);
-    document_full(w, item, cx, false);
+    document_item_info(w, cx, item, parent);
+    document_full(w, item, cx);
 }
 
 /// Render md_text as markdown.
-fn render_markdown(
-    w: &mut Buffer,
-    cx: &Context<'_>,
-    md_text: &str,
-    links: Vec<RenderedLink>,
-    is_hidden: bool,
-) {
+fn render_markdown(w: &mut Buffer, cx: &Context<'_>, md_text: &str, links: Vec<RenderedLink>) {
     let mut ids = cx.id_map.borrow_mut();
     write!(
         w,
-        "<div class=\"docblock{}\">{}</div>",
-        if is_hidden { " hidden" } else { "" },
+        "<div class=\"docblock\">{}</div>",
         Markdown(
             md_text,
             &links,
@@ -544,11 +537,10 @@ fn document_short(
     item: &clean::Item,
     cx: &Context<'_>,
     link: AssocItemLink<'_>,
-    is_hidden: bool,
     parent: &clean::Item,
     show_def_docs: bool,
 ) {
-    document_item_info(w, cx, item, is_hidden, Some(parent));
+    document_item_info(w, cx, item, Some(parent));
     if !show_def_docs {
         return;
     }
@@ -565,19 +557,14 @@ fn document_short(
             }
         }
 
-        write!(
-            w,
-            "<div class='docblock{}'>{}</div>",
-            if is_hidden { " hidden" } else { "" },
-            summary_html,
-        );
+        write!(w, "<div class='docblock'>{}</div>", summary_html,);
     }
 }
 
-fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>, is_hidden: bool) {
+fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>) {
     if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) {
         debug!("Doc block: =====\n{}\n=====", s);
-        render_markdown(w, cx, &s, item.links(cx), is_hidden);
+        render_markdown(w, cx, &s, item.links(cx));
     }
 }
 
@@ -590,16 +577,11 @@ fn document_item_info(
     w: &mut Buffer,
     cx: &Context<'_>,
     item: &clean::Item,
-    is_hidden: bool,
     parent: Option<&clean::Item>,
 ) {
     let item_infos = short_item_info(item, cx, parent);
     if !item_infos.is_empty() {
-        if is_hidden {
-            w.write_str("<div class=\"item-info hidden\">");
-        } else {
-            w.write_str("<div class=\"item-info\">");
-        }
+        w.write_str("<div class=\"item-info\">");
         for info in item_infos {
             w.write_str(&info);
         }
@@ -1282,8 +1264,12 @@ fn render_impl(
     let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]);
     let mut close_tags = String::new();
 
+    // For trait implementations, the `interesting` output contains all methods that have doc
+    // comments, and the `boring` output contains all methods that do not. The distinction is
+    // used to allow hiding the boring methods.
     fn doc_impl_item(
-        w: &mut Buffer,
+        boring: &mut Buffer,
+        interesting: &mut Buffer,
         cx: &Context<'_>,
         item: &clean::Item,
         parent: &clean::Item,
@@ -1306,15 +1292,46 @@ fn render_impl(
             }
         };
 
-        let (is_hidden, extra_class) =
-            if (trait_.is_none() || item.doc_value().is_some() || item.kind.is_type_alias())
-                && !is_default_item
-            {
-                (false, "")
-            } else {
-                (true, " hidden")
-            };
         let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
+
+        let mut doc_buffer = Buffer::empty_from(boring);
+        let mut info_buffer = Buffer::empty_from(boring);
+        let mut short_documented = true;
+
+        if render_method_item {
+            if !is_default_item {
+                if let Some(t) = trait_ {
+                    // The trait item may have been stripped so we might not
+                    // find any documentation or stability for it.
+                    if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
+                        // We need the stability of the item from the trait
+                        // because impls can't have a stability.
+                        if item.doc_value().is_some() {
+                            document_item_info(&mut info_buffer, cx, it, Some(parent));
+                            document_full(&mut doc_buffer, item, cx);
+                            short_documented = false;
+                        } else {
+                            // In case the item isn't documented,
+                            // provide short documentation from the trait.
+                            document_short(&mut doc_buffer, it, cx, link, parent, show_def_docs);
+                        }
+                    }
+                } else {
+                    document_item_info(&mut info_buffer, cx, item, Some(parent));
+                    if show_def_docs {
+                        document_full(&mut doc_buffer, item, cx);
+                        short_documented = false;
+                    }
+                }
+            } else {
+                document_short(&mut doc_buffer, item, cx, link, parent, show_def_docs);
+            }
+        }
+        let w = if short_documented && trait_.is_some() { interesting } else { boring };
+
+        if !doc_buffer.is_empty() {
+            w.write_str("<details class=\"rustdoc-toggle\" open><summary>");
+        }
         match *item.kind {
             clean::MethodItem(..) | clean::TyMethodItem(_) => {
                 // Only render when the method is not static or we allow static methods
@@ -1327,11 +1344,7 @@ fn render_impl(
                             })
                         })
                         .map(|item| format!("{}.{}", item.type_(), name));
-                    write!(
-                        w,
-                        "<h4 id=\"{}\" class=\"{}{}{}\">",
-                        id, item_type, extra_class, in_trait_class,
-                    );
+                    write!(w, "<h4 id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
                     w.write_str("<code>");
                     render_assoc_item(
                         w,
@@ -1356,11 +1369,7 @@ fn render_impl(
             clean::TypedefItem(ref tydef, _) => {
                 let source_id = format!("{}.{}", ItemType::AssocType, name);
                 let id = cx.derive_id(source_id.clone());
-                write!(
-                    w,
-                    "<h4 id=\"{}\" class=\"{}{}{}\"><code>",
-                    id, item_type, extra_class, in_trait_class
-                );
+                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
                 assoc_type(
                     w,
                     item,
@@ -1377,11 +1386,7 @@ fn render_impl(
             clean::AssocConstItem(ref ty, ref default) => {
                 let source_id = format!("{}.{}", item_type, name);
                 let id = cx.derive_id(source_id.clone());
-                write!(
-                    w,
-                    "<h4 id=\"{}\" class=\"{}{}{}\"><code>",
-                    id, item_type, extra_class, in_trait_class
-                );
+                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
                 assoc_const(
                     w,
                     item,
@@ -1406,11 +1411,7 @@ fn render_impl(
             clean::AssocTypeItem(ref bounds, ref default) => {
                 let source_id = format!("{}.{}", item_type, name);
                 let id = cx.derive_id(source_id.clone());
-                write!(
-                    w,
-                    "<h4 id=\"{}\" class=\"{}{}{}\"><code>",
-                    id, item_type, extra_class, in_trait_class
-                );
+                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, in_trait_class);
                 assoc_type(
                     w,
                     item,
@@ -1428,38 +1429,20 @@ fn render_impl(
             _ => panic!("can't make docs for trait item with name {:?}", item.name),
         }
 
-        if render_method_item {
-            if !is_default_item {
-                if let Some(t) = trait_ {
-                    // The trait item may have been stripped so we might not
-                    // find any documentation or stability for it.
-                    if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
-                        // We need the stability of the item from the trait
-                        // because impls can't have a stability.
-                        if item.doc_value().is_some() {
-                            document_item_info(w, cx, it, is_hidden, Some(parent));
-                            document_full(w, item, cx, is_hidden);
-                        } else {
-                            // In case the item isn't documented,
-                            // provide short documentation from the trait.
-                            document_short(w, it, cx, link, is_hidden, parent, show_def_docs);
-                        }
-                    }
-                } else {
-                    document_item_info(w, cx, item, is_hidden, Some(parent));
-                    if show_def_docs {
-                        document_full(w, item, cx, is_hidden);
-                    }
-                }
-            } else {
-                document_short(w, item, cx, link, is_hidden, parent, show_def_docs);
-            }
+        w.push_buffer(info_buffer);
+        if !doc_buffer.is_empty() {
+            w.write_str("</summary>");
+            w.push_buffer(doc_buffer);
+            w.push_str("</details>");
         }
     }
 
     let mut impl_items = Buffer::empty_from(w);
+    let mut default_impl_items = Buffer::empty_from(w);
+
     for trait_item in &i.inner_impl().items {
         doc_impl_item(
+            &mut default_impl_items,
             &mut impl_items,
             cx,
             trait_item,
@@ -1475,7 +1458,8 @@ fn render_impl(
     }
 
     fn render_default_items(
-        w: &mut Buffer,
+        boring: &mut Buffer,
+        interesting: &mut Buffer,
         cx: &Context<'_>,
         t: &clean::Trait,
         i: &clean::Impl,
@@ -1495,7 +1479,8 @@ fn render_impl(
             let assoc_link = AssocItemLink::GotoSource(did, &provided_methods);
 
             doc_impl_item(
-                w,
+                boring,
+                interesting,
                 cx,
                 trait_item,
                 parent,
@@ -1517,6 +1502,7 @@ fn render_impl(
     if show_default_items {
         if let Some(t) = trait_ {
             render_default_items(
+                &mut default_impl_items,
                 &mut impl_items,
                 cx,
                 &t.trait_,
@@ -1529,10 +1515,14 @@ fn render_impl(
             );
         }
     }
-    let details_str = if impl_items.is_empty() {
-        ""
-    } else {
-        "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>"
+    let toggled = !impl_items.is_empty() || !default_impl_items.is_empty();
+    let open_details = |close_tags: &mut String| {
+        if toggled {
+            close_tags.insert_str(0, "</details>");
+            "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>"
+        } else {
+            ""
+        }
     };
     if render_mode == RenderMode::Normal {
         let id = cx.derive_id(match i.inner_impl().trait_ {
@@ -1554,11 +1544,10 @@ fn render_impl(
             write!(
                 w,
                 "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">",
-                details_str, id, aliases
+                open_details(&mut close_tags),
+                id,
+                aliases
             );
-            if !impl_items.is_empty() {
-                close_tags.insert_str(0, "</details>");
-            }
             write!(w, "{}", i.inner_impl().print(use_absolute, cx));
             if show_def_docs {
                 for it in &i.inner_impl().items {
@@ -1582,14 +1571,11 @@ fn render_impl(
             write!(
                 w,
                 "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
-                details_str,
+                open_details(&mut close_tags),
                 id,
                 aliases,
                 i.inner_impl().print(false, cx)
             );
-            if !impl_items.is_empty() {
-                close_tags.insert_str(0, "</details>");
-            }
         }
         write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
         render_stability_since_raw(
@@ -1600,7 +1586,7 @@ fn render_impl(
             outer_const_version,
         );
         write_srclink(cx, &i.impl_item, w);
-        if impl_items.is_empty() {
+        if !toggled {
             w.write_str("</h3>");
         } else {
             w.write_str("</h3></summary>");
@@ -1629,8 +1615,13 @@ fn render_impl(
             );
         }
     }
-    if !impl_items.is_empty() {
+    if toggled {
         w.write_str("<div class=\"impl-items\">");
+        w.push_buffer(default_impl_items);
+        if trait_.is_some() && !impl_items.is_empty() {
+            w.write_str("<details class=\"undocumented\"><summary></summary>");
+            close_tags.insert_str(0, "</details>");
+        }
         w.push_buffer(impl_items);
         close_tags.insert_str(0, "</div>");
     }
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index e81eaca8f0e..1bc53625924 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1156,8 +1156,6 @@ function hideThemeButtonState() {
         var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
         var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false";
         var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
-        var hideTraitImplementations =
-            getSettingValue("auto-hide-trait-implementations") !== "false";
 
         var impl_list = document.getElementById("trait-implementations-list");
         if (impl_list !== null) {
@@ -1173,39 +1171,18 @@ function hideThemeButtonState() {
             });
         }
 
-        var func = function(e) {
-            var next = e.nextElementSibling;
-            if (next && hasClass(next, "item-info")) {
-              next = next.nextElementSibling;
-            }
-            if (!next) {
-                return;
-            }
-            if (hasClass(next, "docblock")) {
-                var newToggle = toggle.cloneNode(true);
-                insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]);
-                if (hideMethodDocs === true && hasClass(e, "method") === true) {
-                    collapseDocs(newToggle, "hide");
+        if (hideMethodDocs === true) {
+            onEachLazy(document.getElementsByClassName("method"), function(e) {
+                var toggle = e.parentNode;
+                if (toggle) {
+                    toggle = toggle.parentNode;
                 }
-            }
-        };
-
-        var funcImpl = function(e) {
-            var next = e.nextElementSibling;
-            if (next && hasClass(next, "item-info")) {
-                next = next.nextElementSibling;
-            }
-            if (next && hasClass(next, "docblock")) {
-                next = next.nextElementSibling;
-            }
-            if (!next) {
-                return;
-            }
-        };
+                if (toggle && toggle.tagName === "DETAILS") {
+                    toggle.open = false;
+                }
+            });
+        }
 
-        onEachLazy(document.getElementsByClassName("method"), func);
-        onEachLazy(document.getElementsByClassName("associatedconstant"), func);
-        var impl_call = function() {};
         onEachLazy(document.getElementsByTagName("details"), function (e) {
             var showLargeItem = !hideLargeItemContents && hasClass(e, "type-contents-toggle");
             var showImplementor = !hideImplementors && hasClass(e, "implementors-toggle");
@@ -1213,60 +1190,6 @@ function hideThemeButtonState() {
                 e.open = true;
             }
         });
-        if (hideMethodDocs === true) {
-            impl_call = function(e, newToggle) {
-                if (e.id.match(/^impl(?:-\d+)?$/) === null) {
-                    // Automatically minimize all non-inherent impls
-                    if (hasClass(e, "impl") === true) {
-                        collapseDocs(newToggle, "hide");
-                    }
-                }
-            };
-        }
-        var newToggle = document.createElement("a");
-        newToggle.href = "javascript:void(0)";
-        newToggle.className = "collapse-toggle hidden-default collapsed";
-        newToggle.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(true) +
-                              "</span>] Show hidden undocumented items";
-        function toggleClicked() {
-            if (hasClass(this, "collapsed")) {
-                removeClass(this, "collapsed");
-                onEachLazy(this.parentNode.getElementsByClassName("hidden"), function(x) {
-                    if (hasClass(x, "content") === false) {
-                        removeClass(x, "hidden");
-                        addClass(x, "x");
-                    }
-                }, true);
-                this.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(false) +
-                                 "</span>] Hide undocumented items";
-            } else {
-                addClass(this, "collapsed");
-                onEachLazy(this.parentNode.getElementsByClassName("x"), function(x) {
-                    if (hasClass(x, "content") === false) {
-                        addClass(x, "hidden");
-                        removeClass(x, "x");
-                    }
-                }, true);
-                this.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(true) +
-                                 "</span>] Show hidden undocumented items";
-            }
-        }
-        onEachLazy(document.getElementsByClassName("impl-items"), function(e) {
-            onEachLazy(e.getElementsByClassName("associatedconstant"), func);
-            // We transform the DOM iterator into a vec of DOM elements to prevent performance
-            // issues on webkit browsers.
-            var hiddenElems = Array.prototype.slice.call(e.getElementsByClassName("hidden"));
-            var needToggle = hiddenElems.some(function(hiddenElem) {
-                return hasClass(hiddenElem, "content") === false &&
-                    hasClass(hiddenElem, "docblock") === false;
-            });
-            if (needToggle === true) {
-                var inner_toggle = newToggle.cloneNode(true);
-                inner_toggle.onclick = toggleClicked;
-                e.insertBefore(inner_toggle, e.firstChild);
-                impl_call(e.previousSibling, inner_toggle);
-            }
-        });
 
         var currentType = document.getElementsByClassName("type-decl")[0];
         var className = null;
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index a95c90e999f..d3fe59e8d0b 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -110,7 +110,7 @@ h3 {
 	font-size: 1.3em;
 }
 h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.notable),
-h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
+h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant):not(.associatedtype) {
 	font-weight: 500;
 	margin: 20px 0 15px 0;
 	padding-bottom: 6px;
@@ -128,10 +128,10 @@ h1.fqn > .in-band > a:hover {
 	text-decoration: underline;
 }
 h2, h3:not(.impl):not(.method):not(.type):not(.tymethod),
-h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
+h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant):not(.associatedtype) {
 	border-bottom: 1px solid;
 }
-h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant {
+h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant, h4.associatedtype {
 	flex-basis: 100%;
 	font-weight: 600;
 	margin-top: 16px;
@@ -139,7 +139,7 @@ h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant {
 	position: relative;
 }
 h3.impl, h3.method, h4.method.trait-impl, h3.type,
-h4.type.trait-impl, h4.associatedconstant.trait-impl {
+h4.type.trait-impl, h4.associatedconstant.trait-impl, h4.associatedtype.trait-impl {
 	padding-left: 15px;
 }
 
@@ -147,6 +147,9 @@ h1, h2, h3, h4,
 .sidebar, a.source, .search-input, .content table td:first-child > a,
 .collapse-toggle, div.item-list .out-of-band,
 #source-sidebar, #sidebar-toggle,
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before,
+.content ul.crate a.crate,
 /* This selector is for the items listed in the "all items" page. */
 #main > ul.docblock > li > a {
 	font-family: "Fira Sans", Arial, sans-serif;
@@ -154,7 +157,6 @@ h1, h2, h3, h4,
 
 .content ul.crate a.crate {
 	font-size: 16px/1.6;
-	font-family: "Fira Sans", Arial, sans-serif;
 }
 
 ol, ul {
@@ -596,7 +598,10 @@ h4 > code, h3 > code, .invisible > code {
 	left: -19px;
 }
 
-.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant {
+.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant,
+.impl-items > .associatedtype, .content .impl-items details > summary > .type,
+.impl-items details > summary > .associatedconstant,
+.impl-items details > summary > .associatedtype {
 	margin-left: 20px;
 }
 
@@ -656,7 +661,8 @@ a {
 }
 
 .in-band:hover > .anchor, .impl:hover > .anchor, .method.trait-impl:hover > .anchor,
-.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor {
+.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor,
+.associatedtype.trait-impl:hover > .anchor {
 	display: inline-block;
 	position: absolute;
 }
@@ -1466,7 +1472,8 @@ h4 > .notable-traits {
 		margin-left: 0;
 	}
 
-	.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant {
+	.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant,
+	.impl-items > .associatedtype {
 		display: flex;
 	}
 
@@ -1764,7 +1771,10 @@ details.rustdoc-toggle > summary.hideme {
 	cursor: pointer;
 }
 
-details.rustdoc-toggle > summary::-webkit-details-marker {
+details.rustdoc-toggle > summary::-webkit-details-marker,
+details.rustdoc-toggle > summary::marker,
+details.undocumented > summary::-webkit-details-marker,
+details.undocumented > summary::marker {
 	display: none;
 }
 
@@ -1787,6 +1797,14 @@ details.rustdoc-toggle > summary.hideme::before {
 details.rustdoc-toggle > summary:not(.hideme)::before {
 	position: absolute;
 	left: -23px;
+	top: initial;
+}
+
+.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before,
+.undocumented > details.rustdoc-toggle > summary:not(.hideme)::before {
+	position: absolute;
+	top: 3px;
+	left: -2px;
 }
 
 /* When a "hideme" summary is open and the "Expand description" or "Show
@@ -1798,7 +1816,7 @@ details.rustdoc-toggle[open] > summary.hideme {
 	position: absolute;
 }
 
-details.rustdoc-toggle[open] {
+details.rustdoc-toggle, details.undocumented {
 	position: relative;
 }
 
@@ -1810,3 +1828,14 @@ details.rustdoc-toggle[open] > summary::before {
 	content: "[−]";
 	display: inline;
 }
+
+details.undocumented > summary::before {
+	content: "[+] Show hidden undocumented items";
+	cursor: pointer;
+	font-size: 16px;
+	font-weight: 300;
+}
+
+details.undocumented[open] > summary::before {
+	content: "[-] Hide undocumented items";
+}
diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css
index 72396ec6b76..aace0b3c037 100644
--- a/src/librustdoc/html/static/themes/ayu.css
+++ b/src/librustdoc/html/static/themes/ayu.css
@@ -226,7 +226,8 @@ a {
 
 .collapse-toggle,
 details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before {
 	color: #999;
 }
 
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index b2003b52741..c23e95ce107 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -188,7 +188,8 @@ a.test-arrow {
 
 .collapse-toggle,
 details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before {
 	color: #999;
 }
 
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 04187773b64..93309721210 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -186,7 +186,8 @@ a.test-arrow {
 
 .collapse-toggle,
 details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
+details.rustdoc-toggle > summary::before,
+details.undocumented > summary::before {
 	color: #999;
 }
 
diff --git a/src/test/rustdoc/assoc-consts.rs b/src/test/rustdoc/assoc-consts.rs
index bb6af7995a0..7bfa922185b 100644
--- a/src/test/rustdoc/assoc-consts.rs
+++ b/src/test/rustdoc/assoc-consts.rs
@@ -88,12 +88,14 @@ impl Qux for Bar {
     /// Docs for QUX1 in impl.
     const QUX1: i8 = 5;
     // @has - '//*[@id="associatedconstant.QUX_DEFAULT0"]' 'const QUX_DEFAULT0: u16'
-    // @has - '//*[@class="docblock hidden"]' "Docs for QUX_DEFAULT12 in trait."
+    // @!has - '//div[@class="impl-items"]/details[@open=""]//*[@class="docblock"]' "Docs for QUX_DEFAULT12 in trait."
+    // @has - '//div[@class="impl-items"]/details//*[@class="docblock"]' "Docs for QUX_DEFAULT12 in trait."
     const QUX_DEFAULT0: u16 = 6;
     // @has - '//*[@id="associatedconstant.QUX_DEFAULT1"]' 'const QUX_DEFAULT1: i16'
     // @has - '//*[@class="docblock"]' "Docs for QUX_DEFAULT1 in impl."
     /// Docs for QUX_DEFAULT1 in impl.
     const QUX_DEFAULT1: i16 = 7;
     // @has - '//*[@id="associatedconstant.QUX_DEFAULT2"]' 'const QUX_DEFAULT2: u32'
-    // @has - '//*[@class="docblock hidden"]' "Docs for QUX_DEFAULT2 in trait."
+    // @!has - '//div[@class="impl-items"]/details[@open=""]//*[@class="docblock"]' "Docs for QUX_DEFAULT2 in trait."
+    // @has - '//div[@class="impl-items"]/details//*[@class="docblock"]' "Docs for QUX_DEFAULT2 in trait."
 }
diff --git a/src/test/rustdoc/inline_cross/assoc-items.rs b/src/test/rustdoc/inline_cross/assoc-items.rs
index 7eb3e43cb11..8fc01c3f04c 100644
--- a/src/test/rustdoc/inline_cross/assoc-items.rs
+++ b/src/test/rustdoc/inline_cross/assoc-items.rs
@@ -16,15 +16,18 @@ extern crate assoc_items;
 // @has - '//*[@id="associatedconstant.ConstNoDefault"]' 'const ConstNoDefault: i16'
 // @has - '//*[@class="docblock"]' 'dox for ConstNoDefault'
 // @has - '//*[@id="associatedconstant.ConstWithDefault"]' 'const ConstWithDefault: u16'
-// @has - '//*[@class="docblock hidden"]' 'docs for ConstWithDefault'
+// @!has - '//details[@open=""]/details/div[@class="docblock"]' 'docs for ConstWithDefault'
+// @has - '//details/details/div[@class="docblock"]' 'docs for ConstWithDefault'
 // @has - '//*[@id="associatedtype.TypeNoDefault"]' 'type TypeNoDefault = i32'
 // @has - '//*[@class="docblock"]' 'dox for TypeNoDefault'
 // @has - '//*[@id="associatedtype.TypeWithDefault"]' 'type TypeWithDefault = u32'
-// @has - '//*[@class="docblock hidden"]' 'docs for TypeWithDefault'
+// @!has - '//details[@open=""]/details/div[@class="docblock"]' 'docs for TypeWithDefault'
+// @has - '//details/details/div[@class="docblock"]' 'docs for TypeWithDefault'
 // @has - '//*[@id="method.method_no_default"]' 'fn method_no_default()'
 // @has - '//*[@class="docblock"]' 'dox for method_no_default'
 // @has - '//*[@id="method.method_with_default"]' 'fn method_with_default()'
-// @has - '//*[@class="docblock hidden"]' 'docs for method_with_default'
+// @!has - '//details[@open=""]/details/div[@class="docblock"]' 'docs for method_with_default'
+// @has - '//details/details/div[@class="docblock"]' 'docs for method_with_default'
 pub use assoc_items::MyStruct;
 
 // @has foo/trait.MyTrait.html
diff --git a/src/test/rustdoc/inline_cross/impl-inline-without-trait.rs b/src/test/rustdoc/inline_cross/impl-inline-without-trait.rs
index 4591bb526ae..cc0596c70ce 100644
--- a/src/test/rustdoc/inline_cross/impl-inline-without-trait.rs
+++ b/src/test/rustdoc/inline_cross/impl-inline-without-trait.rs
@@ -8,5 +8,6 @@ extern crate impl_inline_without_trait;
 
 // @has 'foo/struct.MyStruct.html'
 // @has - '//*[@id="method.my_trait_method"]' 'fn my_trait_method()'
-// @has - '//*[@class="docblock hidden"]' 'docs for my_trait_method'
+// @!has - '//details[@open=""]/details/div[@class="docblock"]' 'docs for my_trait_method'
+// @has - '//details/details/div[@class="docblock"]' 'docs for my_trait_method'
 pub use impl_inline_without_trait::MyStruct;
diff --git a/src/test/rustdoc/manual_impl.rs b/src/test/rustdoc/manual_impl.rs
index 11ddab5f7ff..776a191ceef 100644
--- a/src/test/rustdoc/manual_impl.rs
+++ b/src/test/rustdoc/manual_impl.rs
@@ -24,10 +24,13 @@ pub trait T {
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S1 trait implementation.'
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S1 trait a_method implementation.'
 // @!has - '//*[@class="docblock"]' 'Docs associated with the trait a_method definition.'
-// @has - '//*[@class="docblock hidden"]' 'Docs associated with the trait b_method definition.'
-// @has - '//*[@class="docblock hidden"]' 'Docs associated with the trait c_method definition.'
+// @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Docs associated with the trait b_method definition.'
+// @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Docs associated with the trait b_method definition.'
+// @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Docs associated with the trait c_method definition.'
+// @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Docs associated with the trait c_method definition.'
 // @!has - '//*[@class="docblock"]' 'There is another line'
-// @has - '//*[@class="docblock hidden"]' 'Read more'
+// @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Read more'
+// @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Read more'
 pub struct S1(usize);
 
 /// Docs associated with the S1 trait implementation.
@@ -42,9 +45,10 @@ impl T for S1 {
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S2 trait implementation.'
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S2 trait a_method implementation.'
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S2 trait c_method implementation.'
-// @!has - '//*[@class="docblock"]' 'Docs associated with the trait a_method definition.'
-// @!has - '//*[@class="docblock"]' 'Docs associated with the trait c_method definition.'
-// @has - '//*[@class="docblock hidden"]' 'Docs associated with the trait b_method definition.'
+// @!has - '//details[open=""]/div[@class="docblock"]' 'Docs associated with the trait a_method definition.'
+// @!has - '//details[open=""]/div[@class="docblock"]' 'Docs associated with the trait c_method definition.'
+// @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Docs associated with the trait b_method definition.'
+// @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Docs associated with the trait b_method definition.'
 pub struct S2(usize);
 
 /// Docs associated with the S2 trait implementation.
@@ -61,9 +65,10 @@ impl T for S2 {
 }
 
 // @has manual_impl/struct.S3.html '//*[@class="trait"]' 'T'
-// @has  - '//*[@class="docblock"]' 'Docs associated with the S3 trait implementation.'
-// @has  - '//*[@class="docblock"]' 'Docs associated with the S3 trait b_method implementation.'
-// @has - '//*[@class="docblock hidden"]' 'Docs associated with the trait a_method definition.'
+// @has  - '//details[@open=""]/div[@class="docblock"]' 'Docs associated with the S3 trait implementation.'
+// @has  - '//details[@open=""]/div[@class="docblock"]' 'Docs associated with the S3 trait b_method implementation.'
+// @!has - '//div[@class="impl-items"]/details[@open=""]//div[@class="docblock"]' 'Docs associated with the trait a_method definition.'
+// @has - '//div[@class="impl-items"]/details//div[@class="docblock"]' 'Docs associated with the trait a_method definition.'
 pub struct S3(usize);
 
 /// Docs associated with the S3 trait implementation.
diff --git a/src/test/rustdoc/trait-impl.rs b/src/test/rustdoc/trait-impl.rs
index 3bcaa3bb673..931691db3e6 100644
--- a/src/test/rustdoc/trait-impl.rs
+++ b/src/test/rustdoc/trait-impl.rs
@@ -21,26 +21,27 @@ pub trait Trait {
 pub struct Struct;
 
 impl Trait for Struct {
-    // @has trait_impl/struct.Struct.html '//*[@id="method.a"]/../div/p' 'Some long docs'
-    // @!has - '//*[@id="method.a"]/../div/p' 'link will be added'
-    // @has - '//*[@id="method.a"]/../div/p/a' 'Read more'
-    // @has - '//*[@id="method.a"]/../div/p/a/@href' 'trait.Trait.html'
+    // @has trait_impl/struct.Struct.html '//*[@id="method.a"]/../../div[@class="docblock"]/p' 'Some long docs'
+    // @!has - '//*[@id="method.a"]/../../div[@class="docblock"]/p' 'link will be added'
+    // @has - '//*[@id="method.a"]/../../div[@class="docblock"]/p/a' 'Read more'
+    // @has - '//*[@id="method.a"]/../../div[@class="docblock"]/p/a/@href' 'trait.Trait.html#tymethod.a'
     fn a() {}
 
-    // @has trait_impl/struct.Struct.html '//*[@id="method.b"]/../div/p' 'These docs contain'
-    // @has - '//*[@id="method.b"]/../div/p/a' 'reference link'
-    // @has - '//*[@id="method.b"]/../div/p/a/@href' 'https://example.com'
-    // @has - '//*[@id="method.b"]/../div/p/a' 'Read more'
-    // @has - '//*[@id="method.b"]/../div/p/a/@href' 'trait.Trait.html'
+    // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/p' 'These docs contain'
+    // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/p/a' 'reference link'
+    // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/p/a/@href' 'https://example.com'
+    // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/p/a' 'Read more'
+    // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/p/a/@href' 'trait.Trait.html#tymethod.b'
     fn b() {}
 
-    // @!has trait_impl/struct.Struct.html '//*[@id="method.c"]/../div/p' 'code block'
-    // @has - '//*[@id="method.c"]/../div/p/a' 'Read more'
-    // @has - '//*[@id="method.c"]/../div/p/a/@href' 'trait.Trait.html'
+    // @!has - '//*[@id="method.c"]/../../div[@class="docblock"]/p' 'code block'
+    // @has - '//*[@id="method.c"]/../../div[@class="docblock"]/a' 'Read more'
+    // @has - '//*[@id="method.c"]/../../div[@class="docblock"]/a/@href' 'trait.Trait.html#tymethod.c'
     fn c() {}
 
-    // @has trait_impl/struct.Struct.html '//*[@id="method.d"]/../div/p' \
-    //   'Escaped formatting a*b*c* works'
-    // @!has trait_impl/struct.Struct.html '//*[@id="method.d"]/../div/p/em'
+    // @has - '//*[@id="method.d"]/../../div[@class="docblock"]/p' 'Escaped formatting a*b*c* works'
+    // @!has - '//*[@id="method.d"]/../../div[@class="docblock"]/p/em'
     fn d() {}
+
+    // @has - '//*[@id="impl-Trait"]/code/a/@href' 'trait.Trait.html'
 }