Skip to content

Commit 937f629

Browse files
committed
Auto merge of #80653 - jryans:doc-deref-recursive, r=jyn514,GuillaumeGomez
Recursively document methods via `Deref` traits This changes `rustdoc` to recursively follow `Deref` targets so that methods from all levels are added to the rendered output. This implementation displays the methods from all levels in the expanded state with separate sections for each level. ![image](https://user-images.githubusercontent.com/279572/103482863-46723b00-4ddb-11eb-972b-c463351a425c.png) Fixes #26207 Fixes #53038 Fixes #71640 r? `@jyn514`
2 parents 3d8608a + ea94607 commit 937f629

File tree

7 files changed

+276
-122
lines changed

7 files changed

+276
-122
lines changed

src/librustdoc/html/markdown.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1322,7 +1322,6 @@ fn init_id_map() -> FxHashMap<String, usize> {
13221322
map.insert("trait-implementations".to_owned(), 1);
13231323
map.insert("synthetic-implementations".to_owned(), 1);
13241324
map.insert("blanket-implementations".to_owned(), 1);
1325-
map.insert("deref-methods".to_owned(), 1);
13261325
map
13271326
}
13281327

src/librustdoc/html/render/mod.rs

+123-83
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ crate struct Context<'tcx> {
115115
crate render_redirect_pages: bool,
116116
/// The map used to ensure all generated 'id=' attributes are unique.
117117
id_map: Rc<RefCell<IdMap>>,
118+
/// Tracks section IDs for `Deref` targets so they match in both the main
119+
/// body and the sidebar.
120+
deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>,
118121
crate shared: Arc<SharedContext<'tcx>>,
119122
all: Rc<RefCell<AllTypes>>,
120123
/// Storage for the errors produced while generating documentation so they
@@ -372,7 +375,6 @@ crate fn initial_ids() -> Vec<String> {
372375
"implementors-list",
373376
"synthetic-implementors-list",
374377
"methods",
375-
"deref-methods",
376378
"implementations",
377379
]
378380
.iter()
@@ -506,6 +508,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
506508
dst,
507509
render_redirect_pages: false,
508510
id_map: Rc::new(RefCell::new(id_map)),
511+
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
509512
shared: Arc::new(scx),
510513
all: Rc::new(RefCell::new(AllTypes::new())),
511514
errors: Rc::new(receiver),
@@ -3517,14 +3520,18 @@ fn render_assoc_items(
35173520
RenderMode::Normal
35183521
}
35193522
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
3523+
let id =
3524+
cx.derive_id(small_url_encode(&format!("deref-methods-{:#}", type_.print())));
3525+
cx.deref_id_map.borrow_mut().insert(type_.def_id().unwrap(), id.clone());
35203526
write!(
35213527
w,
3522-
"<h2 id=\"deref-methods\" class=\"small-section-header\">\
3523-
Methods from {}&lt;Target = {}&gt;\
3524-
<a href=\"#deref-methods\" class=\"anchor\"></a>\
3528+
"<h2 id=\"{id}\" class=\"small-section-header\">\
3529+
Methods from {trait_}&lt;Target = {type_}&gt;\
3530+
<a href=\"#{id}\" class=\"anchor\"></a>\
35253531
</h2>",
3526-
trait_.print(),
3527-
type_.print()
3532+
id = id,
3533+
trait_ = trait_.print(),
3534+
type_ = type_.print(),
35283535
);
35293536
RenderMode::ForDeref { mut_: deref_mut_ }
35303537
}
@@ -3548,9 +3555,6 @@ fn render_assoc_items(
35483555
);
35493556
}
35503557
}
3551-
if let AssocItemRender::DerefFor { .. } = what {
3552-
return;
3553-
}
35543558
if !traits.is_empty() {
35553559
let deref_impl =
35563560
traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did);
@@ -3560,6 +3564,12 @@ fn render_assoc_items(
35603564
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache);
35613565
}
35623566

3567+
// If we were already one level into rendering deref methods, we don't want to render
3568+
// anything after recursing into any further deref methods above.
3569+
if let AssocItemRender::DerefFor { .. } = what {
3570+
return;
3571+
}
3572+
35633573
let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
35643574
traits.iter().partition(|t| t.inner_impl().synthetic);
35653575
let (blanket_impl, concrete): (Vec<&&Impl>, _) =
@@ -3631,6 +3641,13 @@ fn render_deref_methods(
36313641
let what =
36323642
AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
36333643
if let Some(did) = target.def_id() {
3644+
if let Some(type_did) = impl_.inner_impl().for_.def_id() {
3645+
// `impl Deref<Target = S> for S`
3646+
if did == type_did {
3647+
// Avoid infinite cycles
3648+
return;
3649+
}
3650+
}
36343651
render_assoc_items(w, cx, container_item, did, what, cache);
36353652
} else {
36363653
if let Some(prim) = target.primitive_type() {
@@ -4165,14 +4182,14 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer, cache:
41654182
);
41664183
}
41674184
match *it.kind {
4168-
clean::StructItem(ref s) => sidebar_struct(buffer, it, s),
4169-
clean::TraitItem(ref t) => sidebar_trait(buffer, it, t),
4170-
clean::PrimitiveItem(_) => sidebar_primitive(buffer, it),
4171-
clean::UnionItem(ref u) => sidebar_union(buffer, it, u),
4172-
clean::EnumItem(ref e) => sidebar_enum(buffer, it, e),
4173-
clean::TypedefItem(_, _) => sidebar_typedef(buffer, it),
4185+
clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
4186+
clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
4187+
clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
4188+
clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
4189+
clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
4190+
clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it),
41744191
clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
4175-
clean::ForeignTypeItem => sidebar_foreign_type(buffer, it),
4192+
clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
41764193
_ => (),
41774194
}
41784195

@@ -4273,7 +4290,7 @@ fn small_url_encode(s: &str) -> String {
42734290
.replace("\"", "%22")
42744291
}
42754292

4276-
fn sidebar_assoc_items(it: &clean::Item) -> String {
4293+
fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String {
42774294
let mut out = String::new();
42784295
let c = cache();
42794296
if let Some(v) = c.impls.get(&it.def_id) {
@@ -4303,58 +4320,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
43034320
.filter(|i| i.inner_impl().trait_.is_some())
43044321
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
43054322
{
4306-
debug!("found Deref: {:?}", impl_);
4307-
if let Some((target, real_target)) =
4308-
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
4309-
clean::TypedefItem(ref t, true) => Some(match *t {
4310-
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
4311-
_ => (&t.type_, &t.type_),
4312-
}),
4313-
_ => None,
4314-
})
4315-
{
4316-
debug!("found target, real_target: {:?} {:?}", target, real_target);
4317-
let deref_mut = v
4318-
.iter()
4319-
.filter(|i| i.inner_impl().trait_.is_some())
4320-
.any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did);
4321-
let inner_impl = target
4322-
.def_id()
4323-
.or_else(|| {
4324-
target
4325-
.primitive_type()
4326-
.and_then(|prim| c.primitive_locations.get(&prim).cloned())
4327-
})
4328-
.and_then(|did| c.impls.get(&did));
4329-
if let Some(impls) = inner_impl {
4330-
debug!("found inner_impl: {:?}", impls);
4331-
out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">");
4332-
out.push_str(&format!(
4333-
"Methods from {}&lt;Target={}&gt;",
4334-
Escape(&format!(
4335-
"{:#}",
4336-
impl_.inner_impl().trait_.as_ref().unwrap().print()
4337-
)),
4338-
Escape(&format!("{:#}", real_target.print()))
4339-
));
4340-
out.push_str("</a>");
4341-
let mut ret = impls
4342-
.iter()
4343-
.filter(|i| i.inner_impl().trait_.is_none())
4344-
.flat_map(|i| {
4345-
get_methods(i.inner_impl(), true, &mut used_links, deref_mut)
4346-
})
4347-
.collect::<Vec<_>>();
4348-
// We want links' order to be reproducible so we don't use unstable sort.
4349-
ret.sort();
4350-
if !ret.is_empty() {
4351-
out.push_str(&format!(
4352-
"<div class=\"sidebar-links\">{}</div>",
4353-
ret.join("")
4354-
));
4355-
}
4356-
}
4357-
}
4323+
out.push_str(&sidebar_deref_methods(cx, impl_, v));
43584324
}
43594325
let format_impls = |impls: Vec<&Impl>| {
43604326
let mut links = FxHashSet::default();
@@ -4422,7 +4388,81 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
44224388
out
44234389
}
44244390

4425-
fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
4391+
fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> String {
4392+
let mut out = String::new();
4393+
let c = cache();
4394+
4395+
debug!("found Deref: {:?}", impl_);
4396+
if let Some((target, real_target)) =
4397+
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
4398+
clean::TypedefItem(ref t, true) => Some(match *t {
4399+
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
4400+
_ => (&t.type_, &t.type_),
4401+
}),
4402+
_ => None,
4403+
})
4404+
{
4405+
debug!("found target, real_target: {:?} {:?}", target, real_target);
4406+
let deref_mut = v
4407+
.iter()
4408+
.filter(|i| i.inner_impl().trait_.is_some())
4409+
.any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did);
4410+
let inner_impl = target
4411+
.def_id()
4412+
.or_else(|| {
4413+
target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
4414+
})
4415+
.and_then(|did| c.impls.get(&did));
4416+
if let Some(impls) = inner_impl {
4417+
debug!("found inner_impl: {:?}", impls);
4418+
let mut used_links = FxHashSet::default();
4419+
let mut ret = impls
4420+
.iter()
4421+
.filter(|i| i.inner_impl().trait_.is_none())
4422+
.flat_map(|i| get_methods(i.inner_impl(), true, &mut used_links, deref_mut))
4423+
.collect::<Vec<_>>();
4424+
if !ret.is_empty() {
4425+
let deref_id_map = cx.deref_id_map.borrow();
4426+
let id = deref_id_map
4427+
.get(&real_target.def_id().unwrap())
4428+
.expect("Deref section without derived id");
4429+
out.push_str(&format!(
4430+
"<a class=\"sidebar-title\" href=\"#{}\">Methods from {}&lt;Target={}&gt;</a>",
4431+
id,
4432+
Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print())),
4433+
Escape(&format!("{:#}", real_target.print())),
4434+
));
4435+
// We want links' order to be reproducible so we don't use unstable sort.
4436+
ret.sort();
4437+
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join("")));
4438+
}
4439+
}
4440+
4441+
// Recurse into any further impls that might exist for `target`
4442+
if let Some(target_did) = target.def_id() {
4443+
if let Some(target_impls) = c.impls.get(&target_did) {
4444+
if let Some(target_deref_impl) = target_impls
4445+
.iter()
4446+
.filter(|i| i.inner_impl().trait_.is_some())
4447+
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
4448+
{
4449+
if let Some(type_did) = impl_.inner_impl().for_.def_id() {
4450+
// `impl Deref<Target = S> for S`
4451+
if target_did == type_did {
4452+
// Avoid infinite cycles
4453+
return out;
4454+
}
4455+
}
4456+
out.push_str(&sidebar_deref_methods(cx, target_deref_impl, target_impls));
4457+
}
4458+
}
4459+
}
4460+
}
4461+
4462+
out
4463+
}
4464+
4465+
fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
44264466
let mut sidebar = String::new();
44274467
let fields = get_struct_fields_name(&s.fields);
44284468

@@ -4436,7 +4476,7 @@ fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
44364476
}
44374477
}
44384478

4439-
sidebar.push_str(&sidebar_assoc_items(it));
4479+
sidebar.push_str(&sidebar_assoc_items(cx, it));
44404480

44414481
if !sidebar.is_empty() {
44424482
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
@@ -4467,7 +4507,7 @@ fn is_negative_impl(i: &clean::Impl) -> bool {
44674507
i.polarity == Some(clean::ImplPolarity::Negative)
44684508
}
44694509

4470-
fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
4510+
fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
44714511
let mut sidebar = String::new();
44724512

44734513
let mut types = t
@@ -4567,7 +4607,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
45674607
}
45684608
}
45694609

4570-
sidebar.push_str(&sidebar_assoc_items(it));
4610+
sidebar.push_str(&sidebar_assoc_items(cx, it));
45714611

45724612
sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
45734613
if t.is_auto {
@@ -4580,16 +4620,16 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
45804620
write!(buf, "<div class=\"block items\">{}</div>", sidebar)
45814621
}
45824622

4583-
fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) {
4584-
let sidebar = sidebar_assoc_items(it);
4623+
fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
4624+
let sidebar = sidebar_assoc_items(cx, it);
45854625

45864626
if !sidebar.is_empty() {
45874627
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
45884628
}
45894629
}
45904630

4591-
fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) {
4592-
let sidebar = sidebar_assoc_items(it);
4631+
fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
4632+
let sidebar = sidebar_assoc_items(cx, it);
45934633

45944634
if !sidebar.is_empty() {
45954635
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
@@ -4611,7 +4651,7 @@ fn get_struct_fields_name(fields: &[clean::Item]) -> String {
46114651
fields.join("")
46124652
}
46134653

4614-
fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
4654+
fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
46154655
let mut sidebar = String::new();
46164656
let fields = get_struct_fields_name(&u.fields);
46174657

@@ -4623,14 +4663,14 @@ fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
46234663
));
46244664
}
46254665

4626-
sidebar.push_str(&sidebar_assoc_items(it));
4666+
sidebar.push_str(&sidebar_assoc_items(cx, it));
46274667

46284668
if !sidebar.is_empty() {
46294669
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
46304670
}
46314671
}
46324672

4633-
fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
4673+
fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
46344674
let mut sidebar = String::new();
46354675

46364676
let mut variants = e
@@ -4650,7 +4690,7 @@ fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
46504690
));
46514691
}
46524692

4653-
sidebar.push_str(&sidebar_assoc_items(it));
4693+
sidebar.push_str(&sidebar_assoc_items(cx, it));
46544694

46554695
if !sidebar.is_empty() {
46564696
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
@@ -4739,8 +4779,8 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
47394779
}
47404780
}
47414781

4742-
fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) {
4743-
let sidebar = sidebar_assoc_items(it);
4782+
fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
4783+
let sidebar = sidebar_assoc_items(cx, it);
47444784
if !sidebar.is_empty() {
47454785
write!(buf, "<div class=\"block items\">{}</div>", sidebar);
47464786
}

0 commit comments

Comments
 (0)