IAT: Rustdoc integration
This commit is contained in:
parent
46ec348611
commit
61e1eda6db
@ -556,7 +556,10 @@ where
|
|||||||
WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
|
WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
|
||||||
match *lhs {
|
match *lhs {
|
||||||
Type::QPath(box QPathData {
|
Type::QPath(box QPathData {
|
||||||
ref assoc, ref self_type, ref trait_, ..
|
ref assoc,
|
||||||
|
ref self_type,
|
||||||
|
trait_: Some(ref trait_),
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let ty = &*self_type;
|
let ty = &*self_type;
|
||||||
let mut new_trait = trait_.clone();
|
let mut new_trait = trait_.clone();
|
||||||
|
@ -706,7 +706,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
|
|||||||
|
|
||||||
g.where_predicates.retain(|pred| match pred {
|
g.where_predicates.retain(|pred| match pred {
|
||||||
clean::WherePredicate::BoundPredicate {
|
clean::WherePredicate::BoundPredicate {
|
||||||
ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }),
|
ty:
|
||||||
|
clean::QPath(box clean::QPathData {
|
||||||
|
self_type: clean::Generic(ref s),
|
||||||
|
trait_: Some(trait_),
|
||||||
|
..
|
||||||
|
}),
|
||||||
bounds,
|
bounds,
|
||||||
..
|
..
|
||||||
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
|
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
|
||||||
|
@ -441,7 +441,7 @@ fn clean_projection<'tcx>(
|
|||||||
assoc: projection_to_path_segment(ty, cx),
|
assoc: projection_to_path_segment(ty, cx),
|
||||||
should_show_cast,
|
should_show_cast,
|
||||||
self_type,
|
self_type,
|
||||||
trait_,
|
trait_: Some(trait_),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1330,7 +1330,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
|||||||
let mut bounds: Vec<GenericBound> = Vec::new();
|
let mut bounds: Vec<GenericBound> = Vec::new();
|
||||||
generics.where_predicates.retain_mut(|pred| match *pred {
|
generics.where_predicates.retain_mut(|pred| match *pred {
|
||||||
WherePredicate::BoundPredicate {
|
WherePredicate::BoundPredicate {
|
||||||
ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }),
|
ty:
|
||||||
|
QPath(box QPathData {
|
||||||
|
ref assoc,
|
||||||
|
ref self_type,
|
||||||
|
trait_: Some(ref trait_),
|
||||||
|
..
|
||||||
|
}),
|
||||||
bounds: ref mut pred_bounds,
|
bounds: ref mut pred_bounds,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
@ -1492,25 +1498,30 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
|
|||||||
assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx),
|
assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx),
|
||||||
should_show_cast,
|
should_show_cast,
|
||||||
self_type,
|
self_type,
|
||||||
trait_,
|
trait_: Some(trait_),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
hir::QPath::TypeRelative(qself, segment) => {
|
hir::QPath::TypeRelative(qself, segment) => {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||||
let res = match ty.kind() {
|
let self_type = clean_ty(qself, cx);
|
||||||
|
|
||||||
|
let (trait_, should_show_cast) = match ty.kind() {
|
||||||
ty::Alias(ty::Projection, proj) => {
|
ty::Alias(ty::Projection, proj) => {
|
||||||
Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id)
|
let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id);
|
||||||
|
let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx);
|
||||||
|
register_res(cx, trait_.res);
|
||||||
|
let self_def_id = res.opt_def_id();
|
||||||
|
let should_show_cast =
|
||||||
|
compute_should_show_cast(self_def_id, &trait_, &self_type);
|
||||||
|
|
||||||
|
(Some(trait_), should_show_cast)
|
||||||
}
|
}
|
||||||
|
ty::Alias(ty::Inherent, _) => (None, false),
|
||||||
// Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s.
|
// Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s.
|
||||||
ty::Error(_) => return Type::Infer,
|
ty::Error(_) => return Type::Infer,
|
||||||
// Otherwise, this is an inherent associated type.
|
_ => bug!("clean: expected associated type, found `{ty:?}`"),
|
||||||
_ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None),
|
|
||||||
};
|
};
|
||||||
let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx);
|
|
||||||
register_res(cx, trait_.res);
|
|
||||||
let self_def_id = res.opt_def_id();
|
|
||||||
let self_type = clean_ty(qself, cx);
|
|
||||||
let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type);
|
|
||||||
Type::QPath(Box::new(QPathData {
|
Type::QPath(Box::new(QPathData {
|
||||||
assoc: clean_path_segment(segment, cx),
|
assoc: clean_path_segment(segment, cx),
|
||||||
should_show_cast,
|
should_show_cast,
|
||||||
@ -1836,9 +1847,28 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
|||||||
clean_projection(bound_ty.rebind(*data), cx, parent_def_id)
|
clean_projection(bound_ty.rebind(*data), cx, parent_def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in
|
ty::Alias(ty::Inherent, alias_ty) => {
|
||||||
// `QPathData` optional or alternatively adding a new `clean::Type` variant.
|
let alias_ty = bound_ty.rebind(alias_ty);
|
||||||
ty::Alias(ty::Inherent, _data) => Type::Infer,
|
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None);
|
||||||
|
|
||||||
|
Type::QPath(Box::new(QPathData {
|
||||||
|
assoc: PathSegment {
|
||||||
|
name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name,
|
||||||
|
args: GenericArgs::AngleBracketed {
|
||||||
|
args: substs_to_args(
|
||||||
|
cx,
|
||||||
|
alias_ty.map_bound(|ty| ty.substs.as_slice()),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
bindings: Default::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
should_show_cast: false,
|
||||||
|
self_type,
|
||||||
|
trait_: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
ty::Param(ref p) => {
|
ty::Param(ref p) => {
|
||||||
if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {
|
if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {
|
||||||
|
@ -1660,7 +1660,7 @@ impl Type {
|
|||||||
|
|
||||||
pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
|
pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
|
||||||
if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self {
|
if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self {
|
||||||
Some((self_type, trait_.def_id(), assoc.clone()))
|
Some((self_type, trait_.as_ref()?.def_id(), assoc.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -1704,7 +1704,7 @@ pub(crate) struct QPathData {
|
|||||||
pub self_type: Type,
|
pub self_type: Type,
|
||||||
/// FIXME: compute this field on demand.
|
/// FIXME: compute this field on demand.
|
||||||
pub should_show_cast: bool,
|
pub should_show_cast: bool,
|
||||||
pub trait_: Path,
|
pub trait_: Option<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A primitive (aka, builtin) type.
|
/// A primitive (aka, builtin) type.
|
||||||
|
@ -1116,14 +1116,17 @@ fn fmt_type<'cx>(
|
|||||||
ref trait_,
|
ref trait_,
|
||||||
should_show_cast,
|
should_show_cast,
|
||||||
}) => {
|
}) => {
|
||||||
|
// FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719),
|
||||||
|
// we need to surround them with angle brackets in some cases (e.g. `<dyn …>::P`).
|
||||||
|
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
if should_show_cast {
|
if let Some(trait_) = trait_ && should_show_cast {
|
||||||
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
|
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{:#}::", self_type.print(cx))?
|
write!(f, "{:#}::", self_type.print(cx))?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if should_show_cast {
|
if let Some(trait_) = trait_ && should_show_cast {
|
||||||
write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))?
|
write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))?
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}::", self_type.print(cx))?
|
write!(f, "{}::", self_type.print(cx))?
|
||||||
@ -1139,15 +1142,36 @@ fn fmt_type<'cx>(
|
|||||||
// the ugliness comes from inlining across crates where
|
// the ugliness comes from inlining across crates where
|
||||||
// everything comes in as a fully resolved QPath (hard to
|
// everything comes in as a fully resolved QPath (hard to
|
||||||
// look at).
|
// look at).
|
||||||
if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) {
|
if !f.alternate() {
|
||||||
write!(
|
// FIXME(inherent_associated_types): We always link to the very first associated
|
||||||
f,
|
// type (in respect to source order) that bears the given name (`assoc.name`) and that is
|
||||||
"<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
|
// affiliated with the computed `DefId`. This is obviously incorrect when we have
|
||||||
title=\"type {path}::{name}\">{name}</a>",
|
// multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself
|
||||||
shortty = ItemType::AssocType,
|
// through here and map it to the corresponding HTML ID that was generated by
|
||||||
name = assoc.name,
|
// `render::Context::derive_id` when the impl blocks were rendered.
|
||||||
path = join_with_double_colon(&path),
|
// There is no such mapping unfortunately.
|
||||||
)
|
// As a hack, we could badly imitate `derive_id` here by keeping *count* when looking
|
||||||
|
// for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()`
|
||||||
|
// considering privacy, `doc(hidden)`, etc.
|
||||||
|
// I don't feel like that right now :cold_sweat:.
|
||||||
|
|
||||||
|
let parent_href = match trait_ {
|
||||||
|
Some(trait_) => href(trait_.def_id(), cx).ok(),
|
||||||
|
None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((url, _, path)) = parent_href {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
|
||||||
|
title=\"type {path}::{name}\">{name}</a>",
|
||||||
|
shortty = ItemType::AssocType,
|
||||||
|
name = assoc.name,
|
||||||
|
path = join_with_double_colon(&path),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", assoc.name)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}", assoc.name)
|
write!(f, "{}", assoc.name)
|
||||||
}?;
|
}?;
|
||||||
|
@ -2202,7 +2202,9 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
|
clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
|
||||||
work.push_back(self_type);
|
work.push_back(self_type);
|
||||||
process_path(trait_.def_id());
|
if let Some(trait_) = trait_ {
|
||||||
|
process_path(trait_.def_id());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -574,7 +574,7 @@ impl FromWithTcx<clean::Type> for Type {
|
|||||||
name: assoc.name.to_string(),
|
name: assoc.name.to_string(),
|
||||||
args: Box::new(assoc.args.into_tcx(tcx)),
|
args: Box::new(assoc.args.into_tcx(tcx)),
|
||||||
self_type: Box::new(self_type.into_tcx(tcx)),
|
self_type: Box::new(self_type.into_tcx(tcx)),
|
||||||
trait_: trait_.into_tcx(tcx),
|
trait_: trait_.map(|trait_| trait_.into_tcx(tcx)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// rustdoc format-version.
|
/// rustdoc format-version.
|
||||||
pub const FORMAT_VERSION: u32 = 24;
|
pub const FORMAT_VERSION: u32 = 25;
|
||||||
|
|
||||||
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
|
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
|
||||||
/// about the language items in the local crate, as well as info about external items to allow
|
/// about the language items in the local crate, as well as info about external items to allow
|
||||||
@ -581,13 +581,15 @@ pub enum Type {
|
|||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
type_: Box<Type>,
|
type_: Box<Type>,
|
||||||
},
|
},
|
||||||
/// `<Type as Trait>::Name` or associated types like `T::Item` where `T: Iterator`
|
/// Associated types like `<Type as Trait>::Name` and `T::Item` where
|
||||||
|
/// `T: Iterator` or inherent associated types like `Struct::Name`.
|
||||||
QualifiedPath {
|
QualifiedPath {
|
||||||
name: String,
|
name: String,
|
||||||
args: Box<GenericArgs>,
|
args: Box<GenericArgs>,
|
||||||
self_type: Box<Type>,
|
self_type: Box<Type>,
|
||||||
|
/// `None` iff this is an *inherent* associated type.
|
||||||
#[serde(rename = "trait")]
|
#[serde(rename = "trait")]
|
||||||
trait_: Path,
|
trait_: Option<Path>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,9 @@ impl<'a> Validator<'a> {
|
|||||||
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
|
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
|
||||||
self.check_generic_args(&**args);
|
self.check_generic_args(&**args);
|
||||||
self.check_type(&**self_type);
|
self.check_type(&**self_type);
|
||||||
self.check_path(trait_, PathKind::Trait);
|
if let Some(trait_) = trait_ {
|
||||||
|
self.check_path(trait_, PathKind::Trait);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#![feature(inherent_associated_types)]
|
#![feature(inherent_associated_types)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
// FIXME(fmease): Properly render inherent projections.
|
// @has 'inherent_projections/fn.create.html'
|
||||||
|
// @has - '//pre[@class="rust item-decl"]' "create() -> Owner::Metadata"
|
||||||
// @has inherent_projections/fn.create.html
|
// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Owner.html#associatedtype.Metadata'
|
||||||
// @has - '//pre[@class="rust item-decl"]' "create() -> _"
|
|
||||||
pub fn create() -> Owner::Metadata {}
|
pub fn create() -> Owner::Metadata {}
|
||||||
|
|
||||||
pub struct Owner;
|
pub struct Owner;
|
||||||
@ -12,3 +11,34 @@ pub struct Owner;
|
|||||||
impl Owner {
|
impl Owner {
|
||||||
pub type Metadata = ();
|
pub type Metadata = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we handle bound vars correctly.
|
||||||
|
// @has 'inherent_projections/type.User.html' '//pre[@class="rust item-decl"]' "for<'a> fn(_: Carrier<'a>::Focus)"
|
||||||
|
pub type User = for<'a> fn(Carrier<'a>::Focus);
|
||||||
|
|
||||||
|
pub struct Carrier<'a>(&'a ());
|
||||||
|
|
||||||
|
impl<'a> Carrier<'a> {
|
||||||
|
pub type Focus = &'a mut i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
// FIXME(inherent_associated_types): Below we link to `Proj` but we should link to `Proj-1`.
|
||||||
|
// The current test checks for the buggy behavior for demonstration purposes.
|
||||||
|
|
||||||
|
// @has 'inherent_projections/type.Test.html'
|
||||||
|
// @has - '//pre[@class="rust item-decl"]' "Parametrized<i32>"
|
||||||
|
// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj'
|
||||||
|
// @!has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj-1'
|
||||||
|
pub type Test = Parametrized<i32>::Proj;
|
||||||
|
|
||||||
|
pub struct Parametrized<T>(T);
|
||||||
|
|
||||||
|
impl Parametrized<bool> {
|
||||||
|
pub type Proj = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parametrized<i32> {
|
||||||
|
pub type Proj = String;
|
||||||
|
}
|
||||||
|
45
tests/rustdoc/intra-doc/inherent-associated-types.rs
Normal file
45
tests/rustdoc/intra-doc/inherent-associated-types.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#![feature(inherent_associated_types)]
|
||||||
|
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
|
||||||
|
// @has inherent_associated_types/index.html
|
||||||
|
|
||||||
|
// @has - '//a/@href' 'enum.Simple.html#associatedtype.Type'
|
||||||
|
//! [`Simple::Type`]
|
||||||
|
|
||||||
|
pub enum Simple {}
|
||||||
|
|
||||||
|
impl Simple {
|
||||||
|
pub type Type = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
// @has 'inherent_associated_types/type.Test0.html' '//a/@href' \
|
||||||
|
// 'struct.Parametrized.html#associatedtype.Proj'
|
||||||
|
/// [`Parametrized<bool>::Proj`]
|
||||||
|
pub type Test0 = ();
|
||||||
|
|
||||||
|
// FIXME(inherent_associated_types): The intra-doc link below should point to `Proj-1` not `Proj`.
|
||||||
|
// The current test checks for the buggy behavior for demonstration purposes.
|
||||||
|
// The same bug happens for inherent associated functions and constants (see #85960, #93398).
|
||||||
|
//
|
||||||
|
// Further, at some point we should reject the intra-doc link `Parametrized::Proj`.
|
||||||
|
// It currently links to `Parametrized<bool>::Proj`.
|
||||||
|
|
||||||
|
// @has 'inherent_associated_types/type.Test1.html'
|
||||||
|
// @has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj'
|
||||||
|
// @!has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj-1'
|
||||||
|
/// [`Parametrized<i32>::Proj`]
|
||||||
|
pub type Test1 = ();
|
||||||
|
|
||||||
|
pub struct Parametrized<T>(T);
|
||||||
|
|
||||||
|
impl Parametrized<bool> {
|
||||||
|
pub type Proj = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parametrized<i32> {
|
||||||
|
pub type Proj = String;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user