Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,15 @@ impl Item {
.unwrap_or(false)
}

/// Returns true if item is an associated function with a `self` parameter.
pub(crate) fn has_self_param(&self) -> bool {
if let ItemKind::MethodItem(box Function { decl, .. }, _) = &self.inner.kind {
decl.receiver_type().is_some()
} else {
false
}
}

pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option<Span> {
let kind = match &self.kind {
ItemKind::StrippedItem(k) => k,
Expand Down
132 changes: 82 additions & 50 deletions src/librustdoc/html/render/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ fn sidebar_assoc_items<'a>(

let mut assoc_consts = Vec::new();
let mut assoc_types = Vec::new();
let mut assoc_fns = Vec::new();
let mut methods = Vec::new();
if let Some(v) = cache.impls.get(&did) {
let mut used_links = FxHashSet::default();
Expand All @@ -443,7 +444,12 @@ fn sidebar_assoc_items<'a>(
for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
assoc_types.extend(get_associated_types(impl_, used_links_bor));
methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx()));
methods.extend(get_methods(
impl_,
GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns },
used_links_bor,
cx.tcx(),
));
}
// We want links' order to be reproducible so we don't use unstable sort.
assoc_consts.sort();
Expand All @@ -462,6 +468,11 @@ fn sidebar_assoc_items<'a>(
"associatedtype",
assoc_types,
),
LinkBlock::new(
Link::new("implementations", "Associated Functions"),
"method",
assoc_fns,
),
LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
];

Expand Down Expand Up @@ -546,7 +557,15 @@ fn sidebar_deref_methods<'a>(
i.inner_impl().trait_.is_none()
&& real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
})
.flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
.flat_map(|i| {
get_methods(
i.inner_impl(),
GetMethodsMode::Deref { deref_mut },
used_links,
cx.tcx(),
)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
if !ret.is_empty() {
let id = if let Some(target_def_id) = real_target.def_id(c) {
Expand Down Expand Up @@ -734,69 +753,82 @@ fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
format!("{url}-{add}")
}

enum GetMethodsMode<'r, 'l> {
Deref { deref_mut: bool },
AlsoCollectAssocFns { assoc_fns: &'r mut Vec<Link<'l>> },
}

fn get_methods<'a>(
i: &'a clean::Impl,
for_deref: bool,
mut mode: GetMethodsMode<'_, 'a>,
used_links: &mut FxHashSet<String>,
deref_mut: bool,
tcx: TyCtxt<'_>,
) -> Vec<Link<'a>> {
i.items
.iter()
.filter_map(|item| {
if let Some(ref name) = item.name
&& item.is_method()
&& (!for_deref || super::should_render_item(item, deref_mut, tcx))
{
Some(Link::new(
) -> impl Iterator<Item = Link<'a>> {
i.items.iter().filter_map(move |item| {
if let Some(ref name) = item.name
&& item.is_method()
{
let mut build_link = || {
Link::new(
get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)),
name.as_str(),
))
} else {
None
)
};
match &mut mode {
&mut GetMethodsMode::Deref { deref_mut } => {
if super::should_render_item(item, deref_mut, tcx) {
Some(build_link())
} else {
None
}
}
GetMethodsMode::AlsoCollectAssocFns { assoc_fns } => {
if item.has_self_param() {
Some(build_link())
} else {
assoc_fns.push(build_link());
None
}
}
}
})
.collect()
} else {
None
}
})
}

fn get_associated_constants<'a>(
i: &'a clean::Impl,
used_links: &mut FxHashSet<String>,
) -> Vec<Link<'a>> {
i.items
.iter()
.filter_map(|item| {
if let Some(ref name) = item.name
&& item.is_associated_const()
{
Some(Link::new(
get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
name.as_str(),
))
} else {
None
}
})
.collect()
) -> impl Iterator<Item = Link<'a>> {
i.items.iter().filter_map(|item| {
if let Some(ref name) = item.name
&& item.is_associated_const()
{
Some(Link::new(
get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
name.as_str(),
))
} else {
None
}
})
}

fn get_associated_types<'a>(
i: &'a clean::Impl,
used_links: &mut FxHashSet<String>,
) -> Vec<Link<'a>> {
i.items
.iter()
.filter_map(|item| {
if let Some(ref name) = item.name
&& item.is_associated_type()
{
Some(Link::new(
get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
name.as_str(),
))
} else {
None
}
})
.collect()
) -> impl Iterator<Item = Link<'a>> {
i.items.iter().filter_map(|item| {
if let Some(ref name) = item.name
&& item.is_associated_type()
{
Some(Link::new(
get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
name.as_str(),
))
} else {
None
}
})
}
4 changes: 2 additions & 2 deletions tests/rustdoc-gui/hash-item-expansion.goml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ assert-attribute: ("#blanket-implementations-list > details:nth-child(2)", {"ope
// We first check that the impl block is open by default.
assert-attribute: ("#implementations-list details", {"open": ""})
// To ensure that we will click on the currently hidden method.
assert-text: (".sidebar-elems section .block li > a", "must_use")
click: ".sidebar-elems section .block li > a"
assert-text: (".sidebar-elems section ul:nth-of-type(2) li > a", "must_use")
click: ".sidebar-elems ul:nth-of-type(2) li > a"
// We check that the impl block was opened as expected so that we can see the method.
assert-attribute: ("#implementations-list > details", {"open": ""})
2 changes: 1 addition & 1 deletion tests/rustdoc-gui/sidebar-mobile.goml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ assert-property: ("rustdoc-topbar", {"clientHeight": "45"})
// Check that clicking an element from the sidebar scrolls to the right place
// so the target is not obscured by the topbar.
click: ".sidebar-menu-toggle"
click: ".sidebar-elems section .block li > a"
click: ".sidebar-elems section ul:nth-of-type(2) li > a"
assert-position: ("#method\.must_use", {"y": 45})

// Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
Expand Down
8 changes: 8 additions & 0 deletions tests/rustdoc-html/sidebar/sidebar-items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ pub struct Bar {
waza: u32,
}

//@ has foo/struct.Bar.html
//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#implementations"]' 'Associated Functions'
//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#implementations"]' 'Methods'
impl Bar {
pub fn method(&self) {}
pub fn assoc_fn() {}
}

//@ has foo/enum.En.html
//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#variants"]' 'Variants'
//@ has - '//*[@class="sidebar-elems"]//section//a' 'Foo'
Expand Down
2 changes: 1 addition & 1 deletion tests/rustdoc-html/typedef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl MyStruct {
//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl MyTrait for MyAlias'
//@ hasraw - 'Alias docstring'
//@ has - '//*[@class="sidebar"]//*[@class="location"]' 'MyAlias'
//@ has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods'
//@ has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Associated Functions'
//@ has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations'
/// Alias docstring
pub type MyAlias = MyStruct;
Expand Down
Loading