Skip to content

Commit 8a473ca

Browse files
Recursively document Deref
1 parent f2707fe commit 8a473ca

File tree

3 files changed

+111
-16
lines changed

3 files changed

+111
-16
lines changed

src/librustdoc/html/render/context.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::rc::Rc;
66
use std::sync::mpsc::{channel, Receiver};
77

88
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
9-
use rustc_hir::def_id::LOCAL_CRATE;
9+
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_session::Session;
1212
use rustc_span::edition::Edition;
@@ -54,6 +54,9 @@ crate struct Context<'tcx> {
5454
/// real location of an item. This is used to allow external links to
5555
/// publicly reused items to redirect to the right location.
5656
pub(super) render_redirect_pages: bool,
57+
/// Tracks section IDs for `Deref` targets so they match in both the main
58+
/// body and the sidebar.
59+
pub(super) deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>,
5760
/// The map used to ensure all generated 'id=' attributes are unique.
5861
pub(super) id_map: RefCell<IdMap>,
5962
/// Shared mutable state.
@@ -70,7 +73,7 @@ crate struct Context<'tcx> {
7073

7174
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
7275
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
73-
rustc_data_structures::static_assert_size!(Context<'_>, 104);
76+
rustc_data_structures::static_assert_size!(Context<'_>, 112);
7477

7578
/// Shared mutable state used in [`Context`] and elsewhere.
7679
crate struct SharedContext<'tcx> {
@@ -513,6 +516,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
513516
dst,
514517
render_redirect_pages: false,
515518
id_map: RefCell::new(id_map),
519+
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
516520
shared: Rc::new(scx),
517521
include_sources,
518522
};
@@ -536,6 +540,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
536540
current: self.current.clone(),
537541
dst: self.dst.clone(),
538542
render_redirect_pages: self.render_redirect_pages,
543+
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
539544
id_map: RefCell::new(IdMap::new()),
540545
shared: Rc::clone(&self.shared),
541546
include_sources: self.include_sources,

src/librustdoc/html/render/mod.rs

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,19 @@ fn render_assoc_items(
10531053
containing_item: &clean::Item,
10541054
it: DefId,
10551055
what: AssocItemRender<'_>,
1056+
) {
1057+
let mut derefs = FxHashSet::default();
1058+
derefs.insert(it);
1059+
render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
1060+
}
1061+
1062+
fn render_assoc_items_inner(
1063+
w: &mut Buffer,
1064+
cx: &Context<'_>,
1065+
containing_item: &clean::Item,
1066+
it: DefId,
1067+
what: AssocItemRender<'_>,
1068+
derefs: &mut FxHashSet<DefId>,
10561069
) {
10571070
info!("Documenting associated items of {:?}", containing_item.name);
10581071
let cache = cx.cache();
@@ -1072,12 +1085,18 @@ fn render_assoc_items(
10721085
RenderMode::Normal
10731086
}
10741087
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
1088+
let id =
1089+
cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
1090+
if let Some(def_id) = type_.def_id(cx.cache()) {
1091+
cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
1092+
}
10751093
write!(
10761094
w,
1077-
"<h2 id=\"deref-methods\" class=\"small-section-header\">\
1095+
"<h2 id=\"{id}\" class=\"small-section-header\">\
10781096
<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>\
1079-
<a href=\"#deref-methods\" class=\"anchor\"></a>\
1097+
<a href=\"#{id}\" class=\"anchor\"></a>\
10801098
</h2>",
1099+
id = id,
10811100
trait_ = trait_.print(cx),
10821101
type_ = type_.print(cx),
10831102
);
@@ -1104,17 +1123,22 @@ fn render_assoc_items(
11041123
);
11051124
}
11061125
}
1107-
if let AssocItemRender::DerefFor { .. } = what {
1108-
return;
1109-
}
1126+
11101127
if !traits.is_empty() {
11111128
let deref_impl =
11121129
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
11131130
if let Some(impl_) = deref_impl {
11141131
let has_deref_mut =
11151132
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1116-
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut);
1133+
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs);
11171134
}
1135+
1136+
// If we were already one level into rendering deref methods, we don't want to render
1137+
// anything after recursing into any further deref methods above.
1138+
if let AssocItemRender::DerefFor { .. } = what {
1139+
return;
1140+
}
1141+
11181142
let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
11191143
traits.iter().partition(|t| t.inner_impl().synthetic);
11201144
let (blanket_impl, concrete): (Vec<&&Impl>, _) =
@@ -1166,6 +1190,7 @@ fn render_deref_methods(
11661190
impl_: &Impl,
11671191
container_item: &clean::Item,
11681192
deref_mut: bool,
1193+
derefs: &mut FxHashSet<DefId>,
11691194
) {
11701195
let cache = cx.cache();
11711196
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
@@ -1187,16 +1212,16 @@ fn render_deref_methods(
11871212
if let Some(did) = target.def_id(cache) {
11881213
if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
11891214
// `impl Deref<Target = S> for S`
1190-
if did == type_did {
1215+
if did == type_did || !derefs.insert(did) {
11911216
// Avoid infinite cycles
11921217
return;
11931218
}
11941219
}
1195-
render_assoc_items(w, cx, container_item, did, what);
1220+
render_assoc_items_inner(w, cx, container_item, did, what, derefs);
11961221
} else {
11971222
if let Some(prim) = target.primitive_type() {
11981223
if let Some(&did) = cache.primitive_locations.get(&prim) {
1199-
render_assoc_items(w, cx, container_item, did, what);
1224+
render_assoc_items_inner(w, cx, container_item, did, what, derefs);
12001225
}
12011226
}
12021227
}
@@ -1986,7 +2011,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
19862011
if let Some(impl_) =
19872012
v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
19882013
{
1989-
sidebar_deref_methods(cx, out, impl_, v);
2014+
let mut derefs = FxHashSet::default();
2015+
derefs.insert(did);
2016+
sidebar_deref_methods(cx, out, impl_, v, &mut derefs);
19902017
}
19912018

19922019
let format_impls = |impls: Vec<&Impl>| {
@@ -2060,7 +2087,13 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
20602087
}
20612088
}
20622089

2063-
fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[Impl]) {
2090+
fn sidebar_deref_methods(
2091+
cx: &Context<'_>,
2092+
out: &mut Buffer,
2093+
impl_: &Impl,
2094+
v: &[Impl],
2095+
derefs: &mut FxHashSet<DefId>,
2096+
) {
20642097
let c = cx.cache();
20652098

20662099
debug!("found Deref: {:?}", impl_);
@@ -2077,7 +2110,7 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
20772110
if let Some(did) = target.def_id(c) {
20782111
if let Some(type_did) = impl_.inner_impl().for_.def_id(c) {
20792112
// `impl Deref<Target = S> for S`
2080-
if did == type_did {
2113+
if did == type_did || !derefs.insert(did) {
20812114
// Avoid infinite cycles
20822115
return;
20832116
}
@@ -2101,9 +2134,17 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
21012134
})
21022135
.collect::<Vec<_>>();
21032136
if !ret.is_empty() {
2137+
let map;
2138+
let id = if let Some(target_def_id) = real_target.def_id(c) {
2139+
map = cx.deref_id_map.borrow();
2140+
map.get(&target_def_id).expect("Deref section without derived id")
2141+
} else {
2142+
"deref-methods"
2143+
};
21042144
write!(
21052145
out,
2106-
"<h3 class=\"sidebar-title\"><a href=\"#deref-methods\">Methods from {}&lt;Target={}&gt;</a></h3>",
2146+
"<h3 class=\"sidebar-title\"><a href=\"#{}\">Methods from {}&lt;Target={}&gt;</a></h3>",
2147+
id,
21072148
Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
21082149
Escape(&format!("{:#}", real_target.print(cx))),
21092150
);
@@ -2116,6 +2157,21 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
21162157
out.push_str("</div>");
21172158
}
21182159
}
2160+
2161+
// Recurse into any further impls that might exist for `target`
2162+
if let Some(target_did) = target.def_id_no_primitives() {
2163+
if let Some(target_impls) = c.impls.get(&target_did) {
2164+
if let Some(target_deref_impl) = target_impls.iter().find(|i| {
2165+
i.inner_impl()
2166+
.trait_
2167+
.as_ref()
2168+
.map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
2169+
.unwrap_or(false)
2170+
}) {
2171+
sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs);
2172+
}
2173+
}
2174+
}
21192175
}
21202176
}
21212177

src/librustdoc/passes/collect_trait_impls.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::clean::*;
33
use crate::core::DocContext;
44
use crate::fold::DocFolder;
55

6-
use rustc_data_structures::fx::FxHashSet;
6+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7+
use rustc_hir::def_id::DefId;
78
use rustc_middle::ty::DefIdTree;
89
use rustc_span::symbol::sym;
910

@@ -53,6 +54,29 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
5354
}
5455

5556
let mut cleaner = BadImplStripper { prims, items: crate_items };
57+
let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
58+
59+
// Follow all `Deref` targets of included items and recursively add them as valid
60+
fn add_deref_target(
61+
map: &FxHashMap<DefId, &Type>,
62+
cleaner: &mut BadImplStripper,
63+
type_did: DefId,
64+
) {
65+
if let Some(target) = map.get(&type_did) {
66+
debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
67+
if let Some(target_prim) = target.primitive_type() {
68+
cleaner.prims.insert(target_prim);
69+
} else if let Some(target_did) = target.def_id() {
70+
// `impl Deref<Target = S> for S`
71+
if target_did == type_did {
72+
// Avoid infinite cycles
73+
return;
74+
}
75+
cleaner.items.insert(target_did.into());
76+
add_deref_target(map, cleaner, target_did);
77+
}
78+
}
79+
}
5680

5781
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
5882
for it in &new_items {
@@ -73,6 +97,16 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
7397
} else if let Some(did) = target.def_id(&cx.cache) {
7498
cleaner.items.insert(did.into());
7599
}
100+
if let Some(for_did) = for_.def_id() {
101+
if type_did_to_deref_target.insert(for_did, target).is_none() {
102+
// Since only the `DefId` portion of the `Type` instances is known to be same for both the
103+
// `Deref` target type and the impl for type positions, this map of types is keyed by
104+
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
105+
if cleaner.keep_impl_with_def_id(for_did.into()) {
106+
add_deref_target(&type_did_to_deref_target, &mut cleaner, for_did);
107+
}
108+
}
109+
}
76110
}
77111
}
78112
}

0 commit comments

Comments
 (0)