Skip to content

Commit 26a90e4

Browse files
authored
Rollup merge of #92443 - mdibaiee:90703/resolve-traits-of-primitive-types, r=Manishearth
Rustdoc: resolve associated traits for non-generic primitive types Fixes #90703 This seems to work: <img width="457" alt="image" src="https://user-images.githubusercontent.com/2807772/147774059-9556ff96-4519-409e-8ed0-c33ecc436171.png"> I'm just afraid I might have missed some cases / broken previous functionality. I also have not written tests yet, I will have to take a look to see where tests are and how they are structured, but any help there is also appreciated.
2 parents b510278 + 973cf63 commit 26a90e4

File tree

4 files changed

+119
-30
lines changed

4 files changed

+119
-30
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+71-21
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,39 @@ 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+
// FIXME: Only simple types are supported here, see if we can support
629+
// other types such as Tuple, Array, Slice, etc.
630+
// See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455
631+
Some(tcx.mk_ty(match prim {
632+
Bool => ty::Bool,
633+
Str => ty::Str,
634+
Char => ty::Char,
635+
Never => ty::Never,
636+
I8 => ty::Int(ty::IntTy::I8),
637+
I16 => ty::Int(ty::IntTy::I16),
638+
I32 => ty::Int(ty::IntTy::I32),
639+
I64 => ty::Int(ty::IntTy::I64),
640+
I128 => ty::Int(ty::IntTy::I128),
641+
Isize => ty::Int(ty::IntTy::Isize),
642+
F32 => ty::Float(ty::FloatTy::F32),
643+
F64 => ty::Float(ty::FloatTy::F64),
644+
U8 => ty::Uint(ty::UintTy::U8),
645+
U16 => ty::Uint(ty::UintTy::U16),
646+
U32 => ty::Uint(ty::UintTy::U32),
647+
U64 => ty::Uint(ty::UintTy::U64),
648+
U128 => ty::Uint(ty::UintTy::U128),
649+
Usize => ty::Uint(ty::UintTy::Usize),
650+
_ => return None,
651+
}))
652+
}
653+
621654
/// Returns:
622655
/// - None if no associated item was found
623656
/// - Some((_, _, Some(_))) if an item was found and should go through a side channel
@@ -632,7 +665,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
632665
let tcx = self.cx.tcx;
633666

634667
match root_res {
635-
Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name),
668+
Res::Primitive(prim) => {
669+
self.resolve_primitive_associated_item(prim, ns, item_name).or_else(|| {
670+
let assoc_item = self
671+
.primitive_type_to_ty(prim)
672+
.map(|ty| {
673+
resolve_associated_trait_item(ty, module_id, item_name, ns, self.cx)
674+
})
675+
.flatten();
676+
677+
assoc_item.map(|item| {
678+
let kind = item.kind;
679+
let fragment = UrlFragment::from_assoc_item(item_name, kind, false);
680+
// HACK(jynelson): `clean` expects the type, not the associated item
681+
// but the disambiguator logic expects the associated item.
682+
// Store the kind in a side channel so that only the disambiguator logic looks at it.
683+
(root_res, fragment, Some((kind.as_def_kind(), item.def_id)))
684+
})
685+
})
686+
}
636687
Res::Def(DefKind::TyAlias, did) => {
637688
// Resolve the link on the type the alias points to.
638689
// FIXME: if the associated item is defined directly on the type alias,
@@ -666,8 +717,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
666717
// To handle that properly resolve() would have to support
667718
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
668719
.or_else(|| {
669-
let item =
670-
resolve_associated_trait_item(did, module_id, item_name, ns, self.cx);
720+
let item = resolve_associated_trait_item(
721+
tcx.type_of(did),
722+
module_id,
723+
item_name,
724+
ns,
725+
self.cx,
726+
);
671727
debug!("got associated item {:?}", item);
672728
item
673729
});
@@ -767,20 +823,20 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
767823
/// Given `[std::io::Error::source]`, where `source` is unresolved, this would
768824
/// find `std::error::Error::source` and return
769825
/// `<io::Error as error::Error>::source`.
770-
fn resolve_associated_trait_item(
771-
did: DefId,
826+
fn resolve_associated_trait_item<'a>(
827+
ty: Ty<'a>,
772828
module: DefId,
773829
item_name: Symbol,
774830
ns: Namespace,
775-
cx: &mut DocContext<'_>,
831+
cx: &mut DocContext<'a>,
776832
) -> Option<ty::AssocItem> {
777833
// FIXME: this should also consider blanket impls (`impl<T> X for T`). Unfortunately
778834
// `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the
779835
// meantime, just don't look for these blanket impls.
780836

781837
// Next consider explicit impls: `impl MyTrait for MyType`
782838
// Give precedence to inherent impls.
783-
let traits = traits_implemented_by(cx, did, module);
839+
let traits = traits_implemented_by(cx, ty, module);
784840
debug!("considering traits {:?}", traits);
785841
let mut candidates = traits.iter().filter_map(|&trait_| {
786842
cx.tcx.associated_items(trait_).find_by_name_and_namespace(
@@ -799,7 +855,11 @@ fn resolve_associated_trait_item(
799855
///
800856
/// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
801857
/// So it is not stable to serialize cross-crate.
802-
fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet<DefId> {
858+
fn traits_implemented_by<'a>(
859+
cx: &mut DocContext<'a>,
860+
ty: Ty<'a>,
861+
module: DefId,
862+
) -> FxHashSet<DefId> {
803863
let mut resolver = cx.resolver.borrow_mut();
804864
let in_scope_traits = cx.module_trait_cache.entry(module).or_insert_with(|| {
805865
resolver.access(|resolver| {
@@ -813,7 +873,6 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -
813873
});
814874

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

@@ -826,19 +885,10 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -
826885
"comparing type {} with kind {:?} against type {:?}",
827886
impl_type,
828887
impl_type.kind(),
829-
type_
888+
ty
830889
);
831890
// 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-
};
891+
let saw_impl = impl_type == ty;
842892

843893
if saw_impl { Some(trait_) } else { None }
844894
})

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

-1
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

+2-8
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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![feature(never_type)]
2+
use std::str::FromStr;
3+
4+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.f64.html#method.from_str"]' 'f64::from_str()'
5+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.f32.html#method.from_str"]' 'f32::from_str()'
6+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.isize.html#method.from_str"]' 'isize::from_str()'
7+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.i8.html#method.from_str"]' 'i8::from_str()'
8+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.i16.html#method.from_str"]' 'i16::from_str()'
9+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.i32.html#method.from_str"]' 'i32::from_str()'
10+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.i64.html#method.from_str"]' 'i64::from_str()'
11+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.i128.html#method.from_str"]' 'i128::from_str()'
12+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.usize.html#method.from_str"]' 'usize::from_str()'
13+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.u8.html#method.from_str"]' 'u8::from_str()'
14+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.u16.html#method.from_str"]' 'u16::from_str()'
15+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.u32.html#method.from_str"]' 'u32::from_str()'
16+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.u64.html#method.from_str"]' 'u64::from_str()'
17+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.u128.html#method.from_str"]' 'u128::from_str()'
18+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.char.html#method.from_str"]' 'char::from_str()'
19+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.bool.html#method.from_str"]' 'bool::from_str()'
20+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.str.html#method.eq"]' 'str::eq()'
21+
// @has 'prim_associated_traits/struct.Number.html' '//a[@href="{{channel}}/std/primitive.never.html#method.eq"]' 'never::eq()'
22+
/// [`f64::from_str()`] [`f32::from_str()`] [`isize::from_str()`] [`i8::from_str()`]
23+
/// [`i16::from_str()`] [`i32::from_str()`] [`i64::from_str()`] [`i128::from_str()`]
24+
/// [`u16::from_str()`] [`u32::from_str()`] [`u64::from_str()`] [`u128::from_str()`]
25+
/// [`usize::from_str()`] [`u8::from_str()`] [`char::from_str()`] [`bool::from_str()`]
26+
/// [`str::eq()`] [`never::eq()`]
27+
pub struct Number {
28+
pub f_64: f64,
29+
pub f_32: f32,
30+
pub i_size: isize,
31+
pub i_8: i8,
32+
pub i_16: i16,
33+
pub i_32: i32,
34+
pub i_64: i64,
35+
pub i_128: i128,
36+
pub u_size: usize,
37+
pub u_8: u8,
38+
pub u_16: u16,
39+
pub u_32: u32,
40+
pub u_64: u64,
41+
pub u_128: u128,
42+
pub ch: char,
43+
pub boolean: bool,
44+
pub string: str,
45+
pub n: !,
46+
}

0 commit comments

Comments
 (0)