Skip to content

Commit 14a285c

Browse files
committed
Rustdoc: resolve associated traits for primitive types
Fixes #90703
1 parent ddabe07 commit 14a285c

File tree

4 files changed

+85
-30
lines changed

4 files changed

+85
-30
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hir::def::{
1313
PerNS,
1414
};
1515
use rustc_hir::def_id::{CrateNum, DefId};
16-
use rustc_middle::ty::TyCtxt;
16+
use rustc_middle::ty::{Ty, TyCtxt};
1717
use rustc_middle::{bug, span_bug, ty};
1818
use rustc_resolve::ParentScope;
1919
use rustc_session::lint::Lint;
@@ -618,6 +618,44 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
618618
})
619619
}
620620

621+
/// Convert a PrimitiveType to a Ty, where possible.
622+
///
623+
/// This is used for resolving trait impls for primitives
624+
fn primitive_type_to_ty(&mut self, prim: PrimitiveType) -> Option<Ty<'tcx>> {
625+
use PrimitiveType::*;
626+
let tcx = self.cx.tcx;
627+
628+
Some(tcx.mk_ty(match prim {
629+
Bool => ty::Bool,
630+
Str => ty::Str,
631+
Char => ty::Char,
632+
Never => ty::Never,
633+
I8 => ty::Int(ty::IntTy::I8),
634+
I16 => ty::Int(ty::IntTy::I16),
635+
I32 => ty::Int(ty::IntTy::I32),
636+
I64 => ty::Int(ty::IntTy::I64),
637+
I128 => ty::Int(ty::IntTy::I128),
638+
Isize => ty::Int(ty::IntTy::Isize),
639+
F32 => ty::Float(ty::FloatTy::F32),
640+
F64 => ty::Float(ty::FloatTy::F64),
641+
U8 => ty::Uint(ty::UintTy::U8),
642+
U16 => ty::Uint(ty::UintTy::U16),
643+
U32 => ty::Uint(ty::UintTy::U32),
644+
U64 => ty::Uint(ty::UintTy::U64),
645+
U128 => ty::Uint(ty::UintTy::U128),
646+
Usize => ty::Uint(ty::UintTy::Usize),
647+
//ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit),
648+
//ty::Tuple(_) => Res::Primitive(Tuple),
649+
//ty::Array(..) => Res::Primitive(Array),
650+
//ty::Slice(_) => Res::Primitive(Slice),
651+
//ty::RawPtr(_) => Res::Primitive(RawPointer),
652+
//ty::Ref(..) => Res::Primitive(Reference),
653+
//ty::FnDef(..) => panic!("type alias to a function definition"),
654+
//ty::FnPtr(_) => Res::Primitive(Fn),
655+
_ => return None,
656+
}))
657+
}
658+
621659
/// Returns:
622660
/// - None if no associated item was found
623661
/// - Some((_, _, Some(_))) if an item was found and should go through a side channel
@@ -632,7 +670,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
632670
let tcx = self.cx.tcx;
633671

634672
match root_res {
635-
Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name),
673+
Res::Primitive(prim) => {
674+
self.resolve_primitive_associated_item(prim, ns, item_name).or_else(|| {
675+
let assoc_item = self
676+
.primitive_type_to_ty(prim)
677+
.map(|ty| {
678+
resolve_associated_trait_item(ty, module_id, item_name, ns, self.cx)
679+
})
680+
.flatten();
681+
682+
assoc_item.map(|item| {
683+
let kind = item.kind;
684+
let fragment = UrlFragment::from_assoc_item(item_name, kind, false);
685+
// HACK(jynelson): `clean` expects the type, not the associated item
686+
// but the disambiguator logic expects the associated item.
687+
// Store the kind in a side channel so that only the disambiguator logic looks at it.
688+
(root_res, fragment, Some((kind.as_def_kind(), item.def_id)))
689+
})
690+
})
691+
}
636692
Res::Def(DefKind::TyAlias, did) => {
637693
// Resolve the link on the type the alias points to.
638694
// FIXME: if the associated item is defined directly on the type alias,
@@ -666,8 +722,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
666722
// To handle that properly resolve() would have to support
667723
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
668724
.or_else(|| {
669-
let item =
670-
resolve_associated_trait_item(did, module_id, item_name, ns, self.cx);
725+
let item = resolve_associated_trait_item(
726+
tcx.type_of(did),
727+
module_id,
728+
item_name,
729+
ns,
730+
self.cx,
731+
);
671732
debug!("got associated item {:?}", item);
672733
item
673734
});
@@ -767,20 +828,20 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
767828
/// Given `[std::io::Error::source]`, where `source` is unresolved, this would
768829
/// find `std::error::Error::source` and return
769830
/// `<io::Error as error::Error>::source`.
770-
fn resolve_associated_trait_item(
771-
did: DefId,
831+
fn resolve_associated_trait_item<'a>(
832+
ty: Ty<'a>,
772833
module: DefId,
773834
item_name: Symbol,
774835
ns: Namespace,
775-
cx: &mut DocContext<'_>,
836+
cx: &mut DocContext<'a>,
776837
) -> Option<ty::AssocItem> {
777838
// FIXME: this should also consider blanket impls (`impl<T> X for T`). Unfortunately
778839
// `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the
779840
// meantime, just don't look for these blanket impls.
780841

781842
// Next consider explicit impls: `impl MyTrait for MyType`
782843
// Give precedence to inherent impls.
783-
let traits = traits_implemented_by(cx, did, module);
844+
let traits = traits_implemented_by(cx, ty, module);
784845
debug!("considering traits {:?}", traits);
785846
let mut candidates = traits.iter().filter_map(|&trait_| {
786847
cx.tcx.associated_items(trait_).find_by_name_and_namespace(
@@ -799,7 +860,11 @@ fn resolve_associated_trait_item(
799860
///
800861
/// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
801862
/// So it is not stable to serialize cross-crate.
802-
fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet<DefId> {
863+
fn traits_implemented_by<'a>(
864+
cx: &mut DocContext<'a>,
865+
ty: Ty<'a>,
866+
module: DefId,
867+
) -> FxHashSet<DefId> {
803868
let mut resolver = cx.resolver.borrow_mut();
804869
let in_scope_traits = cx.module_trait_cache.entry(module).or_insert_with(|| {
805870
resolver.access(|resolver| {
@@ -813,7 +878,6 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -
813878
});
814879

815880
let tcx = cx.tcx;
816-
let ty = tcx.type_of(type_);
817881
let iter = in_scope_traits.iter().flat_map(|&trait_| {
818882
trace!("considering explicit impl for trait {:?}", trait_);
819883

@@ -826,19 +890,10 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -
826890
"comparing type {} with kind {:?} against type {:?}",
827891
impl_type,
828892
impl_type.kind(),
829-
type_
893+
ty
830894
);
831895
// Fast path: if this is a primitive simple `==` will work
832-
let saw_impl = impl_type == ty
833-
|| match impl_type.kind() {
834-
// Check if these are the same def_id
835-
ty::Adt(def, _) => {
836-
debug!("adt def_id: {:?}", def.did);
837-
def.did == type_
838-
}
839-
ty::Foreign(def_id) => *def_id == type_,
840-
_ => false,
841-
};
896+
let saw_impl = impl_type == ty;
842897

843898
if saw_impl { Some(trait_) } else { None }
844899
})

src/test/rustdoc-ui/intra-doc/non-path-primitives.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
//! [unit::eq] //~ ERROR unresolved
2929
//! [tuple::eq] //~ ERROR unresolved
3030
//! [fn::eq] //~ ERROR unresolved
31-
//! [never::eq] //~ ERROR unresolved
3231
3332
// FIXME(#78800): This breaks because it's a blanket impl
3433
// (I think? Might break for other reasons too.)

src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,11 @@ error: unresolved link to `fn::eq`
5353
LL | //! [fn::eq]
5454
| ^^^^^^ the builtin type `fn` has no associated item named `eq`
5555

56-
error: unresolved link to `never::eq`
57-
--> $DIR/non-path-primitives.rs:31:6
58-
|
59-
LL | //! [never::eq]
60-
| ^^^^^^^^^ the builtin type `never` has no associated item named `eq`
61-
6256
error: unresolved link to `reference::deref`
63-
--> $DIR/non-path-primitives.rs:35:6
57+
--> $DIR/non-path-primitives.rs:34:6
6458
|
6559
LL | //! [reference::deref]
6660
| ^^^^^^^^^^^^^^^^ the builtin type `reference` has no associated item named `deref`
6761

68-
error: aborting due to 9 previous errors
62+
error: aborting due to 8 previous errors
6963

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use std::{num::ParseFloatError, str::FromStr};
2+
3+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.from_str"]' 'f64::from_str()'
4+
/// Uses the rules from [`f64::from_str()`].
5+
pub struct Number {
6+
pub value: f64,
7+
}

0 commit comments

Comments
 (0)