mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-05 03:23:25 +00:00
Recursively document Deref
This commit is contained in:
parent
f2707fec04
commit
8a473ca346
@ -6,7 +6,7 @@ use std::rc::Rc;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edition::Edition;
|
||||
@ -54,6 +54,9 @@ crate struct Context<'tcx> {
|
||||
/// real location of an item. This is used to allow external links to
|
||||
/// publicly reused items to redirect to the right location.
|
||||
pub(super) render_redirect_pages: bool,
|
||||
/// Tracks section IDs for `Deref` targets so they match in both the main
|
||||
/// body and the sidebar.
|
||||
pub(super) deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>,
|
||||
/// The map used to ensure all generated 'id=' attributes are unique.
|
||||
pub(super) id_map: RefCell<IdMap>,
|
||||
/// Shared mutable state.
|
||||
@ -70,7 +73,7 @@ crate struct Context<'tcx> {
|
||||
|
||||
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
rustc_data_structures::static_assert_size!(Context<'_>, 104);
|
||||
rustc_data_structures::static_assert_size!(Context<'_>, 112);
|
||||
|
||||
/// Shared mutable state used in [`Context`] and elsewhere.
|
||||
crate struct SharedContext<'tcx> {
|
||||
@ -513,6 +516,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
dst,
|
||||
render_redirect_pages: false,
|
||||
id_map: RefCell::new(id_map),
|
||||
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
|
||||
shared: Rc::new(scx),
|
||||
include_sources,
|
||||
};
|
||||
@ -536,6 +540,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||
current: self.current.clone(),
|
||||
dst: self.dst.clone(),
|
||||
render_redirect_pages: self.render_redirect_pages,
|
||||
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
|
||||
id_map: RefCell::new(IdMap::new()),
|
||||
shared: Rc::clone(&self.shared),
|
||||
include_sources: self.include_sources,
|
||||
|
@ -1053,6 +1053,19 @@ fn render_assoc_items(
|
||||
containing_item: &clean::Item,
|
||||
it: DefId,
|
||||
what: AssocItemRender<'_>,
|
||||
) {
|
||||
let mut derefs = FxHashSet::default();
|
||||
derefs.insert(it);
|
||||
render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
|
||||
}
|
||||
|
||||
fn render_assoc_items_inner(
|
||||
w: &mut Buffer,
|
||||
cx: &Context<'_>,
|
||||
containing_item: &clean::Item,
|
||||
it: DefId,
|
||||
what: AssocItemRender<'_>,
|
||||
derefs: &mut FxHashSet<DefId>,
|
||||
) {
|
||||
info!("Documenting associated items of {:?}", containing_item.name);
|
||||
let cache = cx.cache();
|
||||
@ -1072,12 +1085,18 @@ fn render_assoc_items(
|
||||
RenderMode::Normal
|
||||
}
|
||||
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
|
||||
let id =
|
||||
cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
|
||||
if let Some(def_id) = type_.def_id(cx.cache()) {
|
||||
cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"<h2 id=\"deref-methods\" class=\"small-section-header\">\
|
||||
"<h2 id=\"{id}\" class=\"small-section-header\">\
|
||||
<span>Methods from {trait_}<Target = {type_}></span>\
|
||||
<a href=\"#deref-methods\" class=\"anchor\"></a>\
|
||||
<a href=\"#{id}\" class=\"anchor\"></a>\
|
||||
</h2>",
|
||||
id = id,
|
||||
trait_ = trait_.print(cx),
|
||||
type_ = type_.print(cx),
|
||||
);
|
||||
@ -1104,17 +1123,22 @@ fn render_assoc_items(
|
||||
);
|
||||
}
|
||||
}
|
||||
if let AssocItemRender::DerefFor { .. } = what {
|
||||
return;
|
||||
}
|
||||
|
||||
if !traits.is_empty() {
|
||||
let deref_impl =
|
||||
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
|
||||
if let Some(impl_) = deref_impl {
|
||||
let has_deref_mut =
|
||||
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
|
||||
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut);
|
||||
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs);
|
||||
}
|
||||
|
||||
// If we were already one level into rendering deref methods, we don't want to render
|
||||
// anything after recursing into any further deref methods above.
|
||||
if let AssocItemRender::DerefFor { .. } = what {
|
||||
return;
|
||||
}
|
||||
|
||||
let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
|
||||
traits.iter().partition(|t| t.inner_impl().synthetic);
|
||||
let (blanket_impl, concrete): (Vec<&&Impl>, _) =
|
||||
@ -1166,6 +1190,7 @@ fn render_deref_methods(
|
||||
impl_: &Impl,
|
||||
container_item: &clean::Item,
|
||||
deref_mut: bool,
|
||||
derefs: &mut FxHashSet<DefId>,
|
||||
) {
|
||||
let cache = cx.cache();
|
||||
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
|
||||
@ -1187,16 +1212,16 @@ fn render_deref_methods(
|
||||
if let Some(did) = target.def_id(cache) {
|
||||
if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) {
|
||||
// `impl Deref<Target = S> for S`
|
||||
if did == type_did {
|
||||
if did == type_did || !derefs.insert(did) {
|
||||
// Avoid infinite cycles
|
||||
return;
|
||||
}
|
||||
}
|
||||
render_assoc_items(w, cx, container_item, did, what);
|
||||
render_assoc_items_inner(w, cx, container_item, did, what, derefs);
|
||||
} else {
|
||||
if let Some(prim) = target.primitive_type() {
|
||||
if let Some(&did) = cache.primitive_locations.get(&prim) {
|
||||
render_assoc_items(w, cx, container_item, did, what);
|
||||
render_assoc_items_inner(w, cx, container_item, did, what, derefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1986,7 +2011,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
|
||||
if let Some(impl_) =
|
||||
v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
|
||||
{
|
||||
sidebar_deref_methods(cx, out, impl_, v);
|
||||
let mut derefs = FxHashSet::default();
|
||||
derefs.insert(did);
|
||||
sidebar_deref_methods(cx, out, impl_, v, &mut derefs);
|
||||
}
|
||||
|
||||
let format_impls = |impls: Vec<&Impl>| {
|
||||
@ -2060,7 +2087,13 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
|
||||
}
|
||||
}
|
||||
|
||||
fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[Impl]) {
|
||||
fn sidebar_deref_methods(
|
||||
cx: &Context<'_>,
|
||||
out: &mut Buffer,
|
||||
impl_: &Impl,
|
||||
v: &[Impl],
|
||||
derefs: &mut FxHashSet<DefId>,
|
||||
) {
|
||||
let c = cx.cache();
|
||||
|
||||
debug!("found Deref: {:?}", impl_);
|
||||
@ -2077,7 +2110,7 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
|
||||
if let Some(did) = target.def_id(c) {
|
||||
if let Some(type_did) = impl_.inner_impl().for_.def_id(c) {
|
||||
// `impl Deref<Target = S> for S`
|
||||
if did == type_did {
|
||||
if did == type_did || !derefs.insert(did) {
|
||||
// Avoid infinite cycles
|
||||
return;
|
||||
}
|
||||
@ -2101,9 +2134,17 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !ret.is_empty() {
|
||||
let map;
|
||||
let id = if let Some(target_def_id) = real_target.def_id(c) {
|
||||
map = cx.deref_id_map.borrow();
|
||||
map.get(&target_def_id).expect("Deref section without derived id")
|
||||
} else {
|
||||
"deref-methods"
|
||||
};
|
||||
write!(
|
||||
out,
|
||||
"<h3 class=\"sidebar-title\"><a href=\"#deref-methods\">Methods from {}<Target={}></a></h3>",
|
||||
"<h3 class=\"sidebar-title\"><a href=\"#{}\">Methods from {}<Target={}></a></h3>",
|
||||
id,
|
||||
Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
|
||||
Escape(&format!("{:#}", real_target.print(cx))),
|
||||
);
|
||||
@ -2116,6 +2157,21 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[
|
||||
out.push_str("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse into any further impls that might exist for `target`
|
||||
if let Some(target_did) = target.def_id_no_primitives() {
|
||||
if let Some(target_impls) = c.impls.get(&target_did) {
|
||||
if let Some(target_deref_impl) = target_impls.iter().find(|i| {
|
||||
i.inner_impl()
|
||||
.trait_
|
||||
.as_ref()
|
||||
.map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ use crate::clean::*;
|
||||
use crate::core::DocContext;
|
||||
use crate::fold::DocFolder;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
@ -53,6 +54,29 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
|
||||
}
|
||||
|
||||
let mut cleaner = BadImplStripper { prims, items: crate_items };
|
||||
let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
|
||||
|
||||
// Follow all `Deref` targets of included items and recursively add them as valid
|
||||
fn add_deref_target(
|
||||
map: &FxHashMap<DefId, &Type>,
|
||||
cleaner: &mut BadImplStripper,
|
||||
type_did: DefId,
|
||||
) {
|
||||
if let Some(target) = map.get(&type_did) {
|
||||
debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
|
||||
if let Some(target_prim) = target.primitive_type() {
|
||||
cleaner.prims.insert(target_prim);
|
||||
} else if let Some(target_did) = target.def_id() {
|
||||
// `impl Deref<Target = S> for S`
|
||||
if target_did == type_did {
|
||||
// Avoid infinite cycles
|
||||
return;
|
||||
}
|
||||
cleaner.items.insert(target_did.into());
|
||||
add_deref_target(map, cleaner, target_did);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
|
||||
for it in &new_items {
|
||||
@ -73,6 +97,16 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
|
||||
} else if let Some(did) = target.def_id(&cx.cache) {
|
||||
cleaner.items.insert(did.into());
|
||||
}
|
||||
if let Some(for_did) = for_.def_id() {
|
||||
if type_did_to_deref_target.insert(for_did, target).is_none() {
|
||||
// Since only the `DefId` portion of the `Type` instances is known to be same for both the
|
||||
// `Deref` target type and the impl for type positions, this map of types is keyed by
|
||||
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
|
||||
if cleaner.keep_impl_with_def_id(for_did.into()) {
|
||||
add_deref_target(&type_did_to_deref_target, &mut cleaner, for_did);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user