-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[rustdoc] Add new example
disambiguator for intra-doc links
#132792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
52d6fa3
ae9f844
5d0a044
918a4ee
036079e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -487,21 +487,38 @@ impl Item { | |
let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] }; | ||
links | ||
.iter() | ||
.filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { | ||
debug!(?id); | ||
if let Ok((mut href, ..)) = href(*id, cx) { | ||
debug!(?href); | ||
if let Some(ref fragment) = *fragment { | ||
fragment.render(&mut href, cx.tcx()) | ||
.filter_map(|ItemLink { link: s, link_text, ref kind, ref fragment }| match kind { | ||
ItemLinkKind::Item { page_id: id } => { | ||
debug!(?id); | ||
if let Some(id) = id { | ||
if let Ok((mut href, ..)) = href(*id, cx) { | ||
debug!(?href); | ||
if let Some(ref fragment) = *fragment { | ||
fragment.render(&mut href, cx.tcx()) | ||
} | ||
return Some(RenderedLink { | ||
original_text: s.clone(), | ||
new_text: link_text.clone(), | ||
tooltip: link_tooltip(*id, fragment, cx), | ||
href, | ||
}); | ||
} | ||
} | ||
None | ||
} | ||
ItemLinkKind::Example { file_path } => { | ||
let example_name = file_path.split('/').next().unwrap_or(file_path); | ||
let mut href = | ||
std::iter::repeat("../").take(cx.current.len()).collect::<String>(); | ||
Comment on lines
+511
to
+512
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Imagine the following scenario:
I think example links need to carry a CrateNum, and the link-generating code needs to consult There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very good point, thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gave it a few tries but I don't see how it could work: the code is relying on the presence of the examples crates passed with with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I did: diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index fc0f73df799..553cfb0f2a1 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -506,13 +506,23 @@ pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
}
None
}
- ItemLinkKind::Example { file_path } => {
- let example_name = file_path.split('/').next().unwrap_or(file_path);
- let mut href =
- std::iter::repeat("../").take(cx.current.len()).collect::<String>();
+ ItemLinkKind::Example { file_path, krate } => {
+ let mut href = if *krate != LOCAL_CRATE {
+ let Some(location) = cx.cache().extern_locations.get(krate) else { return None };
+ match location {
+ ExternalLocation::Remote(s) => s.clone(),
+ ExternalLocation::Local => {
+ std::iter::repeat("../").take(cx.current.len()).collect::<String>()
+ }
+ ExternalLocation::Unknown => return None,
+ }
+ } else {
+ std::iter::repeat("../").take(cx.current.len()).collect::<String>()
+ };
href.push_str("src/");
href.push_str(file_path);
href.push_str(".html");
+ let example_name = file_path.split('/').next().unwrap_or(file_path);
Some(RenderedLink {
original_text: s.clone(),
new_text: link_text.clone(),
@@ -1141,6 +1151,8 @@ pub(crate) enum ItemLinkKind {
Example {
/// The path of the example file.
file_path: String,
+ /// Crate of the item containing this link.
+ krate: CrateNum,
},
}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index b7de93d08c2..6a1b39ba1ec 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -14,7 +14,7 @@
use rustc_errors::{Applicability, Diag, DiagMessage};
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{DefKind, Namespace, PerNS};
-use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::{Mutability, Safety};
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
@@ -68,7 +68,7 @@ fn filter_assoc_items_by_name_and_namespace<'a>(
pub(crate) enum Res {
Def(DefKind, DefId),
Primitive(PrimitiveType),
- Example(String),
+ Example(String, CrateNum),
}
type ResolveRes = rustc_hir::def::Res<rustc_ast::NodeId>;
@@ -78,7 +78,7 @@ fn descr(&self) -> &'static str {
match self {
Res::Def(kind, id) => ResolveRes::Def(*kind, *id).descr(),
Res::Primitive(_) => "primitive type",
- Res::Example(_) => "example",
+ Res::Example(..) => "example",
}
}
@@ -86,7 +86,7 @@ fn article(&self) -> &'static str {
match self {
Res::Def(kind, id) => ResolveRes::Def(*kind, *id).article(),
Res::Primitive(_) => "a",
- Res::Example(_) => "an",
+ Res::Example(..) => "an",
}
}
@@ -94,7 +94,7 @@ fn name(&self, tcx: TyCtxt<'_>) -> Symbol {
match self {
Res::Def(_, id) => tcx.item_name(*id),
Res::Primitive(prim) => prim.as_sym(),
- Res::Example(_) => panic!("no name"),
+ Res::Example(..) => panic!("no name"),
}
}
@@ -102,7 +102,7 @@ fn def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
match self {
Res::Def(_, id) => Some(*id),
Res::Primitive(prim) => PrimitiveType::primitive_locations(tcx).get(prim).copied(),
- Res::Example(_) => None,
+ Res::Example(..) => None,
}
}
@@ -114,7 +114,7 @@ fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Res {
fn disambiguator_suggestion(&self) -> Suggestion {
let kind = match self {
Res::Primitive(_) => return Suggestion::Prefix("prim"),
- Res::Example(_) => return Suggestion::Prefix("example"),
+ Res::Example(..) => return Suggestion::Prefix("example"),
Res::Def(kind, _) => kind,
};
@@ -1177,7 +1177,7 @@ pub(crate) fn resolve_ambiguities(&mut self) {
info.resolved.retain(|(res, _)| match res {
Res::Def(_, def_id) => self.validate_link(*def_id),
// Primitive types and examples are always valid.
- Res::Primitive(_) | Res::Example(_) => true,
+ Res::Primitive(_) | Res::Example(..) => true,
});
let diag_info = info.diag_info.into_info();
match info.resolved.len() {
@@ -1313,11 +1313,11 @@ fn compute_link(
kind: ItemLinkKind::Item { page_id },
})
}
- Res::Example(path) => Some(ItemLink {
+ Res::Example(path, krate) => Some(ItemLink {
link: Box::<str>::from(&*diag_info.ori_link),
link_text: link_text.clone(),
fragment,
- kind: ItemLinkKind::Example { file_path: path.into() },
+ kind: ItemLinkKind::Example { file_path: path.into(), krate },
}),
}
}
@@ -1483,12 +1483,13 @@ fn get_example_file(
&self,
path_str: &str,
diag: DiagnosticInfo<'_>,
+ item_id: DefId,
) -> Vec<(Res, Option<DefId>)> {
// If the user is referring to the example by its name:
if let Some(files) = self.cx.render_options.examples_files.get(path_str)
&& let Some(file_path) = files.iter().next()
{
- return vec![(Res::Example(file_path.clone()), None)];
+ return vec![(Res::Example(file_path.clone(), item_id.krate), None)];
}
// If the user is referring to a specific file of the example, it'll be of this form:
//
@@ -1497,7 +1498,7 @@ fn get_example_file(
&& let Some(files) = self.cx.render_options.examples_files.get(crate_name)
&& let Some(file_path) = files.get(path_str)
{
- return vec![(Res::Example(file_path.clone()), None)];
+ return vec![(Res::Example(file_path.clone(), item_id.krate), None)];
}
report_diagnostic(
self.cx.tcx,
@@ -1528,7 +1529,7 @@ fn resolve_with_disambiguator(
let module_id = key.module_id;
if matches!(disambiguator, Some(Disambiguator::Example)) {
- return self.get_example_file(path_str, diag);
+ return self.get_example_file(path_str, diag, item_id);
}
match disambiguator.map(Disambiguator::ns) {
Some(expected_ns) => {
@@ -2077,7 +2078,7 @@ fn resolution_failure(
partial_res.as_ref().expect("None case was handled by `last_found_module`");
let kind_did = match res {
Res::Def(kind, did) => Some((kind, did)),
- Res::Primitive(_) | Res::Example(_) => None,
+ Res::Primitive(_) | Res::Example(..) => None,
};
let is_struct_variant = |did| {
if let ty::Adt(def, _) = tcx.type_of(did).instantiate_identity().kind() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It sounds like this would need to tie into https://github.com/rust-lang/rust/blob/master/compiler/rustc_resolve/src/rustdoc.rs so that the list of examples (at least their names, but not necessarily the actual line-by-line connections) would be present in the libs metadata so that they can be accessed when inlining. |
||
href.push_str("src/"); | ||
href.push_str(file_path); | ||
href.push_str(".html"); | ||
Some(RenderedLink { | ||
original_text: s.clone(), | ||
new_text: link_text.clone(), | ||
tooltip: link_tooltip(*id, fragment, cx), | ||
tooltip: format!("Example {example_name}"), | ||
href, | ||
}) | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect() | ||
|
@@ -1110,6 +1127,23 @@ impl<I: Iterator<Item = ast::MetaItemInner>> NestedAttributesExt for I { | |
} | ||
} | ||
|
||
/// The kind of a link that has not yet been rendered. | ||
/// | ||
/// It is used in [`ItemLink`]. | ||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
pub(crate) enum ItemLinkKind { | ||
Item { | ||
/// 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. | ||
page_id: Option<DefId>, | ||
}, | ||
Example { | ||
/// The path of the example file. | ||
file_path: String, | ||
}, | ||
} | ||
|
||
/// A link that has not yet been rendered. | ||
/// | ||
/// This link will be turned into a rendered link by [`Item::links`]. | ||
|
@@ -1122,12 +1156,9 @@ pub(crate) struct ItemLink { | |
/// This may not be the same as `link` if there was a disambiguator | ||
/// in an intra-doc link (e.g. \[`fn@f`\]) | ||
pub(crate) link_text: Box<str>, | ||
/// 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 | ||
pub(crate) fragment: Option<UrlFragment>, | ||
pub(crate) kind: ItemLinkKind, | ||
} | ||
|
||
pub struct RenderedLink { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in this file were needed because
tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
uses the@has
command on the metadata file generated by--scrape-examples
. However, with my changes, some of its content is not utf8 compatible anymore, breaking the test.Now, it only read the content if we actually need to match the content of this file.