Skip to content

Commit 61e1eda

Browse files
committed
IAT: Rustdoc integration
1 parent 46ec348 commit 61e1eda

File tree

11 files changed

+183
-40
lines changed

11 files changed

+183
-40
lines changed

src/librustdoc/clean/auto_trait.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,10 @@ where
556556
WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
557557
match *lhs {
558558
Type::QPath(box QPathData {
559-
ref assoc, ref self_type, ref trait_, ..
559+
ref assoc,
560+
ref self_type,
561+
trait_: Some(ref trait_),
562+
..
560563
}) => {
561564
let ty = &*self_type;
562565
let mut new_trait = trait_.clone();

src/librustdoc/clean/inline.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
706706

707707
g.where_predicates.retain(|pred| match pred {
708708
clean::WherePredicate::BoundPredicate {
709-
ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }),
709+
ty:
710+
clean::QPath(box clean::QPathData {
711+
self_type: clean::Generic(ref s),
712+
trait_: Some(trait_),
713+
..
714+
}),
710715
bounds,
711716
..
712717
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),

src/librustdoc/clean/mod.rs

+45-15
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ fn clean_projection<'tcx>(
441441
assoc: projection_to_path_segment(ty, cx),
442442
should_show_cast,
443443
self_type,
444-
trait_,
444+
trait_: Some(trait_),
445445
}))
446446
}
447447

@@ -1330,7 +1330,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
13301330
let mut bounds: Vec<GenericBound> = Vec::new();
13311331
generics.where_predicates.retain_mut(|pred| match *pred {
13321332
WherePredicate::BoundPredicate {
1333-
ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }),
1333+
ty:
1334+
QPath(box QPathData {
1335+
ref assoc,
1336+
ref self_type,
1337+
trait_: Some(ref trait_),
1338+
..
1339+
}),
13341340
bounds: ref mut pred_bounds,
13351341
..
13361342
} => {
@@ -1492,25 +1498,30 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
14921498
assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx),
14931499
should_show_cast,
14941500
self_type,
1495-
trait_,
1501+
trait_: Some(trait_),
14961502
}))
14971503
}
14981504
hir::QPath::TypeRelative(qself, segment) => {
14991505
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
1500-
let res = match ty.kind() {
1506+
let self_type = clean_ty(qself, cx);
1507+
1508+
let (trait_, should_show_cast) = match ty.kind() {
15011509
ty::Alias(ty::Projection, proj) => {
1502-
Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id)
1510+
let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id);
1511+
let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx);
1512+
register_res(cx, trait_.res);
1513+
let self_def_id = res.opt_def_id();
1514+
let should_show_cast =
1515+
compute_should_show_cast(self_def_id, &trait_, &self_type);
1516+
1517+
(Some(trait_), should_show_cast)
15031518
}
1519+
ty::Alias(ty::Inherent, _) => (None, false),
15041520
// Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s.
15051521
ty::Error(_) => return Type::Infer,
1506-
// Otherwise, this is an inherent associated type.
1507-
_ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None),
1522+
_ => bug!("clean: expected associated type, found `{ty:?}`"),
15081523
};
1509-
let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx);
1510-
register_res(cx, trait_.res);
1511-
let self_def_id = res.opt_def_id();
1512-
let self_type = clean_ty(qself, cx);
1513-
let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type);
1524+
15141525
Type::QPath(Box::new(QPathData {
15151526
assoc: clean_path_segment(segment, cx),
15161527
should_show_cast,
@@ -1836,9 +1847,28 @@ pub(crate) fn clean_middle_ty<'tcx>(
18361847
clean_projection(bound_ty.rebind(*data), cx, parent_def_id)
18371848
}
18381849

1839-
// FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in
1840-
// `QPathData` optional or alternatively adding a new `clean::Type` variant.
1841-
ty::Alias(ty::Inherent, _data) => Type::Infer,
1850+
ty::Alias(ty::Inherent, alias_ty) => {
1851+
let alias_ty = bound_ty.rebind(alias_ty);
1852+
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None);
1853+
1854+
Type::QPath(Box::new(QPathData {
1855+
assoc: PathSegment {
1856+
name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name,
1857+
args: GenericArgs::AngleBracketed {
1858+
args: substs_to_args(
1859+
cx,
1860+
alias_ty.map_bound(|ty| ty.substs.as_slice()),
1861+
true,
1862+
)
1863+
.into(),
1864+
bindings: Default::default(),
1865+
},
1866+
},
1867+
should_show_cast: false,
1868+
self_type,
1869+
trait_: None,
1870+
}))
1871+
}
18421872

18431873
ty::Param(ref p) => {
18441874
if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {

src/librustdoc/clean/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1660,7 +1660,7 @@ impl Type {
16601660

16611661
pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
16621662
if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self {
1663-
Some((self_type, trait_.def_id(), assoc.clone()))
1663+
Some((self_type, trait_.as_ref()?.def_id(), assoc.clone()))
16641664
} else {
16651665
None
16661666
}
@@ -1704,7 +1704,7 @@ pub(crate) struct QPathData {
17041704
pub self_type: Type,
17051705
/// FIXME: compute this field on demand.
17061706
pub should_show_cast: bool,
1707-
pub trait_: Path,
1707+
pub trait_: Option<Path>,
17081708
}
17091709

17101710
/// A primitive (aka, builtin) type.

src/librustdoc/html/format.rs

+35-11
Original file line numberDiff line numberDiff line change
@@ -1116,14 +1116,17 @@ fn fmt_type<'cx>(
11161116
ref trait_,
11171117
should_show_cast,
11181118
}) => {
1119+
// FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719),
1120+
// we need to surround them with angle brackets in some cases (e.g. `<dyn …>::P`).
1121+
11191122
if f.alternate() {
1120-
if should_show_cast {
1123+
if let Some(trait_) = trait_ && should_show_cast {
11211124
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
11221125
} else {
11231126
write!(f, "{:#}::", self_type.print(cx))?
11241127
}
11251128
} else {
1126-
if should_show_cast {
1129+
if let Some(trait_) = trait_ && should_show_cast {
11271130
write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
11281131
} else {
11291132
write!(f, "{}::", self_type.print(cx))?
@@ -1139,15 +1142,36 @@ fn fmt_type<'cx>(
11391142
// the ugliness comes from inlining across crates where
11401143
// everything comes in as a fully resolved QPath (hard to
11411144
// look at).
1142-
if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) {
1143-
write!(
1144-
f,
1145-
"<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
1146-
title=\"type {path}::{name}\">{name}</a>",
1147-
shortty = ItemType::AssocType,
1148-
name = assoc.name,
1149-
path = join_with_double_colon(&path),
1150-
)
1145+
if !f.alternate() {
1146+
// FIXME(inherent_associated_types): We always link to the very first associated
1147+
// type (in respect to source order) that bears the given name (`assoc.name`) and that is
1148+
// affiliated with the computed `DefId`. This is obviously incorrect when we have
1149+
// multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself
1150+
// through here and map it to the corresponding HTML ID that was generated by
1151+
// `render::Context::derive_id` when the impl blocks were rendered.
1152+
// There is no such mapping unfortunately.
1153+
// As a hack, we could badly imitate `derive_id` here by keeping *count* when looking
1154+
// for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()`
1155+
// considering privacy, `doc(hidden)`, etc.
1156+
// I don't feel like that right now :cold_sweat:.
1157+
1158+
let parent_href = match trait_ {
1159+
Some(trait_) => href(trait_.def_id(), cx).ok(),
1160+
None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
1161+
};
1162+
1163+
if let Some((url, _, path)) = parent_href {
1164+
write!(
1165+
f,
1166+
"<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
1167+
title=\"type {path}::{name}\">{name}</a>",
1168+
shortty = ItemType::AssocType,
1169+
name = assoc.name,
1170+
path = join_with_double_colon(&path),
1171+
)
1172+
} else {
1173+
write!(f, "{}", assoc.name)
1174+
}
11511175
} else {
11521176
write!(f, "{}", assoc.name)
11531177
}?;

src/librustdoc/html/render/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2202,7 +2202,9 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
22022202
}
22032203
clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
22042204
work.push_back(self_type);
2205-
process_path(trait_.def_id());
2205+
if let Some(trait_) = trait_ {
2206+
process_path(trait_.def_id());
2207+
}
22062208
}
22072209
_ => {}
22082210
}

src/librustdoc/json/conversions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ impl FromWithTcx<clean::Type> for Type {
574574
name: assoc.name.to_string(),
575575
args: Box::new(assoc.args.into_tcx(tcx)),
576576
self_type: Box::new(self_type.into_tcx(tcx)),
577-
trait_: trait_.into_tcx(tcx),
577+
trait_: trait_.map(|trait_| trait_.into_tcx(tcx)),
578578
},
579579
}
580580
}

src/rustdoc-json-types/lib.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
88
use std::path::PathBuf;
99

1010
/// rustdoc format-version.
11-
pub const FORMAT_VERSION: u32 = 24;
11+
pub const FORMAT_VERSION: u32 = 25;
1212

1313
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
1414
/// about the language items in the local crate, as well as info about external items to allow
@@ -581,13 +581,15 @@ pub enum Type {
581581
#[serde(rename = "type")]
582582
type_: Box<Type>,
583583
},
584-
/// `<Type as Trait>::Name` or associated types like `T::Item` where `T: Iterator`
584+
/// Associated types like `<Type as Trait>::Name` and `T::Item` where
585+
/// `T: Iterator` or inherent associated types like `Struct::Name`.
585586
QualifiedPath {
586587
name: String,
587588
args: Box<GenericArgs>,
588589
self_type: Box<Type>,
590+
/// `None` iff this is an *inherent* associated type.
589591
#[serde(rename = "trait")]
590-
trait_: Path,
592+
trait_: Option<Path>,
591593
},
592594
}
593595

src/tools/jsondoclint/src/validator.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,9 @@ impl<'a> Validator<'a> {
273273
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
274274
self.check_generic_args(&**args);
275275
self.check_type(&**self_type);
276-
self.check_path(trait_, PathKind::Trait);
276+
if let Some(trait_) = trait_ {
277+
self.check_path(trait_, PathKind::Trait);
278+
}
277279
}
278280
}
279281
}

tests/rustdoc/inherent-projections.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,44 @@
11
#![feature(inherent_associated_types)]
22
#![allow(incomplete_features)]
33

4-
// FIXME(fmease): Properly render inherent projections.
5-
6-
// @has inherent_projections/fn.create.html
7-
// @has - '//pre[@class="rust item-decl"]' "create() -> _"
4+
// @has 'inherent_projections/fn.create.html'
5+
// @has - '//pre[@class="rust item-decl"]' "create() -> Owner::Metadata"
6+
// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Owner.html#associatedtype.Metadata'
87
pub fn create() -> Owner::Metadata {}
98

109
pub struct Owner;
1110

1211
impl Owner {
1312
pub type Metadata = ();
1413
}
14+
15+
// Make sure we handle bound vars correctly.
16+
// @has 'inherent_projections/type.User.html' '//pre[@class="rust item-decl"]' "for<'a> fn(_: Carrier<'a>::Focus)"
17+
pub type User = for<'a> fn(Carrier<'a>::Focus);
18+
19+
pub struct Carrier<'a>(&'a ());
20+
21+
impl<'a> Carrier<'a> {
22+
pub type Focus = &'a mut i32;
23+
}
24+
25+
////////////////////////////////////////
26+
27+
// FIXME(inherent_associated_types): Below we link to `Proj` but we should link to `Proj-1`.
28+
// The current test checks for the buggy behavior for demonstration purposes.
29+
30+
// @has 'inherent_projections/type.Test.html'
31+
// @has - '//pre[@class="rust item-decl"]' "Parametrized<i32>"
32+
// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj'
33+
// @!has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj-1'
34+
pub type Test = Parametrized<i32>::Proj;
35+
36+
pub struct Parametrized<T>(T);
37+
38+
impl Parametrized<bool> {
39+
pub type Proj = ();
40+
}
41+
42+
impl Parametrized<i32> {
43+
pub type Proj = String;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![feature(inherent_associated_types)]
2+
3+
#![allow(incomplete_features)]
4+
#![deny(rustdoc::broken_intra_doc_links)]
5+
6+
// @has inherent_associated_types/index.html
7+
8+
// @has - '//a/@href' 'enum.Simple.html#associatedtype.Type'
9+
//! [`Simple::Type`]
10+
11+
pub enum Simple {}
12+
13+
impl Simple {
14+
pub type Type = ();
15+
}
16+
17+
////////////////////////////////////////
18+
19+
// @has 'inherent_associated_types/type.Test0.html' '//a/@href' \
20+
// 'struct.Parametrized.html#associatedtype.Proj'
21+
/// [`Parametrized<bool>::Proj`]
22+
pub type Test0 = ();
23+
24+
// FIXME(inherent_associated_types): The intra-doc link below should point to `Proj-1` not `Proj`.
25+
// The current test checks for the buggy behavior for demonstration purposes.
26+
// The same bug happens for inherent associated functions and constants (see #85960, #93398).
27+
//
28+
// Further, at some point we should reject the intra-doc link `Parametrized::Proj`.
29+
// It currently links to `Parametrized<bool>::Proj`.
30+
31+
// @has 'inherent_associated_types/type.Test1.html'
32+
// @has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj'
33+
// @!has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj-1'
34+
/// [`Parametrized<i32>::Proj`]
35+
pub type Test1 = ();
36+
37+
pub struct Parametrized<T>(T);
38+
39+
impl Parametrized<bool> {
40+
pub type Proj = ();
41+
}
42+
43+
impl Parametrized<i32> {
44+
pub type Proj = String;
45+
}

0 commit comments

Comments
 (0)