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;
/// 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.
// `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> {
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,
};
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,
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();
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),
);
);
}
}
- 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>, _) =
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();
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);
}
}
}
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>| {
}
}
-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_);
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;
}
})
.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))),
);
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);
+ }
+ }
+ }
}
}
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;
}
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 {
} 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);
+ }
+ }
+ }
}
}
}