Skip to content

Commit 29a2d6b

Browse files
committed
intra-doc: Use the impl's assoc item where possible
Before, the trait's associated item would be used. Now, the impl's associated item is used. The only exception is for impls that use default values for associated items set by the trait. In that case, the trait's associated item is still used. As an example of the old and new behavior, take this code: trait MyTrait { type AssocTy; } impl MyTrait for String { type AssocTy = u8; } Before, when resolving a link to `String::AssocTy`, `resolve_associated_trait_item` would return the associated item for `MyTrait::AssocTy`. Now, it would return the associated item for `<String as MyTrait>::AssocTy`, as it claims in its docs.
1 parent 9ad5d82 commit 29a2d6b

File tree

1 file changed

+53
-35
lines changed

1 file changed

+53
-35
lines changed

Diff for: src/librustdoc/passes/collect_intra_doc_links.rs

+53-35
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,15 @@ crate enum FragmentKind {
305305

306306
impl ItemFragment {
307307
/// Create a fragment for an associated item.
308-
///
309-
/// `is_prototype` is whether this associated item is a trait method
310-
/// without a default definition.
311-
fn from_assoc_item(def_id: DefId, kind: ty::AssocKind, is_prototype: bool) -> Self {
312-
match kind {
308+
#[instrument(level = "debug")]
309+
fn from_assoc_item(item: &ty::AssocItem) -> Self {
310+
let def_id = item.def_id;
311+
match item.kind {
313312
ty::AssocKind::Fn => {
314-
if is_prototype {
315-
ItemFragment(FragmentKind::TyMethod, def_id)
316-
} else {
313+
if item.defaultness.has_value() {
317314
ItemFragment(FragmentKind::Method, def_id)
315+
} else {
316+
ItemFragment(FragmentKind::TyMethod, def_id)
318317
}
319318
}
320319
ty::AssocKind::Const => ItemFragment(FragmentKind::AssociatedConstant, def_id),
@@ -473,8 +472,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
473472
tcx.associated_items(impl_)
474473
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_)
475474
.map(|item| {
476-
let kind = item.kind;
477-
let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false);
475+
let fragment = ItemFragment::from_assoc_item(item);
478476
(Res::Primitive(prim_ty), fragment)
479477
})
480478
})
@@ -726,8 +724,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
726724
.flatten();
727725

728726
assoc_item.map(|item| {
729-
let kind = item.kind;
730-
let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false);
727+
let fragment = ItemFragment::from_assoc_item(&item);
731728
(root_res, fragment)
732729
})
733730
})
@@ -765,20 +762,19 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
765762
// To handle that properly resolve() would have to support
766763
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
767764
.or_else(|| {
768-
let item = resolve_associated_trait_item(
765+
resolve_associated_trait_item(
769766
tcx.type_of(did),
770767
module_id,
771768
item_name,
772769
ns,
773770
self.cx,
774-
);
775-
debug!("got associated item {:?}", item);
776-
item
771+
)
777772
});
778773

774+
debug!("got associated item {:?}", assoc_item);
775+
779776
if let Some(item) = assoc_item {
780-
let kind = item.kind;
781-
let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false);
777+
let fragment = ItemFragment::from_assoc_item(&item);
782778
return Some((root_res, fragment));
783779
}
784780

@@ -813,11 +809,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
813809
.associated_items(did)
814810
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, did)
815811
.map(|item| {
816-
let fragment = ItemFragment::from_assoc_item(
817-
item.def_id,
818-
item.kind,
819-
!item.defaultness.has_value(),
820-
);
812+
let fragment = ItemFragment::from_assoc_item(item);
821813
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
822814
(res, fragment)
823815
}),
@@ -883,30 +875,56 @@ fn resolve_associated_trait_item<'a>(
883875

884876
// Next consider explicit impls: `impl MyTrait for MyType`
885877
// Give precedence to inherent impls.
886-
let traits = traits_implemented_by(cx, ty, module);
878+
let traits = trait_impls_for(cx, ty, module);
887879
debug!("considering traits {:?}", traits);
888-
let mut candidates = traits.iter().filter_map(|&trait_| {
889-
cx.tcx.associated_items(trait_).find_by_name_and_namespace(
890-
cx.tcx,
891-
Ident::with_dummy_span(item_name),
892-
ns,
893-
trait_,
894-
)
880+
let mut candidates = traits.iter().filter_map(|&(impl_, trait_)| {
881+
cx.tcx
882+
.associated_items(trait_)
883+
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
884+
.map(|trait_assoc| {
885+
trait_assoc_to_impl_assoc_item(cx.tcx, impl_, trait_assoc.def_id)
886+
.unwrap_or(trait_assoc)
887+
})
895888
});
896889
// FIXME(#74563): warn about ambiguity
897890
debug!("the candidates were {:?}", candidates.clone().collect::<Vec<_>>());
898891
candidates.next().copied()
899892
}
900893

901-
/// Given a type, return all traits in scope in `module` implemented by that type.
894+
/// Find the associated item in the impl `impl_id` that corresponds to the
895+
/// trait associated item `trait_assoc_id`.
896+
///
897+
/// This function returns `None` if no associated item was found in the impl.
898+
/// This can occur when the trait associated item has a default value that is
899+
/// not overriden in the impl.
900+
///
901+
/// This is just a wrapper around [`TyCtxt::impl_item_implementor_ids()`] and
902+
/// [`TyCtxt::associated_item()`] (with some helpful logging added).
903+
#[instrument(level = "debug", skip(tcx))]
904+
fn trait_assoc_to_impl_assoc_item<'tcx>(
905+
tcx: TyCtxt<'tcx>,
906+
impl_id: DefId,
907+
trait_assoc_id: DefId,
908+
) -> Option<&'tcx ty::AssocItem> {
909+
let trait_to_impl_assoc_map = tcx.impl_item_implementor_ids(impl_id);
910+
debug!(?trait_to_impl_assoc_map);
911+
let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?;
912+
debug!(?impl_assoc_id);
913+
let impl_assoc = tcx.associated_item(impl_assoc_id);
914+
debug!(?impl_assoc);
915+
Some(impl_assoc)
916+
}
917+
918+
/// Given a type, return all trait impls in scope in `module` for that type.
919+
/// Returns a set of pairs of `(impl_id, trait_id)`.
902920
///
903921
/// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
904922
/// So it is not stable to serialize cross-crate.
905-
fn traits_implemented_by<'a>(
923+
fn trait_impls_for<'a>(
906924
cx: &mut DocContext<'a>,
907925
ty: Ty<'a>,
908926
module: DefId,
909-
) -> FxHashSet<DefId> {
927+
) -> FxHashSet<(DefId, DefId)> {
910928
let mut resolver = cx.resolver.borrow_mut();
911929
let in_scope_traits = cx.module_trait_cache.entry(module).or_insert_with(|| {
912930
resolver.access(|resolver| {
@@ -948,7 +966,7 @@ fn traits_implemented_by<'a>(
948966
_ => false,
949967
};
950968

951-
if saw_impl { Some(trait_) } else { None }
969+
if saw_impl { Some((impl_, trait_)) } else { None }
952970
})
953971
});
954972
iter.collect()

0 commit comments

Comments
 (0)