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.
This commit is contained in:
bors 2021-04-14 12:47:49 +00:00
commit b203b0d240
3 changed files with 134 additions and 19 deletions

View File

@ -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,
);
}

View File

@ -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;
}

View File

@ -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;