Rustdoc-Json: Correcty handle intra-doc-links to items without HTML page

Closes #101531
This commit is contained in:
Nixon Enraght-Moony 2022-09-10 00:04:10 +01:00
parent 98f3001eec
commit 2c17099671
5 changed files with 63 additions and 7 deletions

View File

@ -510,7 +510,7 @@ impl Item {
.get(&self.item_id) .get(&self.item_id)
.map_or(&[][..], |v| v.as_slice()) .map_or(&[][..], |v| v.as_slice())
.iter() .iter()
.filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| {
debug!(?did); debug!(?did);
if let Ok((mut href, ..)) = href(*did, cx) { if let Ok((mut href, ..)) = href(*did, cx) {
debug!(?href); debug!(?href);
@ -1134,7 +1134,10 @@ pub(crate) struct ItemLink {
/// This may not be the same as `link` if there was a disambiguator /// This may not be the same as `link` if there was a disambiguator
/// in an intra-doc link (e.g. \[`fn@f`\]) /// in an intra-doc link (e.g. \[`fn@f`\])
pub(crate) link_text: String, pub(crate) link_text: String,
pub(crate) did: DefId, /// The `DefId` of the Item whose **HTML Page** contains the item being
/// linked to. This will be different to `item_id` on item's that don't
/// have their own page, such as struct fields and enum variants.
pub(crate) page_id: DefId,
/// The url fragment to append to the link /// The url fragment to append to the link
pub(crate) fragment: Option<UrlFragment>, pub(crate) fragment: Option<UrlFragment>,
} }

View File

@ -19,6 +19,7 @@ use crate::clean::utils::print_const_expr;
use crate::clean::{self, ItemId}; use crate::clean::{self, ItemId};
use crate::formats::item_type::ItemType; use crate::formats::item_type::ItemType;
use crate::json::JsonRenderer; use crate::json::JsonRenderer;
use crate::passes::collect_intra_doc_links::UrlFragment;
impl JsonRenderer<'_> { impl JsonRenderer<'_> {
pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> { pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
@ -29,8 +30,14 @@ impl JsonRenderer<'_> {
.get(&item.item_id) .get(&item.item_id)
.into_iter() .into_iter()
.flatten() .flatten()
.map(|clean::ItemLink { link, did, .. }| { .map(|clean::ItemLink { link, page_id, fragment, .. }| {
(link.clone(), from_item_id((*did).into(), self.tcx)) let id = match fragment {
Some(UrlFragment::Item(frag_id)) => *frag_id,
// FIXME: Pass the `UserWritten` segment to JSON consumer.
Some(UrlFragment::UserWritten(_)) | None => *page_id,
};
(link.clone(), from_item_id(id.into(), self.tcx))
}) })
.collect(); .collect();
let docs = item.attrs.collapsed_doc_value(); let docs = item.attrs.collapsed_doc_value();

View File

@ -223,6 +223,9 @@ enum MalformedGenerics {
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub(crate) enum UrlFragment { pub(crate) enum UrlFragment {
Item(DefId), Item(DefId),
/// A part of a page that isn't a rust item.
///
/// Eg: `[Vector Examples](std::vec::Vec#examples)`
UserWritten(String), UserWritten(String),
} }
@ -1127,7 +1130,7 @@ impl LinkCollector<'_, '_> {
Some(ItemLink { Some(ItemLink {
link: ori_link.link.clone(), link: ori_link.link.clone(),
link_text: link_text.clone(), link_text: link_text.clone(),
did: res.def_id(self.cx.tcx), page_id: res.def_id(self.cx.tcx),
fragment, fragment,
}) })
} }
@ -1146,11 +1149,12 @@ impl LinkCollector<'_, '_> {
item, item,
&diag_info, &diag_info,
)?; )?;
let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
let page_id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
Some(ItemLink { Some(ItemLink {
link: ori_link.link.clone(), link: ori_link.link.clone(),
link_text: link_text.clone(), link_text: link_text.clone(),
did: id, page_id,
fragment, fragment,
}) })
} }

View File

@ -0,0 +1,34 @@
// Regression test for <https://github.com/rust-lang/rust/issues/101531>,
// where links where to the item who's HTML page had the item linked to.
//! [`Struct::struct_field`]
//! [`Enum::Variant`]
//! [`Trait::AssocType`]
//! [`Trait::ASSOC_CONST`]
//! [`Trait::method`]
// @set struct_field = "$.index[*][?(@.name=='struct_field')].id"
// @set Variant = "$.index[*][?(@.name=='Variant')].id"
// @set AssocType = "$.index[*][?(@.name=='AssocType')].id"
// @set ASSOC_CONST = "$.index[*][?(@.name=='ASSOC_CONST')].id"
// @set method = "$.index[*][?(@.name=='method')].id"
// @is "$.index[*][?(@.name=='non_page')].links['`Struct::struct_field`']" $struct_field
// @is "$.index[*][?(@.name=='non_page')].links['`Enum::Variant`']" $Variant
// @is "$.index[*][?(@.name=='non_page')].links['`Trait::AssocType`']" $AssocType
// @is "$.index[*][?(@.name=='non_page')].links['`Trait::ASSOC_CONST`']" $ASSOC_CONST
// @is "$.index[*][?(@.name=='non_page')].links['`Trait::method`']" $method
pub struct Struct {
pub struct_field: i32,
}
pub enum Enum {
Variant(),
}
pub trait Trait {
const ASSOC_CONST: i32;
type AssocType;
fn method();
}

View File

@ -0,0 +1,8 @@
//! For motivation, see [the reasons](foo#reasons)
/// # Reasons
/// To test rustdoc json
pub fn foo() {}
// @set foo = "$.index[*][?(@.name=='foo')].id"
// @is "$.index[*][?(@.name=='user_written')].links['foo#reasons']" $foo