about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-04-14 12:47:49 +0000
committerbors <bors@rust-lang.org>2021-04-14 12:47:49 +0000
commitb203b0d240b67916cfa77f640aedaf1c87d50f6d (patch)
tree17289f5efab1957fede030d555301997ad8ec1ac
parentd408fdd4a82bc3e7ea61dd81bc9a8781b2bf939d (diff)
parente36ca09101830c9b1968891fc1adfe88654f6a1d (diff)
downloadrust-b203b0d240b67916cfa77f640aedaf1c87d50f6d.tar.gz
rust-b203b0d240b67916cfa77f640aedaf1c87d50f6d.zip
Auto merge of #83068 - mockersf:method-trait-foreign-impl, r=GuillaumeGomez
rustdoc: links from items in a trait impl are inconsistent

Depending on where the struct implementing a trait is coming from, or the current page, the items in a trait impl are not linking to the same thing:

|item| trait page, implementors| trait page, implementations on Foreign Types|struct page, trait implementations|
|-|-|-|-|
|function|             link to current impl|link to first impl in the list|link to trait def
|default function |    not present         |not present                   |link to trait def
|default function with custom impl|link to current impl|link to trait def             |link to trait def
|constant|             link to current impl|link to trait def             |link to trait def
|associated type|      link to current impl|link to trait def             |link to trait def
||*missing link to trait def*|*function link wrong + missing link to current impl*|*missing link to current impl*|

<details>
  <summary>rust code with those cases</summary>

```rust
pub trait MyTrait {
    type Assoc;
    const VALUE: u32;
    fn trait_function(&self);
    fn defaulted(&self) {}
    fn defaulted_override(&self) {}
}

impl MyTrait for String {
    /// will link to trait def
    type Assoc = ();
    /// will link to trait def
    const VALUE: u32 = 5;
    /// will link to first foreign implementor
    fn trait_function(&self) {}
    /// will link to trait def
    fn defaulted_override(&self) {}
}

impl MyTrait for Vec<u8> {
    /// will link to trait def
    type Assoc = ();
    /// will link to trait def
    const VALUE: u32 = 5;
    /// will link to first foreign implementor
    fn trait_function(&self) {}
    /// will link to trait def
    fn defaulted_override(&self) {}
}

impl MyTrait for MyStruct {
    /// in trait page, will link to current impl
    ///
    /// in struct page, will link to trait def
    type Assoc = bool;
    /// in trait page, will link to current impl
    ///
    /// in struct page, will link to trait def
    const VALUE: u32 = 20;
    /// in trait page, will link to current impl
    ///
    /// in struct page, will link to trait def
    fn trait_function(&self) {}
    /// in trait page, will link to current impl
    ///
    /// in struct page, will link to trait def
    fn defaulted_override(&self) {}
}

pub struct MyStruct;
```
</details>

In this PR, I fixed all links to target the trait definition, and added an anchor-link to the current implementation appearing on mouse hover.
-rw-r--r--src/librustdoc/html/render/mod.rs82
-rw-r--r--src/librustdoc/html/static/rustdoc.css6
-rw-r--r--src/test/rustdoc/trait-impl-items-links-and-anchors.rs65
3 files changed, 134 insertions, 19 deletions
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index fbe799e7184..efd453f96b8 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -912,10 +912,9 @@ fn render_assoc_item(
         let cache = cx.cache();
         let tcx = cx.tcx();
         let name = meth.name.as_ref().unwrap();
-        let anchor = format!("#{}.{}", meth.type_(), name);
         let href = match link {
             AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
-            AssocItemLink::Anchor(None) => anchor,
+            AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name),
             AssocItemLink::GotoSource(did, provided_methods) => {
                 // We're creating a link from an impl-item to the corresponding
                 // trait-item and need to map the anchored type accordingly.
@@ -925,7 +924,9 @@ fn render_assoc_item(
                     ItemType::TyMethod
                 };
 
-                href(did, cache).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor)
+                href(did, cache)
+                    .map(|p| format!("{}#{}.{}", p.0, ty, name))
+                    .unwrap_or_else(|| format!("#{}.{}", ty, name))
             }
         };
         let vis = meth.visibility.print_with_space(tcx, meth.def_id, cache).to_string();
@@ -1452,14 +1453,32 @@ fn render_impl(
             } else {
                 (true, " hidden")
             };
+        let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
         match *item.kind {
             clean::MethodItem(..) | clean::TyMethodItem(_) => {
                 // Only render when the method is not static or we allow static methods
                 if render_method_item {
                     let id = cx.derive_id(format!("{}.{}", item_type, name));
-                    write!(w, "<h4 id=\"{}\" class=\"{}{}\">", id, item_type, extra_class);
+                    let source_id = trait_
+                        .and_then(|trait_| {
+                            trait_.items.iter().find(|item| {
+                                item.name.map(|n| n.as_str().eq(&name.as_str())).unwrap_or(false)
+                            })
+                        })
+                        .map(|item| format!("{}.{}", item.type_(), name));
+                    write!(
+                        w,
+                        "<h4 id=\"{}\" class=\"{}{}{}\">",
+                        id, item_type, extra_class, in_trait_class,
+                    );
                     w.write_str("<code>");
-                    render_assoc_item(w, item, link.anchor(&id), ItemType::Impl, cx);
+                    render_assoc_item(
+                        w,
+                        item,
+                        link.anchor(source_id.as_ref().unwrap_or(&id)),
+                        ItemType::Impl,
+                        cx,
+                    );
                     w.write_str("</code>");
                     render_stability_since_raw(
                         w,
@@ -1468,29 +1487,50 @@ fn render_impl(
                         outer_version,
                         outer_const_version,
                     );
+                    write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
                     write_srclink(cx, item, w);
                     w.write_str("</h4>");
                 }
             }
             clean::TypedefItem(ref tydef, _) => {
-                let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name));
-                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class);
+                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
+                );
                 assoc_type(
                     w,
                     item,
                     &Vec::new(),
                     Some(&tydef.type_),
-                    link.anchor(&id),
+                    link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     "",
                     cx.cache(),
                     tcx,
                 );
-                w.write_str("</code></h4>");
+                w.write_str("</code>");
+                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                w.write_str("</h4>");
             }
             clean::AssocConstItem(ref ty, ref default) => {
-                let id = cx.derive_id(format!("{}.{}", item_type, name));
-                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class);
-                assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "", cx);
+                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
+                );
+                assoc_const(
+                    w,
+                    item,
+                    ty,
+                    default.as_ref(),
+                    link.anchor(if trait_.is_some() { &source_id } else { &id }),
+                    "",
+                    cx,
+                );
                 w.write_str("</code>");
                 render_stability_since_raw(
                     w,
@@ -1499,23 +1539,31 @@ fn render_impl(
                     outer_version,
                     outer_const_version,
                 );
+                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
                 write_srclink(cx, item, w);
                 w.write_str("</h4>");
             }
             clean::AssocTypeItem(ref bounds, ref default) => {
-                let id = cx.derive_id(format!("{}.{}", item_type, name));
-                write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class);
+                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
+                );
                 assoc_type(
                     w,
                     item,
                     bounds,
                     default.as_ref(),
-                    link.anchor(&id),
+                    link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     "",
                     cx.cache(),
                     tcx,
                 );
-                w.write_str("</code></h4>");
+                w.write_str("</code>");
+                write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+                w.write_str("</h4>");
             }
             clean::StrippedItem(..) => return,
             _ => panic!("can't make docs for trait item with name {:?}", item.name),
@@ -1605,7 +1653,7 @@ fn render_impl(
                 true,
                 outer_version,
                 outer_const_version,
-                None,
+                Some(t),
                 show_def_docs,
             );
         }
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 585b7459bd7..705ae17f3eb 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -133,7 +133,8 @@ h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant {
 	margin-bottom: 10px;
 	position: relative;
 }
-h3.impl, h3.method, h3.type {
+h3.impl, h3.method, h4.method.trait-impl, h3.type,
+h4.type.trait-impl, h4.associatedconstant.trait-impl {
 	padding-left: 15px;
 }
 
@@ -655,7 +656,8 @@ a {
 	display: initial;
 }
 
-.in-band:hover > .anchor, .impl:hover > .anchor {
+.in-band:hover > .anchor, .impl:hover > .anchor, .method.trait-impl:hover > .anchor,
+.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor {
 	display: inline-block;
 	position: absolute;
 }
diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs
new file mode 100644
index 00000000000..6c09be1144a
--- /dev/null
+++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs
@@ -0,0 +1,65 @@
+pub trait MyTrait {
+    type Assoc;
+    const VALUE: u32;
+    fn trait_function(&self);
+    fn defaulted(&self) {}
+    fn defaulted_override(&self) {}
+}
+
+
+impl MyTrait for String {
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="type"]/@href' #associatedtype.Assoc
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1
+    type Assoc = ();
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1
+    const VALUE: u32 = 5;
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
+    fn trait_function(&self) {}
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1
+    fn defaulted_override(&self) {}
+}
+
+impl MyTrait for Vec<u8> {
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="type"]/@href' #associatedtype.Assoc
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2
+    type Assoc = ();
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2
+    const VALUE: u32 = 5;
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1
+    fn trait_function(&self) {}
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2
+    fn defaulted_override(&self) {}
+}
+
+impl MyTrait for MyStruct {
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedtype.Assoc
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc
+    type Assoc = bool;
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedconstant.VALUE
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
+    const VALUE: u32 = 20;
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
+    fn trait_function(&self) {}
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
+    fn defaulted_override(&self) {}
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
+}
+
+pub struct MyStruct;