diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index bb1c186668cd6..0bc10c5e10fbe 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -7,8 +7,10 @@ use std::sync::mpsc::{channel, Receiver}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::source_map::FileName; use rustc_span::{sym, Symbol}; @@ -22,13 +24,13 @@ use super::{ sidebar::{sidebar_module_like, Sidebar}, AllTypes, LinkFromSrc, StylePath, }; -use crate::clean::{self, types::ExternalLocation, ExternalCrate}; +use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem}; use crate::config::{ModuleSorting, RenderOptions}; use crate::docfs::{DocFS, PathError}; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::FormatRenderer; +use crate::formats::{self, FormatRenderer}; use crate::html::escape::Escape; use crate::html::format::{join_with_double_colon, Buffer}; use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; @@ -147,6 +149,53 @@ impl SharedContext<'_> { pub(crate) fn edition(&self) -> Edition { self.tcx.sess.edition() } + + /// Returns a list of impls on the given type, and, if it's a type alias, + /// other types that it aliases. + pub(crate) fn all_impls_for_item<'a>( + &'a self, + it: &clean::Item, + did: DefId, + ) -> Vec<&'a formats::Impl> { + let tcx = self.tcx; + let cache = &self.cache; + let mut saw_impls = FxHashSet::default(); + let mut v: Vec<&formats::Impl> = cache + .impls + .get(&did) + .map(Vec::as_slice) + .unwrap_or(&[]) + .iter() + .filter(|i| saw_impls.insert(i.def_id())) + .collect(); + if let TypeAliasItem(ait) = &*it.kind && + let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) && + let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) && + let Some(av) = cache.impls.get(&aliased_type_defid) && + let Some(alias_def_id) = it.item_id.as_def_id() + { + // This branch of the compiler compares types structually, but does + // not check trait bounds. That's probably fine, since type aliases + // don't normally constrain on them anyway. + // https://github.com/rust-lang/rust/issues/21903 + // + // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification. + // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress. + let aliased_ty = tcx.type_of(alias_def_id).skip_binder(); + let reject_cx = DeepRejectCtxt { + treat_obligation_params: TreatParams::AsCandidateKey, + }; + v.extend(av.iter().filter(|impl_| { + if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() { + reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder()) + && saw_impls.insert(impl_def_id) + } else { + false + } + })); + } + v + } } impl<'tcx> Context<'tcx> { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a85e8356c2f21..5adbecd6d0411 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -54,7 +54,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; use rustc_middle::middle::stability; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TyCtxt; use rustc_span::{ symbol::{sym, Symbol}, @@ -63,7 +62,6 @@ use rustc_span::{ use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; -use crate::clean::types::TypeAliasItem; use crate::clean::{self, ItemId, RenderedLink, SelfTy}; use crate::error::Error; use crate::formats::cache::Cache; @@ -1119,13 +1117,13 @@ pub(crate) fn render_all_impls( fn render_assoc_items<'a, 'cx: 'a>( cx: &'a mut Context<'cx>, containing_item: &'a clean::Item, - it: DefId, + did: DefId, what: AssocItemRender<'a>, ) -> impl fmt::Display + 'a + Captures<'cx> { let mut derefs = DefIdSet::default(); - derefs.insert(it); + derefs.insert(did); display_fn(move |f| { - render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs); + render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs); Ok(()) }) } @@ -1134,46 +1132,16 @@ fn render_assoc_items_inner( mut w: &mut dyn fmt::Write, cx: &mut Context<'_>, containing_item: &clean::Item, - it: DefId, + did: DefId, what: AssocItemRender<'_>, derefs: &mut DefIdSet, ) { info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); - let cache = &shared.cache; - let tcx = cx.tcx(); - let av = if let TypeAliasItem(ait) = &*containing_item.kind && - let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) && - let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) && - let Some(mut av) = cache.impls.get(&aliased_type_defid).cloned() && - let Some(alias_def_id) = containing_item.item_id.as_def_id() - { - // This branch of the compiler compares types structually, but does - // not check trait bounds. That's probably fine, since type aliases - // don't normally constrain on them anyway. - // https://github.com/rust-lang/rust/issues/21903 - // - // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification. - // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress. - let aliased_ty = tcx.type_of(alias_def_id).skip_binder(); - let reject_cx = DeepRejectCtxt { - treat_obligation_params: TreatParams::AsCandidateKey, - }; - av.retain(|impl_| { - if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() { - reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder()) - } else { - false - } - }); - av - } else { - Vec::new() - }; - let blank = Vec::new(); - let v = cache.impls.get(&it).unwrap_or(&blank); - let (non_trait, traits): (Vec<_>, _) = - v.iter().chain(&av[..]).partition(|i| i.inner_impl().trait_.is_none()); + let v = shared.all_impls_for_item(containing_item, did); + let v = v.as_slice(); + let (non_trait, traits): (Vec<&Impl>, _) = + v.iter().partition(|i| i.inner_impl().trait_.is_none()); let mut saw_impls = FxHashSet::default(); if !non_trait.is_empty() { let mut tmp_buf = Buffer::html(); diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index fce31a8423d16..6466ae825d746 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -278,11 +278,12 @@ fn sidebar_assoc_items<'a>( links: &mut Vec>, ) { let did = it.item_id.expect_def_id(); - let cache = cx.cache(); + let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id()); + let v = v.as_slice(); let mut assoc_consts = Vec::new(); let mut methods = Vec::new(); - if let Some(v) = cache.impls.get(&did) { + if !v.is_empty() { let mut used_links = FxHashSet::default(); let mut id_map = IdMap::new(); @@ -318,7 +319,7 @@ fn sidebar_assoc_items<'a>( cx, &mut deref_methods, impl_, - v, + v.iter().copied(), &mut derefs, &mut used_links, ); @@ -348,7 +349,7 @@ fn sidebar_deref_methods<'a>( cx: &'a Context<'_>, out: &mut Vec>, impl_: &Impl, - v: &[Impl], + v: impl Iterator, derefs: &mut DefIdSet, used_links: &mut FxHashSet, ) { @@ -373,7 +374,7 @@ fn sidebar_deref_methods<'a>( // Avoid infinite cycles return; } - let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); let inner_impl = target .def_id(c) .or_else(|| { @@ -424,7 +425,7 @@ fn sidebar_deref_methods<'a>( cx, out, target_deref_impl, - target_impls, + target_impls.iter(), derefs, used_links, ); diff --git a/tests/rustdoc/issue-32077-type-alias-impls.rs b/tests/rustdoc/issue-32077-type-alias-impls.rs index 555d0579bee79..26778c67c2433 100644 --- a/tests/rustdoc/issue-32077-type-alias-impls.rs +++ b/tests/rustdoc/issue-32077-type-alias-impls.rs @@ -29,6 +29,11 @@ impl Bar for GenericStruct {} // @!has - '//h3' 'impl Bar for GenericStruct {}' // Same goes for the `Deref` impl. // @!has - '//h2' 'Methods from Deref' +// @count - '//nav[@class="sidebar"]//a' 'on_alias' 1 +// @count - '//nav[@class="sidebar"]//a' 'on_gen' 1 +// @count - '//nav[@class="sidebar"]//a' 'Foo' 1 +// @!has - '//nav[@class="sidebar"]//a' 'Bar' +// @!has - '//nav[@class="sidebar"]//a' 'on_u32' pub type TypedefStruct = GenericStruct; impl TypedefStruct {