diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2023-03-29 03:24:35 +0200 |
|---|---|---|
| committer | León Orell Valerian Liehr <me@fmease.dev> | 2023-05-04 16:59:11 +0200 |
| commit | 61e1eda6db042413cf1794407fd10b7edc90059d (patch) | |
| tree | 411915ab2c3c7c06c0862ab1f67a019c5b8b0c07 | |
| parent | 46ec3486116dacbd4fcfa1d92943de7fcc17921a (diff) | |
| download | rust-61e1eda6db042413cf1794407fd10b7edc90059d.tar.gz rust-61e1eda6db042413cf1794407fd10b7edc90059d.zip | |
IAT: Rustdoc integration
| -rw-r--r-- | src/librustdoc/clean/auto_trait.rs | 5 | ||||
| -rw-r--r-- | src/librustdoc/clean/inline.rs | 7 | ||||
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 60 | ||||
| -rw-r--r-- | src/librustdoc/clean/types.rs | 4 | ||||
| -rw-r--r-- | src/librustdoc/html/format.rs | 46 | ||||
| -rw-r--r-- | src/librustdoc/html/render/mod.rs | 4 | ||||
| -rw-r--r-- | src/librustdoc/json/conversions.rs | 2 | ||||
| -rw-r--r-- | src/rustdoc-json-types/lib.rs | 8 | ||||
| -rw-r--r-- | src/tools/jsondoclint/src/validator.rs | 4 | ||||
| -rw-r--r-- | tests/rustdoc/inherent-projections.rs | 38 | ||||
| -rw-r--r-- | tests/rustdoc/intra-doc/inherent-associated-types.rs | 45 |
11 files changed, 183 insertions, 40 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fb32b6ef1d3..baf2b0a8585 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -556,7 +556,10 @@ where WherePredicate::EqPredicate { lhs, rhs, bound_params } => { match *lhs { 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 mut new_trait = trait_.clone(); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 951f54e9366..c852f9cca2b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -706,7 +706,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g.where_predicates.retain(|pred| match pred { 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.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 72b60d95b8c..657f3c9ec45 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -441,7 +441,7 @@ fn clean_projection<'tcx>( assoc: projection_to_path_segment(ty, cx), should_show_cast, 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(); generics.where_predicates.retain_mut(|pred| match *pred { 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, .. } => { @@ -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), should_show_cast, self_type, - trait_, + trait_: Some(trait_), })) } hir::QPath::TypeRelative(qself, segment) => { 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) => { - 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. ty::Error(_) => return Type::Infer, - // Otherwise, this is an inherent associated type. - _ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None), + _ => bug!("clean: expected associated type, found `{ty:?}`"), }; - 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 { assoc: clean_path_segment(segment, cx), should_show_cast, @@ -1836,9 +1847,28 @@ pub(crate) fn clean_middle_ty<'tcx>( clean_projection(bound_ty.rebind(*data), cx, parent_def_id) } - // FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in - // `QPathData` optional or alternatively adding a new `clean::Type` variant. - ty::Alias(ty::Inherent, _data) => Type::Infer, + ty::Alias(ty::Inherent, alias_ty) => { + let alias_ty = bound_ty.rebind(alias_ty); + 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) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 7371b44465b..38664c3e359 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1660,7 +1660,7 @@ impl Type { pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { 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 { None } @@ -1704,7 +1704,7 @@ pub(crate) struct QPathData { pub self_type: Type, /// FIXME: compute this field on demand. pub should_show_cast: bool, - pub trait_: Path, + pub trait_: Option<Path>, } /// A primitive (aka, builtin) type. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1c6810bdaf9..d963d6092c4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1116,14 +1116,17 @@ fn fmt_type<'cx>( ref trait_, 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 should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{:#}::", self_type.print(cx))? } } else { - if should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{}::", self_type.print(cx))? @@ -1139,15 +1142,36 @@ fn fmt_type<'cx>( // the ugliness comes from inlining across crates where // everything comes in as a fully resolved QPath (hard to // look at). - if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) { - 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), - ) + if !f.alternate() { + // FIXME(inherent_associated_types): We always link to the very first associated + // type (in respect to source order) that bears the given name (`assoc.name`) and that is + // affiliated with the computed `DefId`. This is obviously incorrect when we have + // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself + // through here and map it to the corresponding HTML ID that was generated by + // `render::Context::derive_id` when the impl blocks were rendered. + // 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 { write!(f, "{}", assoc.name) }?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e09c6480060..d6773169639 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -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_, .. }) => { work.push_back(self_type); - process_path(trait_.def_id()); + if let Some(trait_) = trait_ { + process_path(trait_.def_id()); + } } _ => {} } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index b5bebb70593..b1cef20b434 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -574,7 +574,7 @@ impl FromWithTcx<clean::Type> for Type { name: assoc.name.to_string(), args: Box::new(assoc.args.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)), }, } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 3cf8ceed620..3556834071f 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// 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 /// 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")] 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 { name: String, args: Box<GenericArgs>, self_type: Box<Type>, + /// `None` iff this is an *inherent* associated type. #[serde(rename = "trait")] - trait_: Path, + trait_: Option<Path>, }, } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index a1f675a3b40..bf8a64acf08 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -273,7 +273,9 @@ impl<'a> Validator<'a> { Type::QualifiedPath { name: _, args, self_type, trait_ } => { self.check_generic_args(&**args); self.check_type(&**self_type); - self.check_path(trait_, PathKind::Trait); + if let Some(trait_) = trait_ { + self.check_path(trait_, PathKind::Trait); + } } } } diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs index bdd881ab456..9bda0acaf83 100644 --- a/tests/rustdoc/inherent-projections.rs +++ b/tests/rustdoc/inherent-projections.rs @@ -1,10 +1,9 @@ #![feature(inherent_associated_types)] #![allow(incomplete_features)] -// FIXME(fmease): Properly render inherent projections. - -// @has inherent_projections/fn.create.html -// @has - '//pre[@class="rust item-decl"]' "create() -> _" +// @has 'inherent_projections/fn.create.html' +// @has - '//pre[@class="rust item-decl"]' "create() -> Owner::Metadata" +// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Owner.html#associatedtype.Metadata' pub fn create() -> Owner::Metadata {} pub struct Owner; @@ -12,3 +11,34 @@ pub struct Owner; impl Owner { 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; +} diff --git a/tests/rustdoc/intra-doc/inherent-associated-types.rs b/tests/rustdoc/intra-doc/inherent-associated-types.rs new file mode 100644 index 00000000000..2b28d2ae60b --- /dev/null +++ b/tests/rustdoc/intra-doc/inherent-associated-types.rs @@ -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; +} |
